/* FichothequeLib_Tools - Copyright (c) 2006-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 java.util.List;
import net.fichotheque.corpus.fiche.Atts;
import net.fichotheque.corpus.fiche.Li;
import net.fichotheque.corpus.fiche.P;
import net.fichotheque.corpus.fiche.Ul;
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 UlParser extends BlockParser {

    private final TextContentParser textContentParser;
    private UlBuffer rootUlBuffer;
    private UlBuffer currentUlBuffer;
    private Atts previousAtts = null;
    private final boolean withLineNumber;

    UlParser(TextContentParser textContentParser, boolean withLineNumber) {
        this.textContentParser = textContentParser;
        this.withLineNumber = withLineNumber;
    }

    @Override
    public boolean isStartLine(String line) {
        LineInfo lineInfo = LineInfo.parse(line, -1);
        return (lineInfo.getCleanedLine().startsWith("-"));
    }

    @Override
    public void parse(String[] lineArray, int startIndex, Atts initAtts) {
        rootUlBuffer = new UlBuffer(1, initAtts, null);
        previousAtts = null;
        currentUlBuffer = rootUlBuffer;
        int stop = lineArray.length - 1;
        for (int i = startIndex; i <= stop; i++) {
            LineInfo lineInfo = LineInfo.parse(StringUtils.cleanString(lineArray[i]), (withLineNumber) ? i : -1);
            if (lineInfo.hasAttsError()) {
                stop = i - 1;
                break;
            }
            String cleanedLine = lineInfo.getCleanedLine();
            if (cleanedLine.startsWith("-")) {
                int tiretDepth = getTiretDepth(lineInfo);
                if ((tiretDepth == 1) && (hasPrevious())) {
                    stop = i - 2;
                    break;
                } else {
                    addTiret(lineInfo, tiretDepth, i);
                }
            } else if (hasPrevious()) {
                stop = i - 2;
                break;
            } else if (cleanedLine.length() == 0) {
                previousAtts = lineInfo.getFirstAtts();
            } else if (cleanedLine.startsWith("_")) {
                addSouligne(lineInfo);
            } else {
                stop = i - 1;
                break;
            }
        }
        setParseResult(stop, rootUlBuffer.ul);
    }

    boolean hasPrevious() {
        return ((previousAtts != null) && (!previousAtts.hasOnlyAutomaticAtts()));
    }

    int getTiretDepth(LineInfo lineInfo) {
        String cleanedLine = lineInfo.getCleanedLine();
        int depth = 1;
        int availableDepth = cleanedLine.length();
        while ((depth < availableDepth) && (cleanedLine.charAt(depth) == '-')) {
            depth++;
        }
        return depth;
    }

    void addTiret(LineInfo lineInfo, int tiretDepth, int lineIndex) {
        int currentDepth = currentUlBuffer.depth;
        String cleanedLine = lineInfo.getCleanedLine();
        int nextDepth = currentDepth + 1;
        List<Atts> attsList = lineInfo.getAttsList();
        if ((currentUlBuffer.isEmpty()) && (tiretDepth > currentDepth)) {
            tiretDepth = currentDepth;
        } else if (tiretDepth > nextDepth) {
            tiretDepth = nextDepth;
        }
        P p = FicheBlockParser.parseP(cleanedLine.substring(tiretDepth).trim(), textContentParser);
        Atts secondAtts = lineInfo.getSecondAtts();
        if ((secondAtts == null) && (withLineNumber)) {
            secondAtts = LineInfo.getAutomaticAtts(lineIndex);
        }
        FicheUtils.populate(p, secondAtts);
        Li li = new Li(p);
        FicheUtils.populate(li, lineInfo.getFirstAtts());
        if (tiretDepth == currentDepth) {
            currentUlBuffer.addLi(li);
        } else if (tiretDepth == nextDepth) {
            UlBuffer ulBuffer = new UlBuffer(tiretDepth, previousAtts, currentUlBuffer);
            previousAtts = null;
            ulBuffer.addLi(li);
            currentUlBuffer.addUl(ulBuffer);
            currentUlBuffer = ulBuffer;
        } else {
            UlBuffer ancestor = getAncestor(tiretDepth);
            ancestor.addLi(li);
            currentUlBuffer = ancestor;
        }
    }

    private void addSouligne(LineInfo lineInfo) {
        int currentDepth = currentUlBuffer.depth;
        String cleanedLine = lineInfo.getCleanedLine();
        int depth = 1;
        int availableDepth = cleanedLine.length();
        while ((depth < availableDepth) && (cleanedLine.charAt(depth) == '_')) {
            depth++;
        }
        if (depth > currentDepth) {
            depth = currentDepth;
        }
        P p = FicheBlockParser.parseP(cleanedLine.substring(depth).trim(), textContentParser);
        FicheUtils.populate(p, lineInfo.getFirstAtts());
        if (depth == currentDepth) {
            currentUlBuffer.addP(p);
        } else {
            UlBuffer ancestor = getAncestor(depth);
            ancestor.addP(p);
            currentUlBuffer = ancestor;
        }
    }

    private UlBuffer getAncestor(int depth) {
        UlBuffer parent = currentUlBuffer.parent;
        while (parent.depth > depth) {
            parent = parent.parent;
        }
        return parent;
    }


    private class UlBuffer {

        private final UlBuffer parent;
        private final int depth;
        private final Atts initAtts;
        private Ul ul = null;
        private Li lastLi;

        private UlBuffer(int depth, Atts initAtts, UlBuffer parent) {
            this.depth = depth;
            this.parent = parent;
            this.initAtts = initAtts;
        }

        private boolean isEmpty() {
            return (ul == null);
        }

        private void addLi(Li li) {
            if (ul == null) {
                ul = new Ul(li);
                FicheUtils.populate(ul, initAtts);
            } else {
                ul.add(li);
            }
            lastLi = li;
        }

        private void addP(P p) {
            if (lastLi == null) {
                addLi(new Li(p));
            } else {
                lastLi.add(p);
            }
        }

        private void addUl(UlBuffer ulBuffer) {
            if (ulBuffer.ul != null) {
                lastLi.add(ulBuffer.ul);
            }
        }

    }

}
