/* FichothequeLib_Xml - Copyright (c) 2011-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.xml.extraction;

import java.io.IOException;
import java.util.Map;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.extraction.ExtractParameters;
import net.fichotheque.extraction.ExtractionConstants;
import net.fichotheque.extraction.ExtractionContext;
import net.fichotheque.extraction.def.CorpusExtractDef;
import net.fichotheque.extraction.def.PackClause;
import net.fichotheque.extraction.def.TagNameInfo;
import net.fichotheque.extraction.def.TitleClause;
import net.fichotheque.extraction.filterunit.FilterUnit;
import net.fichotheque.extraction.run.CorpusExtractResult;
import net.fichotheque.extraction.run.FicheExtractInfo;
import net.fichotheque.extraction.run.FicheGroup;
import net.fichotheque.utils.ExtractionUtils;
import net.fichotheque.utils.FichothequeUtils;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LangContext;
import net.mapeadores.util.localisation.ListLangContext;
import net.mapeadores.util.localisation.UserLangContext;
import net.mapeadores.util.text.LabelUtils;
import net.mapeadores.util.xml.XMLPart;
import net.mapeadores.util.xml.XMLWriter;


/**
 *
 * @author Vincent Calame
 */
public class CorpusExtractXMLPart extends XMLPart {

    private final ExtractParameters extractParameters;
    private final int extractVersion;
    private final FicheXMLPart ficheXMLPart;
    private final FicheItemXMLPart ficheItemXMLPart;
    private final PackWriter packWriter = new PackWriter();
    private final TagNames tagNames;


    public CorpusExtractXMLPart(XMLWriter xmlWriter, ExtractParameters extractParameters) {
        super(xmlWriter);
        this.extractParameters = extractParameters;
        this.extractVersion = extractParameters.getExtractVersion();
        this.ficheXMLPart = new FicheXMLPart(xmlWriter, extractParameters);
        this.ficheItemXMLPart = new FicheItemXMLPart(xmlWriter, extractParameters);
        this.tagNames = new TagNames(extractVersion);
    }

    public void addCorpusExtract(CorpusExtractResult corpusExtractResult) throws IOException {
        addCorpusExtract(corpusExtractResult, null);
    }

    public void addCorpusExtract(CorpusExtractResult corpusExtractResult, FilterUnit filterUnit) throws IOException {
        CorpusExtractDef corpusExtractDef = corpusExtractResult.getCorpusExtractDef();
        if (corpusExtractResult.isEmpty()) {
            if (extractParameters.hideIfEmpty(filterUnit)) {
                return;
            }
        }
        String tagName = getTagName(corpusExtractDef.getTagNameInfo());
        boolean withTag = (tagName != null);
        if (corpusExtractResult instanceof CorpusExtractResult.Named) {
            if (withTag) {
                String name = corpusExtractDef.getName();
                startOpenTag(tagName);
                addAttribute("name", name);
                if (extractVersion == ExtractionConstants.INITIAL_VERSION) {
                    addAttribute("extrait-name", name);
                }
                ExtractionXMLUtils.addFilterParameters(this, filterUnit);
                ExtractionXMLUtils.addCellMax(this, corpusExtractDef.getFicheFilter());
                endOpenTag();
            }
            packWriter.setCurrentPackClause(corpusExtractDef.getPackClause());
            for (FicheGroup ficheGroup : ((CorpusExtractResult.Named) corpusExtractResult).getGroupList()) {
                addGroup(ficheGroup);
            }
            packWriter.flush();
            if (withTag) {
                closeTag(tagName);
            }
        } else {
            TitleClause titleClause = corpusExtractDef.getTitleClause();
            boolean withCorpusTitle = ((titleClause != null) && (titleClause.withTitle()));
            for (CorpusExtractResult.Entry entry : ((CorpusExtractResult.Unnamed) corpusExtractResult).getEntryList()) {
                Corpus corpus = entry.getCorpus();
                if (withTag) {
                    startOpenTag(tagName);
                    addAttribute("corpus", corpus.getSubsetName());
                    ExtractionXMLUtils.addFilterParameters(this, filterUnit);
                    ExtractionXMLUtils.addCellMax(this, corpusExtractDef.getFicheFilter());
                    endOpenTag();
                }
                packWriter.setCurrentPackClause(corpusExtractDef.getPackClause());
                for (FicheGroup ficheGroup : entry.getGroupList()) {
                    addGroup(ficheGroup);
                }
                packWriter.flush();
                if (withCorpusTitle) {
                    addCorpusTitle(corpus);
                }
                if (withTag) {
                    closeTag(tagName);
                }
            }

        }
    }

    private void addGroup(FicheGroup group) throws IOException {
        String tagName = getGroupTagName(group.getTagNameInfo());
        if (tagName != null) {
            String name = group.getName();
            startOpenTag(tagName);
            addAttribute("name", name);
            Map<String, String> attributesMap = group.getAttributesMap();
            if (attributesMap != null) {
                for (Map.Entry<String, String> mapEntry : attributesMap.entrySet()) {
                    addAttribute(mapEntry.getKey(), mapEntry.getValue());
                }
            }
            endOpenTag();
            FicheItem[] matchingFicheItemArray = group.getMatchingFicheItemArray();
            if (matchingFicheItemArray != null) {
                for (FicheItem ficheItem : matchingFicheItemArray) {
                    ficheItemXMLPart.addFicheItem(ficheItem, null, ExtractionUtils.SAME_CONVERTER);
                }
            }
        }
        if (group.isBottomGroup()) {
            FicheExtractInfo[] array = group.getFicheExtractInfoArray();
            if (array != null) {
                for (FicheExtractInfo ficheExtractInfo : array) {
                    packWriter.ficheStart();
                    ficheXMLPart.addFiche(ficheExtractInfo);
                    packWriter.ficheEnd();
                }
            }
        } else {
            FicheGroup[] subgroupArray = group.getSubgroupArray();
            if (subgroupArray != null) {
                for (FicheGroup ficheGroup : subgroupArray) {
                    addGroup(ficheGroup);
                }
            }
        }
        if (tagName != null) {
            packWriter.groupFlush();
            closeTag(tagName);
        }
    }

    private void addCorpusTitle(Corpus corpus) throws IOException {
        ExtractionContext extractionContext = extractParameters.getExtractionContext();
        String titleTagName = tagNames.getSubsetTitleTagName();
        openTag(titleTagName);
        LangContext langContext = extractionContext.getLangContext();
        if (langContext instanceof ListLangContext) {
            for (ListLangContext.Unit unit : (ListLangContext) langContext) {
                Lang lang = unit.getLang();
                String title = FichothequeUtils.getTitle(corpus, lang);
                LabelUtils.addLabel(this, lang, title);
            }
        } else if (langContext instanceof UserLangContext) {
            UserLangContext userLangContext = (UserLangContext) langContext;
            Lang workingLang = userLangContext.getWorkingLang();
            String title = FichothequeUtils.getTitle(corpus, workingLang);
            LabelUtils.addLabel(this, null, title);
        }
        closeTag(titleTagName);
    }

    private String getTagName(TagNameInfo tagNameInfo) {
        switch (tagNameInfo.getType()) {
            case TagNameInfo.CUSTOM_TYPE:
                return tagNameInfo.getCustomTagName();
            case TagNameInfo.NULL_TYPE:
                return null;
            default:
                return tagNames.getSubsetTagName(SubsetKey.CATEGORY_CORPUS);
        }
    }

    private String getGroupTagName(TagNameInfo tagNameInfo) {
        switch (tagNameInfo.getType()) {
            case TagNameInfo.CUSTOM_TYPE:
                return tagNameInfo.getCustomTagName();
            case TagNameInfo.NULL_TYPE:
                return null;
            default:
                return "group";
        }
    }

    private String getPackTagName(TagNameInfo tagNameInfo) {
        switch (tagNameInfo.getType()) {
            case TagNameInfo.CUSTOM_TYPE:
                return tagNameInfo.getCustomTagName();
            case TagNameInfo.NULL_TYPE:
                return null;
            default:
                return "pack";
        }
    }


    private class PackWriter {

        private PackClause packClause;
        private String tagName;
        private int packSize;
        private int counter = 0;
        private int modulo = 0;
        private int packCounter = 1;

        PackWriter() {
        }

        void setCurrentPackClause(PackClause newPackClause) {
            reset();
            if (newPackClause != null) {
                String tn = getPackTagName(newPackClause.getTagNameInfo());
                if (tn != null) {
                    int ps = newPackClause.getPackSize();
                    if (ps > 0) {
                        this.packClause = newPackClause;
                        this.tagName = tn;
                        this.packSize = ps;
                        int m = newPackClause.getModulo();
                        if (m < 2) {
                            m = 0;
                        }
                        this.modulo = m;
                    }
                }
            }
        }

        public void ficheStart() throws IOException {
            if (!testPack()) {
                return;
            }
            if (counter == 0) {
                startOpenTag(tagName);
                if (modulo > 0) {
                    addAttribute("modulo-val", packCounter);
                    if (packCounter == modulo) {
                        packCounter = 1;
                    } else {
                        packCounter++;
                    }
                }
                endOpenTag();
            }
            counter++;
        }

        public void ficheEnd() throws IOException {
            if (!testPack()) {
                return;
            }
            if (counter == packSize) {
                closeTag(tagName);
                counter = 0;
            }
        }

        public void groupFlush() throws IOException {
            if (!testPack()) {
                return;
            }
            if (counter > 0) {
                closePack();
            }
        }

        public void flush() throws IOException {
            if (!testPack()) {
                return;
            }
            if (counter > 0) {
                closePack();
            }
            reset();
        }

        private boolean testPack() {
            return (packClause != null);
        }

        private void closePack() throws IOException {
            closeTag(tagName);
            counter = 0;
        }

        private void reset() {
            packClause = null;
            tagName = null;
            counter = 0;
            packSize = 0;
            modulo = 0;
            packCounter = 1;
        }

    }

}
