/* FichothequeLib_Tools - Copyright (c) 2006-2025 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 java.util.List;
import net.fichotheque.corpus.fiche.Atts;
import net.fichotheque.corpus.fiche.FicheBlock;
import net.fichotheque.corpus.fiche.H;
import net.fichotheque.corpus.fiche.P;
import net.fichotheque.tools.parsers.LineInfo;
import net.fichotheque.tools.parsers.TextContentParser;
import net.fichotheque.utils.FicheUtils;
import net.mapeadores.util.html.HtmlCleaner;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.text.TypoOptions;


/**
 *
 * @author Vincent Calame
 */
public class FicheBlockParser {

    public static final short MODE_FORMULAIRE = 1;
    public static final short MODE_EXPORT = 2;
    private final TextContentParser textContentParser;
    private final BlockParser tableParser;
    private final BlockParser codeParser;
    private final BlockParser ulParser;
    private final BlockParser insertParser;
    private final BlockParser divParser;
    private final BlockParser cdatadivParser;
    private final ParagraphState paragraphState = new ParagraphState();
    private final boolean withLineNumber;

    public FicheBlockParser(HtmlCleaner htmlCleaner, TypoOptions typoOptions, boolean withLineNumber) {
        this.textContentParser = new TextContentParser(typoOptions);
        this.tableParser = new TableParser(textContentParser, withLineNumber);
        this.codeParser = new CodeParser(textContentParser, withLineNumber);
        this.ulParser = new UlParser(textContentParser, withLineNumber);
        this.insertParser = new InsertParser(textContentParser);
        this.divParser = new DivParser(textContentParser, typoOptions, htmlCleaner, withLineNumber);
        this.cdatadivParser = new CdatadivParser(textContentParser, htmlCleaner);
        this.withLineNumber = withLineNumber;
    }

    public void parseFicheBlockList(String string, List<FicheBlock> list) {
        if (string.length() == 0) {
            return;
        }
        reinit();
        String[] lines = StringUtils.getLineTokens(string, StringUtils.NOTCLEAN);
        for (int k = 0; k < lines.length; k++) {
            k = parseLine(lines, k, list);
        }
    }

    public void reinit() {
        paragraphState.reinit();
    }

    public int parseLine(String[] lineArray, int lineIndex, List<FicheBlock> ficheBlockList) {
        String line = StringUtils.cleanString(lineArray[lineIndex]);
        if (line.length() == 0) {
            paragraphState.flushPreviousAtts(ficheBlockList);
            paragraphState.increaseWhiteLineCount();
            return lineIndex;
        }
        BlockParser specificBlockParser = checkSpecificBlockParser(line);
        if (specificBlockParser != null) {
            Atts initAtts = paragraphState.getPreviousAtts();
            if ((initAtts == null) && (withLineNumber)) {
                initAtts = LineInfo.getAutomaticAtts(lineIndex);
            }
            specificBlockParser.parse(lineArray, lineIndex, initAtts);
            FicheBlock ficheBlock = specificBlockParser.getFicheBlock();
            if (ficheBlock != null) {
                ficheBlockList.add(ficheBlock);
            }
            int newLineIndex = specificBlockParser.getEndIndex();
            paragraphState.reinit();
            return newLineIndex;
        } else {
            paragraphState.flushPreviousAtts(ficheBlockList);
            LineInfo lineInfo = LineInfo.parse(line, (withLineNumber) ? lineIndex : -1);
            if (lineInfo.isCleanedLineEmpty()) {
                paragraphState.setPreviousAtts(lineInfo.getFirstAtts());
            } else {
                FicheBlock prg = parseLine(lineInfo, textContentParser);
                if (prg instanceof H) {
                    paragraphState.checkH();
                } else {
                    paragraphState.flushWhiteLines(ficheBlockList);
                }
                ficheBlockList.add(prg);
            }
            return lineIndex;
        }
    }

    private BlockParser checkSpecificBlockParser(String line) {
        if (codeParser.isStartLine(line)) {
            return codeParser;
        }
        if (tableParser.isStartLine(line)) {
            return tableParser;
        }
        if (insertParser.isStartLine(line)) {
            return insertParser;
        }
        if (cdatadivParser.isStartLine(line)) {
            return cdatadivParser;
        }
        if (divParser.isStartLine(line)) {
            return divParser;
        }
        if (ulParser.isStartLine(line)) {
            return ulParser;
        }
        return null;
    }

    /**
     *
     * @param lineInfo
     * @param textContentParser
     * @return instance de H ou P
     */
    public static FicheBlock parseLine(LineInfo lineInfo, TextContentParser textContentParser) {
        String line = lineInfo.getCleanedLine();
        if (lineInfo.hasAttsError()) {
            P p = new P();
            p.addText(line);
            return p;
        }
        if ((line.length() > 0) && (line.charAt(0) == '#')) {
            H h = parseH(line, textContentParser);
            FicheUtils.populate(h, lineInfo.getFirstAtts());
            return h;
        } else {
            P p = parseP(line, textContentParser);
            FicheUtils.populate(p, lineInfo.getFirstAtts());
            return p;
        }
    }

    public static H parseH(String line, TextContentParser textContentParser) {
        int level = 1;
        for (int i = 1; i < line.length(); i++) {
            char carac = line.charAt(i);
            if (carac == '#') {
                level++;
            } else {
                break;
            }
        }
        H h = new H(level);
        textContentParser.parse(h, line.substring(level).trim());
        return h;
    }

    public static P parseP(String line, TextContentParser textContentParser) {
        if (line.length() == 0) {
            P p = new P();
            return p;
        }
        short type = P.STANDARD;
        switch (line.charAt(0)) {
            case '>':
                type = P.CITATION;
                break;
            case '?':
                type = P.QUESTION;
                break;
            case '!':
                type = P.REMARK;
                break;
        }
        if (type != P.STANDARD) {
            line = line.substring(1).trim();
        }
        P p = new P(type);
        int length = line.length();
        if ((length > 0) && (line.charAt(0) == '@')) {
            StringBuilder sourceBuilder = new StringBuilder();
            int stopIndex = -1;
            for (int i = 1; i < length; i++) {
                char carac = line.charAt(i);
                if ((carac == '\\') && (i < (length - 1))) {
                    char next = line.charAt(i + 1);
                    if ((next == '\\') || (next == ':')) {
                        sourceBuilder.append(next);
                    } else {
                        sourceBuilder.append('\\');
                        sourceBuilder.append(next);
                    }
                    i = i + 1;
                } else if (carac == ':') {
                    stopIndex = i;
                    break;
                } else {
                    sourceBuilder.append(carac);
                }
            }
            if (stopIndex != -1) {
                p.setSource(sourceBuilder.toString().trim());
                line = line.substring(stopIndex + 1).trim();
            }
        }
        textContentParser.parse(p, line);
        return p;
    }

}
