/* FichothequeLib_Tools - Copyright (c) 2007-2022 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.fichotheque.tools.parsers.ficheblock;

import net.fichotheque.corpus.fiche.Atts;
import net.fichotheque.corpus.fiche.Table;
import net.fichotheque.corpus.fiche.Td;
import net.fichotheque.corpus.fiche.Tr;
import net.fichotheque.tools.parsers.LineInfo;
import net.fichotheque.tools.parsers.TextContentParser;
import net.fichotheque.utils.FicheUtils;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
class TableParser extends BlockParser {

    private final static int HORIZONTALHEADER_BITVALUE = 1;
    private final static int VERTICALHEADER_BITVALUE = 2;
    private final ImportParser importParser = new ImportParser();
    private final SyntaxParser syntaxParser;
    private final TextContentParser textContentParser;
    private final boolean withLineNumber;

    TableParser(TextContentParser textContentParser, boolean withLineNumber) {
        this.textContentParser = textContentParser;
        this.syntaxParser = new SyntaxParser(textContentParser);
        this.withLineNumber = withLineNumber;
    }

    @Override
    public boolean isStartLine(String line) {
        return line.startsWith("===");
    }

    @Override
    public void parse(String[] lineArray, int startIndex, Atts initAtts) {
        String firstLine = StringUtils.cleanString(lineArray[startIndex]);
        Table table = new Table();
        FicheUtils.populate(table, initAtts);
        char carac = initSeparator(firstLine);
        InternalParser internalParser;
        if (carac == '0') {
            internalParser = syntaxParser;
        } else {
            importParser.setSeparator(carac);
            internalParser = importParser;
        }
        internalParser.setTable(table);
        internalParser.setHeaderMask(getHeaderMask(firstLine));
        int beginIndex = BlockParser.parseZoneBlockElements(table, startIndex + 1, lineArray, textContentParser);
        int stop = lineArray.length - 1;
        for (int i = beginIndex; i <= stop; i++) {
            String line = lineArray[i];
            if (line.trim().startsWith("===")) {
                stop = i;
                break;
            } else {
                internalParser.parseLine(line, (withLineNumber) ? i : -1);
            }
        }
        internalParser.flush();
        setParseResult(stop, table);
    }

    private int getHeaderMask(String s) {
        int headerMask = 0;
        if (s.indexOf('h') != -1) {
            headerMask = headerMask | HORIZONTALHEADER_BITVALUE;
        }
        if (s.indexOf('v') != -1) {
            headerMask = headerMask | VERTICALHEADER_BITVALUE;
        }
        return headerMask;
    }

    private char initSeparator(String s) {
        for (int i = 0; i < s.length(); i++) {
            char carac = s.charAt(i);
            switch (carac) {
                case 't':
                    return '\t';
                case ',':
                    return ',';
                case ';':
                    return ';';
                case ':':
                    return ':';
                case '|':
                    return '|';
                case '*':
                    return '*';
            }
        }
        return '0';
    }


    private static abstract class InternalParser {

        Table table;
        int headerMask;
        int rowIndex;

        public abstract void parseLine(String line, int lineIndex);

        public abstract void flush();

        public abstract void reinit();

        public void setTable(Table table) {
            this.table = table;
            this.rowIndex = 0;
            reinit();
        }

        public void setHeaderMask(int headerMask) {
            this.headerMask = headerMask;
        }

        protected short checkType(short type, int colIndex) {
            if (type == Td.HEADER) {
                return Td.HEADER;
            }
            if ((rowIndex == 0) && ((headerMask & HORIZONTALHEADER_BITVALUE) != 0)) {
                return Td.HEADER;
            }
            if ((colIndex == 0) && ((headerMask & VERTICALHEADER_BITVALUE) != 0)) {
                return Td.HEADER;
            }
            return Td.STANDARD;
        }

        protected Td createTd(int colIndex) {
            if ((rowIndex == 0) && ((headerMask & HORIZONTALHEADER_BITVALUE) != 0)) {
                return new Td(Td.HEADER);
            }
            if ((colIndex == 0) && ((headerMask & VERTICALHEADER_BITVALUE) != 0)) {
                return new Td(Td.HEADER);
            }
            return new Td();
        }

        protected void addRow(Tr tr) {
            table.add(tr);
            rowIndex++;
        }

    }


    private static class ImportParser extends InternalParser {

        private char separator = '\t';

        private ImportParser() {
        }

        @Override
        public void parseLine(String line, int lineIndex) {
            if (line.length() == 0) {
                return;
            }
            Tr tr = new Tr();
            StringBuilder buf = new StringBuilder();
            int q = 0;
            for (int i = 0; i < line.length(); i++) {
                char c = line.charAt(i);
                if (c == separator) {
                    Td td = new Td(checkType(Td.STANDARD, q));
                    td.addText(StringUtils.cleanString(buf.toString()));
                    tr.add(td);
                    buf = new StringBuilder();
                    q++;
                } else {
                    buf.append(c);
                }
            }
            String result = StringUtils.cleanString(buf.toString());
            if (result.length() == 0) {
                if (tr.isEmpty()) {
                    return;
                }
            } else {
                Td td = new Td(checkType(Td.STANDARD, q));
                td.addText(result);
                tr.add(td);
            }
            addRow(tr);
        }

        @Override
        public void reinit() {
        }

        public void setSeparator(char separator) {
            this.separator = separator;
        }

        @Override
        public void flush() {
        }

    }


    private static class SyntaxParser extends InternalParser {

        private final TextContentParser parser;
        private Tr currentRow;
        private int tdIndex = 0;
        private Atts potentialTrAtts = null;

        private SyntaxParser(TextContentParser parser) {
            this.parser = parser;
        }

        @Override
        public void parseLine(String line, int lineIndex) {
            line = StringUtils.cleanString(line);
            LineInfo lineInfo = LineInfo.parse(line, lineIndex);
            line = lineInfo.getCleanedLine();
            if (line.length() == 0) {
                flush();
                potentialTrAtts = lineInfo.getFirstAtts();
            } else {
                short tdType = Td.STANDARD;
                if (line.startsWith("#")) {
                    tdType = Td.HEADER;
                    line = line.substring(1).trim();
                }
                if (line.equals("-")) {
                    line = "";
                }
                if (currentRow == null) {
                    currentRow = new Tr();
                    if (potentialTrAtts != null) {
                        FicheUtils.populate(currentRow, potentialTrAtts);
                    }
                }
                Td td = new Td(checkType(tdType, tdIndex));
                tdIndex++;
                if (lineInfo.hasAttsError()) {
                    td.addText(line);
                } else {
                    FicheUtils.populate(td, lineInfo.getFirstAtts());
                    parser.parse(td, line);
                }
                currentRow.add(td);
            }
        }

        @Override
        public void flush() {
            if (currentRow != null) {
                addRow(currentRow);
                currentRow = null;
                potentialTrAtts = null;
                tdIndex = 0;
            }
        }

        @Override
        public void reinit() {
            currentRow = null;
            tdIndex = 0;
        }

    }

}
