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

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import net.fichotheque.corpus.FicheContext;
import net.fichotheque.corpus.fiche.Fiche;
import net.fichotheque.corpus.fiche.FicheBlock;
import net.fichotheque.corpus.fiche.FicheBlocks;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.tools.corpus.FicheChangeBuilder;
import net.fichotheque.utils.FicheUtils;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.logging.LogUtils;
import net.mapeadores.util.logging.MessageHandler;
import net.mapeadores.util.text.CleanedString;
import net.mapeadores.util.xml.DOMUtils;
import net.mapeadores.util.xml.XMLUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


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

    private final FicheChangeBuilder currentFiche;
    private final FicheContext ficheContext;
    private final RootConsumer rootConsumer = new RootConsumer();
    private final HeadConsumer headConsumer = new HeadConsumer();
    private final BodyConsumer bodyConsumer = new BodyConsumer();
    private final FicheItemDOMReader ficheItemDOMReader;
    private final FicheBlockDOMReader ficheBlockDOMReader;

    public FicheChangeDOMReader(FicheChangeBuilder ficheChangeBuilder, FicheContext ficheContext) {
        this.currentFiche = ficheChangeBuilder;
        this.ficheContext = ficheContext;
        MessageHandler messageHandler = LogUtils.NULL_MESSAGEHANDLER;
        this.ficheBlockDOMReader = new FicheBlockDOMReader(ficheContext.getHtmlCleaner(), messageHandler);
        this.ficheItemDOMReader = new FicheItemDOMReader(ficheBlockDOMReader, ficheContext.getFichothequeQuestioner(), messageHandler);
    }

    public void readFiche(Element element) {
        Fiche fiche = new Fiche();
        try {
            Lang lang = Lang.parse(element.getAttribute("xml:lang"));
            fiche.setLang(lang);
        } catch (ParseException pe) {
        }
        DOMUtils.readChildren(element, rootConsumer);
    }

    public boolean readFicheChild(Element element) {
        boolean done = headConsumer.readHeadElement(element);
        if (!done) {
            done = bodyConsumer.readBodyElement(element);
        }
        return done;
    }


    private class RootConsumer implements Consumer<Element> {

        private RootConsumer() {

        }

        @Override
        public void accept(Element element) {
            switch (element.getTagName()) {
                case "head":
                case "entete":
                case "source":
                    DOMUtils.readChildren(element, headConsumer);
                    break;
                case "body":
                case "corpsdefiche":
                    DOMUtils.readChildren(element, bodyConsumer);
                    break;
            }
        }

    }


    private class HeadConsumer implements Consumer<Element> {

        private HeadConsumer() {

        }

        @Override
        public void accept(Element element) {
            readHeadElement(element);
        }

        private boolean readHeadElement(Element element) {
            switch (element.getTagName()) {
                case "title":
                case "titre":
                    currentFiche.setTitle(CleanedString.newInstance(XMLUtils.getData(element)));
                    return true;
                case "subtitle":
                case "soustitre":
                    currentFiche.setSubtitle(ficheItemDOMReader.readPara(element));
                    return true;
                case "prop":
                case "propriete":
                    readProp(element);
                    return true;
                case "info":
                case "information":
                    readInfo(element);
                    return true;
                case "owners":
                case "redacteurs":
                    readOwners(element);
                    return true;
                case "lang":
                    readLang(element);
                    return true;
                default:
                    return false;
            }
        }

    }


    private class BodyConsumer implements Consumer<Element> {

        private BodyConsumer() {

        }

        @Override
        public void accept(Element element) {
            readBodyElement(element);
        }

        private boolean readBodyElement(Element element) {
            switch (element.getTagName()) {
                case "texte":
                    FieldKey fieldKey = FieldKey.build(FieldKey.SECTION_CATEGORY, "texte");
                    List<FicheBlock> list = ficheBlockDOMReader.readFicheBlockList(element);
                    FicheBlocks ficheBlocks = FicheUtils.toFicheBlocks(list);
                    currentFiche.appendSection(fieldKey, ficheBlocks);
                    return true;
                case "section":
                case "annexe":
                    readSection(element);
                    return true;
                default:
                    return false;
            }
        }

    }

    private void readLang(Element element) {
        String langString = XMLUtils.getData(element);
        if (langString.isEmpty()) {
            currentFiche.setLang(null);
        } else {
            try {
                Lang lang = Lang.parse(langString);
                currentFiche.setLang(lang);
            } catch (ParseException pe) {

            }
        }
    }

    public void readProp(Element element) {
        String fieldName = element.getAttribute("name");
        if (fieldName.length() == 0) {
            fieldName = element.getAttribute("type");
        }
        if (fieldName.length() == 0) {
            return;
        }
        FieldKey fieldKey;
        try {
            fieldKey = FieldKey.parse(FieldKey.PROP_CATEGORY, fieldName);
        } catch (ParseException pe) {
            return;
        }
        FicheItem ficheItem = null;
        NodeList liste = element.getChildNodes();
        for (int j = 0; j < liste.getLength(); j++) {
            if (liste.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element el = (Element) liste.item(j);
                ficheItem = ficheItemDOMReader.readFicheItem(el);
                break;
            }
        }
        currentFiche.setProp(fieldKey, ficheItem);
    }

    public void readInfo(Element element) {
        String fieldName = element.getAttribute("name");
        if (fieldName.length() == 0) {
            fieldName = element.getAttribute("type");
        }
        if (fieldName.length() == 0) {
            return;
        }
        FieldKey fieldKey;
        try {
            fieldKey = FieldKey.parse(FieldKey.INFO_CATEGORY, fieldName);
        } catch (ParseException pe) {
            return;
        }
        FicheItems ficheItems = readFicheItems(element);
        currentFiche.appendInfo(fieldKey, ficheItems);
    }

    public void readOwners(Element element) {
        FicheItems ficheItems = readFicheItems(element);
        currentFiche.appendOwners(ficheItems);
    }


    public void readSection(Element element) {
        String fieldName = element.getAttribute("name");
        if (fieldName.length() == 0) {
            fieldName = element.getAttribute("type");
        }
        FieldKey fieldKey;
        try {
            fieldKey = FieldKey.parse(FieldKey.SECTION_CATEGORY, fieldName);
        } catch (ParseException pe) {
            return;
        }
        List<FicheBlock> list = ficheBlockDOMReader.readFicheBlockList(element);
        FicheBlocks ficheBlocks = FicheUtils.toFicheBlocks(list);
        currentFiche.appendSection(fieldKey, ficheBlocks);
    }


    private FicheItems readFicheItems(Element element) {
        List<FicheItem> list = new ArrayList<FicheItem>();
        NodeList liste = element.getChildNodes();
        for (int j = 0; j < liste.getLength(); j++) {
            if (liste.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) liste.item(j);
                FicheItem ficheItem = ficheItemDOMReader.readFicheItem(child);
                if (ficheItem != null) {
                    try {
                        list.add(ficheItem);
                    } catch (IllegalArgumentException iae) {
                    }
                }
            }
        }
        return FicheUtils.toFicheItems(list);
    }

}
