/* FichothequeLib_Tools - 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.tools.exportation.scrutari;

import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
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.corpus.fiche.DateItem;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.fiche.ParaItem;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.exportation.scrutari.CorpusScrutariDef;
import net.fichotheque.exportation.scrutari.ScrutariExportDef;
import net.fichotheque.exportation.scrutari.ThesaurusScrutariDef;
import net.fichotheque.exportation.table.FormatColDefChecker;
import net.fichotheque.exportation.table.SubsetTable;
import net.fichotheque.exportation.table.TableDef;
import net.fichotheque.exportation.table.TableExportContext;
import net.fichotheque.exportation.table.TableInclusionResolverProvider;
import net.fichotheque.format.FichothequeFormatDef;
import net.fichotheque.format.FormatContext;
import net.fichotheque.format.FormatSource;
import net.fichotheque.format.FormatSourceKey;
import net.fichotheque.format.SourceLabelProvider;
import net.fichotheque.format.formatters.SourceFormatter;
import net.fichotheque.pointeurs.FichePointeur;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.tools.exportation.table.SubsetTableBuilder;
import net.fichotheque.tools.exportation.table.TableDefParser;
import net.mapeadores.util.attr.AttributeKey;
import net.mapeadores.util.format.FormatConstants;
import net.mapeadores.util.logging.LineLogs;
import net.mapeadores.util.logging.LineLogsBuilder;
import net.mapeadores.util.logging.LineMessageHandler;
import net.mapeadores.util.logging.LogUtils;
import net.mapeadores.util.date.FuzzyDate;
import net.mapeadores.util.text.AccoladePattern;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
public final class ScrutariExportUtils {

    public final static SourceFormatter TITRE = (formatSource) -> {
        return (String) ((FichePointeur) formatSource.getSubsetItemPointeur()).getValue(FieldKey.TITLE);
    };
    public final static SourceFormatter SOUSTITRE = (formatSource) -> {
        ParaItem para = (ParaItem) ((FichePointeur) formatSource.getSubsetItemPointeur()).getValue(FieldKey.SUBTITLE);
        if (para != null) {
            return para.contentToString();
        } else {
            return null;
        }
    };
    public final static SourceFormatter DATE_CREATION = (formatSource) -> {
        FuzzyDate date = ((FicheMeta) formatSource.getSubsetItemPointeur().getCurrentSubsetItem()).getCreationDate();
        if (date != null) {
            return date.toString();
        } else {
            return null;
        }
    };
    public final static SourceFormatter DATE_MODIFICATION = (formatSource) -> {
        FuzzyDate date = ((FicheMeta) formatSource.getSubsetItemPointeur().getCurrentSubsetItem()).getModificationDate();
        if (date != null) {
            return date.toString();
        } else {
            return null;
        }
    };
    public final static SourceFormatter NULL = (formatSource) -> {
        return null;
    };

    private ScrutariExportUtils() {
    }

    public static CorpusScrutariDef getDefaultScrutariCorpusDef(SubsetKey corpusKey) {
        return new DefaultCorpusScrutariDef(corpusKey);
    }

    public static Map<SubsetKey, CorpusScrutariDef> toCorpusScrutariDefMap(ScrutariExportDef scrutariExportDef) {
        Map<SubsetKey, CorpusScrutariDef> map = new HashMap<SubsetKey, CorpusScrutariDef>();
        for (CorpusScrutariDef corpusScrutariDef : scrutariExportDef.getCorpusScrutariDefList()) {
            map.put(corpusScrutariDef.getCorpusKey(), corpusScrutariDef);
        }
        return map;
    }

    public static Map<SubsetKey, ThesaurusScrutariDef> toThesaurusScrutariDefMap(ScrutariExportDef scrutariExportDef) {
        Map<SubsetKey, ThesaurusScrutariDef> map = new HashMap<SubsetKey, ThesaurusScrutariDef>();
        for (ThesaurusScrutariDef thesaurusScrutariDef : scrutariExportDef.getThesaurusScrutariDefList()) {
            map.put(thesaurusScrutariDef.getThesaurusKey(), thesaurusScrutariDef);
        }
        return map;
    }

    public static Map<SubsetKey, LineLogs> checkFieldGeneration(ScrutariExportDef scrutariExportDef, TableExportContext tableExportContext) {
        Map<SubsetKey, LineLogs> lineLogMap = new LinkedHashMap<SubsetKey, LineLogs>();
        Fichotheque fichotheque = tableExportContext.getFichotheque();
        TableExportContext scrutariTableExportContext = toScrutariTableExportContext(tableExportContext);
        for (CorpusScrutariDef corpusScrutariDef : scrutariExportDef.getCorpusScrutariDefList()) {
            Corpus corpus = (Corpus) fichotheque.getSubset(corpusScrutariDef.getCorpusKey());
            if (corpus != null) {
                LineLogsBuilder lineLogsBuilder = new LineLogsBuilder();
                TableDefParser.parse(corpusScrutariDef.getFieldGenerationSource(), corpus, scrutariTableExportContext, lineLogsBuilder, 0);
                if (lineLogsBuilder.hasMessage()) {
                    LineLogs lineLogs = lineLogsBuilder.toLineLogs();
                    lineLogMap.put(corpus.getSubsetKey(), lineLogs);
                }
            }
        }
        for (ThesaurusScrutariDef thesaurusScrutariDef : scrutariExportDef.getThesaurusScrutariDefList()) {
            Thesaurus thesaurus = (Thesaurus) fichotheque.getSubset(thesaurusScrutariDef.getThesaurusKey());
            if (thesaurus != null) {
                LineLogsBuilder lineLogsBuilder = new LineLogsBuilder();
                TableDefParser.parse(thesaurusScrutariDef.getFieldGenerationSource(), thesaurus, scrutariTableExportContext, lineLogsBuilder, 0);
                if (lineLogsBuilder.hasMessage()) {
                    LineLogs lineLogs = lineLogsBuilder.toLineLogs();
                    lineLogMap.put(thesaurus.getSubsetKey(), lineLogs);
                }
            }
        }
        return lineLogMap;
    }

    public static TableExportContext toScrutariTableExportContext(TableExportContext origin) {
        return new ScrutariTableExportContext(origin);
    }

    public static boolean testColName(short category, String colName) {
        int idx = colName.indexOf(":");
        if (idx > 0) {
            try {
                AttributeKey.parse(colName);
                return true;
            } catch (ParseException pe) {
                return false;
            }
        }
        switch (category) {
            case SubsetKey.CATEGORY_CORPUS:
                if (colName.startsWith("comp")) {
                    return true;
                } else {
                    switch (colName) {
                        case "titre":
                        case "soustitre":
                        case "date":
                        case "href":
                        case "ficheicon":
                            return true;
                        default:
                            return false;
                    }
                }
            case SubsetKey.CATEGORY_THESAURUS:
                switch (colName) {
                    case "labels":
                        return true;
                    default:
                        return false;
                }
            default:
                return false;
        }
    }

    public static SourceFormatter getDefaultLabelsFormatter(Thesaurus thesaurus, TableExportContext tableExportContext) {
        StringBuilder buf = new StringBuilder();
        buf.append("labels\nthis\n");
        if (thesaurus.isIdalphaType()) {
            if (thesaurus.getThesaurusMetadata().isBracketsIdalphaStyle()) {
                buf.append("[{idalpha}] {label}");
            } else {
                buf.append("{idalpha} – {label}");
            }
        } else {
            buf.append("{label}");
        }
        TableDef tableDef = TableDefParser.parse(buf.toString(), thesaurus, tableExportContext, LogUtils.NULL_LINEMESSAGEHANDLER, 0);
        SubsetTable subsetTable = SubsetTableBuilder.init(thesaurus).populate(tableDef, tableExportContext).toSubsetTable();
        return subsetTable.getColList().get(0).getSourceFormatter();
    }

    public static SourceFormatter getDateFieldFormatter(FieldKey fieldKey) {
        return new DateFieldSourceFormatter(fieldKey);
    }


    private static class DefaultCorpusScrutariDef implements CorpusScrutariDef {

        private final SubsetKey corpusKey;

        private DefaultCorpusScrutariDef(SubsetKey corpusKey) {
            this.corpusKey = corpusKey;
        }

        @Override
        public String getFieldGenerationSource() {
            return "";
        }

        @Override
        public SubsetKey getCorpusKey() {
            return corpusKey;
        }

        @Override
        public FormatSourceKey getDateFormatSourceKey() {
            return FormatSourceKey.DATE_CREATION;
        }

        @Override
        public AccoladePattern getHrefPattern() {
            return null;
        }

        @Override
        public String getMultilangMode() {
            return null;
        }

        @Override
        public String getMultilangParam() {
            return null;
        }

        @Override
        public List<String> getIncludeTokenList() {
            return StringUtils.EMPTY_STRINGLIST;
        }

    }


    private static class ScrutariTableExportContext implements TableExportContext {

        private final TableExportContext tableExportContext;

        private ScrutariTableExportContext(TableExportContext tableExportContext) {
            this.tableExportContext = tableExportContext;
        }

        @Override
        public FormatColDefChecker getFormatColDefChecker(Subset subset) {
            return new ScrutariFormatColDefChecker(subset.getSubsetKey().getCategory(), tableExportContext.getFormatColDefChecker(subset));
        }

        @Override
        public FormatContext getFormatContext() {
            return tableExportContext.getFormatContext();
        }

        @Override
        public TableInclusionResolverProvider getTableInclusionResolverProvider() {
            return tableExportContext.getTableInclusionResolverProvider();
        }

        @Override
        public SourceLabelProvider getSourceLabelProvider() {
            return tableExportContext.getSourceLabelProvider();
        }

    }


    private static class ScrutariFormatColDefChecker implements FormatColDefChecker {

        private final short category;
        private final FormatColDefChecker formatColDefChecker;

        private ScrutariFormatColDefChecker(short category, FormatColDefChecker formatColDefChecker) {
            this.category = category;
            this.formatColDefChecker = formatColDefChecker;
        }

        @Override
        public boolean checkFormatColDef(String colName, FichothequeFormatDef formatDef, int lineNumber, LineMessageHandler lineMessageHandler) {
            if (!testColName(category, colName)) {
                lineMessageHandler.addMessage(FormatConstants.WARNING_FICHOTHEQUE, lineNumber, "_ error.unsupported.scrutariexportcolname", colName);
                return false;
            }
            return formatColDefChecker.checkFormatColDef(colName, formatDef, lineNumber, lineMessageHandler);
        }

    }


    private static class DateFieldSourceFormatter implements SourceFormatter {

        private final FieldKey fieldKey;

        private DateFieldSourceFormatter(FieldKey fieldKey) {
            this.fieldKey = fieldKey;
        }

        @Override
        public String formatSource(FormatSource formatSource) {
            FuzzyDate date = null;
            Object obj = ((FichePointeur) formatSource.getSubsetItemPointeur()).getValue(fieldKey);
            if (obj != null) {
                if (obj instanceof DateItem) {
                    date = ((DateItem) obj).getDate();
                } else if (obj instanceof FicheItems) {
                    FicheItems fil = (FicheItems) obj;
                    if (fil.size() > 0) {
                        FicheItem fi = fil.get(0);
                        if (fi instanceof DateItem) {
                            date = ((DateItem) fi).getDate();
                        }
                    }
                }
            }
            if (date != null) {
                return date.toString();
            } else {
                return null;
            }
        }

    }


}
