/* FichothequeLib_Tools - Copyright (c) 2017-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.exportation.table.inclusionresolvers;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import net.fichotheque.Fichotheque;
import net.fichotheque.Subset;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.exportation.table.Col;
import net.fichotheque.exportation.table.TableExportContext;
import net.fichotheque.exportation.table.TableInclusionDef;
import net.fichotheque.exportation.table.TableInclusionResolver;
import net.fichotheque.format.FormatSource;
import net.fichotheque.format.formatters.SourceFormatter;
import net.fichotheque.include.IncludeKey;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.tools.corpus.FicheDistribution;
import net.fichotheque.tools.corpus.FicheDistributionEngine;
import net.fichotheque.tools.corpus.GroupingKey;
import net.fichotheque.utils.TableDefUtils;
import net.fichotheque.utils.TableExportUtils;
import net.mapeadores.util.exceptions.ShouldNotOccurException;
import net.mapeadores.util.format.FormatConstants;
import net.mapeadores.util.json.JSONWriter;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.logging.LineMessageHandler;
import net.mapeadores.util.logging.LogUtils;
import net.mapeadores.util.primitives.FuzzyDate;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
public class JsonFicheDistributionInclusionResolver implements TableInclusionResolver {

    public final static String NAMESPACE = "json_fichedistribution";

    public JsonFicheDistributionInclusionResolver() {

    }

    @Override
    public boolean test(TableInclusionDef tableInclusionDef, Subset subset, TableExportContext tableExportContext, int lineNumber, LineMessageHandler lineMessageHandler) {
        String nameSpace = tableInclusionDef.getNameSpace();
        if (!nameSpace.equals(NAMESPACE)) {
            return false;
        }
        FicheDistributionEngine ficheDistributionEngine = parseConfig(subset.getFichotheque(), tableInclusionDef.getConfigLineList(), lineNumber + 1, lineMessageHandler);
        return !ficheDistributionEngine.isEmpty();
    }

    @Override
    public Col[] resolve(TableInclusionDef tableInclusionDef, Subset subset, TableExportContext tableExportContext) {
        String nameSpace = tableInclusionDef.getNameSpace();
        if (!nameSpace.equals(NAMESPACE)) {
            return TableExportUtils.EMPTY_COLARRAY;
        }
        FicheDistributionEngine ficheDistributionEngine = parseConfig(subset.getFichotheque(), tableInclusionDef.getConfigLineList(), 0, LogUtils.NULL_LINEMESSAGEHANDLER);
        InternalSourceFormatter sourceFormatter = new InternalSourceFormatter(ficheDistributionEngine);
        String localName = tableInclusionDef.getLocalName();
        if (localName.length() == 0) {
            localName = NAMESPACE;
        }
        Col[] colArray = new Col[1];
        colArray[0] = TableExportUtils.toCol(TableDefUtils.toColDef(localName, tableInclusionDef.getLabels()), sourceFormatter);
        return colArray;
    }

    private FicheDistributionEngine parseConfig(Fichotheque fichotheque, List<String> configLineList, int startLineNumber, LineMessageHandler lineMessageHandler) {
        FicheDistributionEngine ficheDistributionEngine = new FicheDistributionEngine(fichotheque);
        for (String configLine : configLineList) {
            String[] tokens = StringUtils.getTokens(configLine, '|', StringUtils.EMPTY_INCLUDE);
            int length = tokens.length;
            if (length > 0) {
                List<IncludeKey> corpusIncludeKeyList = toIncludeKeyList(fichotheque, tokens[0], startLineNumber, lineMessageHandler);
                GroupingKey groupingKey = GroupingKey.CREATION_DATE;
                if (length > 2) {
                    try {
                        groupingKey = GroupingKey.parse(tokens[2]);
                    } catch (ParseException pe) {
                        lineMessageHandler.addMessage(FormatConstants.SEVERE_SYNTAX, startLineNumber, "_ error.wrong.fieldkey", tokens[2]);
                    }
                }
                String thesaurusPart = null;
                if (length > 1) {
                    thesaurusPart = tokens[1];
                }
                if ((thesaurusPart == null) || (thesaurusPart.startsWith("="))) {
                    String fixedString;
                    if (thesaurusPart == null) {
                        fixedString = "";
                    } else {
                        fixedString = thesaurusPart.substring(1).trim();
                    }
                    for (IncludeKey corpusIncludeKey : corpusIncludeKeyList) {
                        ficheDistributionEngine.addCorpus(corpusIncludeKey, fixedString, groupingKey);
                    }
                } else {
                    IncludeKey thesaurusIncludeKey = toIncludeKey(fichotheque, thesaurusPart, startLineNumber, lineMessageHandler);
                    if (thesaurusIncludeKey != null) {
                        for (IncludeKey corpusIncludeKey : corpusIncludeKeyList) {
                            ficheDistributionEngine.addCorpus(corpusIncludeKey, thesaurusIncludeKey, groupingKey);
                        }
                    }
                }
            }
            startLineNumber++;
        }
        return ficheDistributionEngine;
    }

    private List<IncludeKey> toIncludeKeyList(Fichotheque fichotheque, String corpusPart, int lineNumber, LineMessageHandler lineMessageHandler) {
        String[] tokens = StringUtils.getTechnicalTokens(corpusPart, true);
        List<IncludeKey> includeKeyList = new ArrayList<IncludeKey>();
        if (tokens.length == 0) {
            lineMessageHandler.addMessage(FormatConstants.SEVERE_SYNTAX, lineNumber, "_ error.wrong.includekey", corpusPart);
        } else {
            for (String token : tokens) {
                try {
                    IncludeKey includeKey = IncludeKey.parse(token);
                    SubsetKey subsetKey = includeKey.getSubsetKey();
                    if (!subsetKey.isCorpusSubset()) {
                        lineMessageHandler.addMessage(FormatConstants.SEVERE_SYNTAX, lineNumber, "_ error.unsupported.subset", token);
                    } else {
                        Corpus corpus = (Corpus) fichotheque.getSubset(subsetKey);
                        if (corpus == null) {
                            lineMessageHandler.addMessage(FormatConstants.WARNING_FICHOTHEQUE, lineNumber, "_ error.unknown.corpus", token);
                        } else {
                            includeKeyList.add(includeKey);
                        }
                    }
                } catch (ParseException pe) {
                    lineMessageHandler.addMessage(FormatConstants.SEVERE_SYNTAX, lineNumber, "_ error.wrong.includekey", token);
                }
            }
        }
        return includeKeyList;
    }

    private IncludeKey toIncludeKey(Fichotheque fichotheque, String thesaurusPart, int lineNumber, LineMessageHandler lineMessageHandler) {
        try {
            IncludeKey thesaurusIncludeKey = IncludeKey.parse(thesaurusPart);
            SubsetKey subsetKey = thesaurusIncludeKey.getSubsetKey();
            if (!subsetKey.isThesaurusSubset()) {
                lineMessageHandler.addMessage(FormatConstants.SEVERE_SYNTAX, lineNumber, "_ error.unsupported.subset", thesaurusPart);
                return null;
            } else {
                Thesaurus thesaurus = (Thesaurus) fichotheque.getSubset(subsetKey);
                if (thesaurus == null) {
                    lineMessageHandler.addMessage(FormatConstants.WARNING_FICHOTHEQUE, lineNumber, "_ error.unknown.thesaurus", thesaurusPart);
                    return null;
                } else {
                    return thesaurusIncludeKey;
                }
            }
        } catch (ParseException pe) {
            lineMessageHandler.addMessage(FormatConstants.SEVERE_SYNTAX, lineNumber, "_ error.wrong.includekey", thesaurusPart);
            return null;
        }
    }


    private static class InternalSourceFormatter implements SourceFormatter {

        private final FicheDistributionEngine ficheDistributionEngine;

        private InternalSourceFormatter(FicheDistributionEngine ficheDistributionEngine) {
            this.ficheDistributionEngine = ficheDistributionEngine;
        }

        @Override
        public String formatSource(FormatSource formatSource) {
            FicheDistribution ficheDistribution = ficheDistributionEngine.run(formatSource.getSubsetItemPointeur().getCurrentSubsetItem(), formatSource.getGlobalPredicate());
            List<FicheDistribution.Group> groupList = ficheDistribution.toGroupList();
            if (groupList.isEmpty()) {
                return null;
            }
            StringBuilder buf = new StringBuilder();
            JSONWriter jw = new JSONWriter(buf);
            try {
                jw.object();
                for (FicheDistribution.Group group : groupList) {
                    jw.key(group.getName());
                    jw.array();
                    for (FicheDistribution.Year year : group.toYearList()) {
                        Integer yearInteger = year.getYearInteger();
                        jw.object();
                        {
                            jw.key("year");
                            if (yearInteger != null) {
                                jw.value(yearInteger.longValue());
                            } else {
                                jw.value(null);
                            }
                            jw.key("fiches");
                            jw.array();
                            for (FicheDistribution.FicheInfo ficheInfo : year.toFicheInfoList()) {
                                writeFicheInfo(jw, ficheInfo);
                            }
                            jw.endArray();
                        }
                        jw.endObject();
                    }
                    for (FicheDistribution.ByLang byLang : group.getByLangList()) {
                        Lang lang = byLang.getLang();
                        jw.object();
                        {
                            jw.key("lang")
                                    .value(lang.toString());
                            jw.key("fiches");
                            jw.array();
                            for (FicheDistribution.FicheInfo ficheInfo : byLang.toFicheInfoList()) {
                                writeFicheInfo(jw, ficheInfo);
                            }
                            jw.endArray();
                        }
                        jw.endObject();
                    }
                    jw.endArray();
                }
                jw.endObject();
            } catch (IOException ioe) {
                throw new ShouldNotOccurException(ioe);
            }
            return buf.toString();
        }

        private void writeFicheInfo(JSONWriter jw, FicheDistribution.FicheInfo ficheInfo) throws IOException {
            FicheMeta ficheMeta = ficheInfo.getFicheMeta();
            jw.object();
            {
                jw.key("corpus")
                        .value(ficheMeta.getSubsetName());
                jw.key("id")
                        .value(ficheMeta.getId());
                jw.key("lang");
                Lang lang = ficheMeta.getLang();
                if (lang != null) {
                    jw.value(lang.toString());
                } else {
                    jw.value("und");
                }
                FuzzyDate date = ficheInfo.getFuzzyDate();
                jw.key("date");
                if (date != null) {
                    jw.value(date.toString());
                } else {
                    jw.value(null);
                }
            }
            jw.endObject();
        }

    }


}
