/* FichothequeLib_Tools - Copyright (c) 2024 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.exportation.balayage.dom;

import java.text.ParseException;
import java.util.function.Consumer;
import net.fichotheque.Fichotheque;
import net.fichotheque.exportation.balayage.BalayageConstants;
import net.fichotheque.tools.exportation.balayage.BalayageDefBuilder;
import net.fichotheque.tools.exportation.balayage.BalayageOutputBuilder;
import net.fichotheque.tools.exportation.balayage.BalayageUnitBuilder;
import net.fichotheque.tools.selection.SelectionDOMUtils;
import net.mapeadores.util.localisation.Langs;
import net.mapeadores.util.localisation.LangsUtils;
import net.mapeadores.util.logging.MessageHandler;
import net.mapeadores.util.text.AccoladePattern;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.xml.DOMUtils;
import net.mapeadores.util.xml.XMLUtils;
import org.w3c.dom.Element;


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

    private final Fichotheque fichotheque;
    private final BalayageDefBuilder balayageDefBuilder;
    private final MessageHandler messageHandler;

    public BalayageUnitDOMReader(Fichotheque fichotheque, BalayageDefBuilder balayageDefBuilder, MessageHandler messageHandler) {
        this.fichotheque = fichotheque;
        this.balayageDefBuilder = balayageDefBuilder;
        this.messageHandler = messageHandler;
    }

    public BalayageUnitDOMReader read(Element element) {
        switch (element.getTagName()) {
            case "corpus-balayage":
                addCorpusBalayageUnit(element);
                break;
            case "fiche-balayage":
                addFicheBalayageUnit(element);
                break;
            case "thesaurus-balayage":
                addThesaurusBalayageUnit(element);
                break;
            case "motcle-balayage":
                addMotcleBalayageUnit(element);
                break;
            case "unique-balayage":
                addUniqueBalayageUnit(element);
                break;
            case "document-balayage":
                addDocumentBalayageUnit(element);
                break;
            case "illustration-balayage":
                addIllustrationBalayageUnit(element);
                break;
        }
        return this;
    }

    private void addCorpusBalayageUnit(Element element) {
        BalayageUnitBuilder balayageUnitBuilder = balayageDefBuilder.addBalayageUnitBuilder(BalayageConstants.CORPUS_TYPE);
        commonBalayageUnit(balayageUnitBuilder, element);
        balayageUnitBuilder.setFicheQuery(SelectionDOMUtils.getFicheConditionEntry(fichotheque, element).getFicheQuery());
    }

    private void addFicheBalayageUnit(Element element) {
        BalayageUnitBuilder balayageUnitBuilder = balayageDefBuilder.addBalayageUnitBuilder(BalayageConstants.FICHE_TYPE);
        commonBalayageUnit(balayageUnitBuilder, element);
        balayageUnitBuilder.setFicheQuery(SelectionDOMUtils.getFicheConditionEntry(fichotheque, element).getFicheQuery());
    }

    private void addThesaurusBalayageUnit(Element element) {
        BalayageUnitBuilder balayageUnitBuilder = balayageDefBuilder.addBalayageUnitBuilder(BalayageConstants.THESAURUS_TYPE);
        commonBalayageUnit(balayageUnitBuilder, element);
        balayageUnitBuilder.setMotcleQuery(SelectionDOMUtils.getMotcleConditionEntry(fichotheque, element).getMotcleQuery());
    }

    private void addMotcleBalayageUnit(Element element) {
        BalayageUnitBuilder balayageUnitBuilder = balayageDefBuilder.addBalayageUnitBuilder(BalayageConstants.MOTCLE_TYPE);
        commonBalayageUnit(balayageUnitBuilder, element);
        balayageUnitBuilder.setMotcleQuery(SelectionDOMUtils.getMotcleConditionEntry(fichotheque, element).getMotcleQuery());
    }

    private void addUniqueBalayageUnit(Element element) {
        BalayageUnitBuilder balayageUnitBuilder = balayageDefBuilder.addBalayageUnitBuilder(BalayageConstants.UNIQUE_TYPE);
        commonBalayageUnit(balayageUnitBuilder, element);
    }

    private void addDocumentBalayageUnit(Element element) {
        BalayageUnitBuilder balayageUnitBuilder = balayageDefBuilder.addBalayageUnitBuilder(BalayageConstants.DOCUMENT_TYPE);
        commonBalayageUnit(balayageUnitBuilder, element);
        balayageUnitBuilder
                .setFicheQuery(SelectionDOMUtils.getFicheConditionEntry(fichotheque, element).getFicheQuery())
                .setDocumentQuery(SelectionDOMUtils.getDocumentConditionEntry(element).getDocumentQuery());
        DOMUtils.readChildren(element, new ExtensionConsumer(balayageUnitBuilder));
    }

    private void addIllustrationBalayageUnit(Element element) {
        BalayageUnitBuilder balayageUnitBuilder = balayageDefBuilder.addBalayageUnitBuilder(BalayageConstants.ILLUSTRATION_TYPE);
        commonBalayageUnit(balayageUnitBuilder, element);
        balayageUnitBuilder.setIllustrationQuery(SelectionDOMUtils.getIllustrationConditionEntry(element).getIllustrationQuery());
    }

    private void commonBalayageUnit(BalayageUnitBuilder balayageUnitBuilder, Element element) {
        balayageUnitBuilder
                .setName(element.getAttribute("name"))
                .setExtractionPath(element.getAttribute("extraction"));
        String globalselect = element.getAttribute("global-select");
        if (globalselect.equals("false")) {
            balayageUnitBuilder.setGlobalSelect(false);
        }
        DOMUtils.readChildren(element, new OutputConsumer(balayageUnitBuilder));
        String mode = element.getAttribute("mode");
        String[] tokens = StringUtils.getTechnicalTokens(mode, true);
        for (String token : tokens) {
            balayageUnitBuilder.addMode(token);
        }
    }

    public BalayageUnitDOMReader init(Fichotheque fichotheque, BalayageDefBuilder balayageDefBuilder, MessageHandler messageHandler) {
        return new BalayageUnitDOMReader(fichotheque, balayageDefBuilder, messageHandler);
    }


    private class ExtensionConsumer implements Consumer<Element> {

        private final BalayageUnitBuilder balayageUnitBuilder;

        private ExtensionConsumer(BalayageUnitBuilder balayageUnitBuilder) {
            this.balayageUnitBuilder = balayageUnitBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName();
            switch (tagName) {
                case "extension":
                    String extension = XMLUtils.getData(element);
                    if (extension.length() > 0) {
                        balayageUnitBuilder.addExtension(extension);
                    }
                    break;
            }
        }

    }


    private class OutputConsumer implements Consumer<Element> {

        private final BalayageUnitBuilder balayageUnitBuilder;

        private OutputConsumer(BalayageUnitBuilder balayageUnitBuilder) {
            this.balayageUnitBuilder = balayageUnitBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName();
            switch (tagName) {
                case "output":
                    BalayageOutputBuilder outputBuilder = balayageUnitBuilder.addOutputBuilder();
                    parseOutput(element, outputBuilder);
                    break;
            }
        }

        private void parseOutput(Element element, BalayageOutputBuilder outputBuilder) {
            outputBuilder
                    .setName(element.getAttribute("name"))
                    .setXsltPath(element.getAttribute("xslt"))
                    .setMode(element.getAttribute("mode"))
                    .setPosttransform(element.getAttribute("posttransform"))
                    .setCleanBefore(StringUtils.isTrue(element.getAttribute("clean")));
            String patternString = element.getAttribute("pattern");
            if (patternString.length() > 0) {
                try {
                    AccoladePattern pattern = new AccoladePattern(patternString);
                    outputBuilder.setPattern(pattern);
                } catch (ParseException pe) {
                    //balayageLog.addOutputWarning(balayageUnitIndex, outputIndex, "pattern", BalayageLog.WRONG_PATTERN, pattern);
                }
            }
            String langsValue = element.getAttribute("langs");
            if (langsValue.length() > 0) {
                Langs langs = LangsUtils.toCleanLangs(langsValue);
                if (!langs.isEmpty()) {
                    outputBuilder.setCustomLangs(langs);
                }
            }
            String path = normalizePath(element.getAttribute("path"));
            if (path != null) {
                if (path.equals("_inc_/")) {
                    outputBuilder.setIncludeOutput(true);
                } else {
                    outputBuilder.setIncludeOutput(false);
                    try {
                        AccoladePattern outputPathPattern = new AccoladePattern(path);
                        outputBuilder.setOutputPathPattern(outputPathPattern);
                    } catch (ParseException pe) {
                        //balayageLog.addOutputWarning(balayageUnitIndex, outputIndex, "path", BalayageLog.WRONG_PATH, path);
                    }
                }
            }
            try {
                outputBuilder.setLangOption(element.getAttribute("lang-option"));
            } catch (IllegalArgumentException iae) {
            }
            DOMUtils.readChildren(element, new ParamConsumer(outputBuilder));
        }

    }


    private class ParamConsumer implements Consumer<Element> {

        private final BalayageOutputBuilder outputBuilder;

        private ParamConsumer(BalayageOutputBuilder outputBuilder) {
            this.outputBuilder = outputBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName();
            switch (tagName) {
                case "param":
                    String name = element.getAttribute("name");
                    if (name.length() > 0) {
                        String value = element.getAttribute("value");
                        outputBuilder.addOutputParam(name, value);
                    }
                    break;
            }
        }

    }

    private static String normalizePath(String path) {
        int length = path.length();
        if (length == 0) {
            return null;
        }
        if (path.charAt(0) == '/') {
            return normalizePath(path.substring(1));
        }
        char lastChar = path.charAt(length - 1);
        if (lastChar != '/') {
            path = path + '/';
        }
        return path;
    }

}
