/* BdfServer - Copyright (c) 2016-2023 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 fr.exemole.bdfserver.impl;

import fr.exemole.bdfserver.api.configuration.LangConfiguration;
import fr.exemole.bdfserver.api.managers.L10nManager;
import fr.exemole.bdfserver.api.managers.UiManager;
import fr.exemole.bdfserver.api.ui.IncludeUi;
import fr.exemole.bdfserver.api.ui.UiComponents;
import fr.exemole.bdfserver.tools.L10nUtils;
import net.fichotheque.Fichotheque;
import net.fichotheque.Subset;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.exportation.table.FormatColDefChecker;
import net.fichotheque.exportation.table.TableExportContext;
import net.fichotheque.exportation.table.TableInclusionResolver;
import net.fichotheque.exportation.table.TableInclusionResolverProvider;
import net.fichotheque.format.FichothequeFormatDef;
import net.fichotheque.format.FormatContext;
import net.fichotheque.format.FormatSourceKey;
import net.fichotheque.format.SourceLabelProvider;
import net.fichotheque.format.SubsetPathKey;
import net.fichotheque.format.formatters.SourceFormatter;
import net.fichotheque.include.IncludeKey;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.thesaurus.metadata.ThesaurusFieldKey;
import net.fichotheque.tools.exportation.table.inclusionresolvers.AlphabeticInclusionResolver;
import net.fichotheque.tools.exportation.table.inclusionresolvers.ChecksumInclusionResolver;
import net.fichotheque.tools.exportation.table.inclusionresolvers.JsonFicheDistributionInclusionResolver;
import net.fichotheque.tools.exportation.table.inclusionresolvers.MultilangInclusionResolver;
import net.fichotheque.tools.format.FichothequeFormatDefEngine;
import net.fichotheque.utils.TableDefUtils;
import net.mapeadores.util.logging.LineMessageHandler;
import net.mapeadores.util.text.LabelUtils;
import net.mapeadores.util.text.Labels;


/**
 *
 * @author Vincent Calame
 */
class TableExportContextImpl implements TableExportContext {

    private final static AlphabeticInclusionResolver ALPHABETIC_INCLUSIONRESOLVER = new AlphabeticInclusionResolver();
    private final static MultilangInclusionResolver MULTILANG_INCLUSIONRESOLVER = new MultilangInclusionResolver();
    private final static JsonFicheDistributionInclusionResolver JSONFICHEDISTRIBUTION_INCLUSIONRESOLVER = new JsonFicheDistributionInclusionResolver();
    private final SourceLabelProviderImpl sourceLabelProvider = new SourceLabelProviderImpl();
    private final TableInclusionResolverProviderImpl tableInclusionResolverProvider = new TableInclusionResolverProviderImpl();
    private final Fichotheque fichotheque;
    private final ExtensionManagerImpl extensionManager;
    private final FormatContext formatContext;
    private final LangConfiguration langConfiguration;
    private final L10nManager l10nManager;
    private final UiManager uiManager;

    TableExportContextImpl(Fichotheque fichotheque, ExtensionManagerImpl extensionManager, FormatContext formatContext, LangConfiguration langConfiguration, L10nManager l10nManager, UiManager uiManager) {
        this.fichotheque = fichotheque;
        this.extensionManager = extensionManager;
        this.formatContext = formatContext;
        this.langConfiguration = langConfiguration;
        this.l10nManager = l10nManager;
        this.uiManager = uiManager;
    }

    @Override
    public FormatColDefChecker getFormatColDefChecker(Subset subset) {
        return new FormatColDefCheckerImpl(subset);
    }

    @Override
    public FormatContext getFormatContext() {
        return formatContext;
    }

    @Override
    public TableInclusionResolverProvider getTableInclusionResolverProvider() {
        return tableInclusionResolverProvider;
    }

    @Override
    public SourceLabelProvider getSourceLabelProvider() {
        return sourceLabelProvider;
    }


    private class TableInclusionResolverProviderImpl implements TableInclusionResolverProvider {

        private TableInclusionResolverProviderImpl() {

        }

        @Override
        public TableInclusionResolver getTableInclusionResolver(String inclusionNameSpace) {
            TableInclusionResolver tableInclusionResolver = extensionManager.getExtensionTableInclusionResolver(inclusionNameSpace);
            if (tableInclusionResolver != null) {
                return tableInclusionResolver;
            }
            switch (inclusionNameSpace) {
                case AlphabeticInclusionResolver.NAMESPACE:
                    return ALPHABETIC_INCLUSIONRESOLVER;
                case MultilangInclusionResolver.NAMESPACE:
                    return MULTILANG_INCLUSIONRESOLVER;
                case JsonFicheDistributionInclusionResolver.NAMESPACE:
                    return JSONFICHEDISTRIBUTION_INCLUSIONRESOLVER;
                case ChecksumInclusionResolver.MD5_NAMESPACE:
                    return ChecksumInclusionResolver.MD5_INSTANCE;
                case ChecksumInclusionResolver.SHA1_NAMESPACE:
                    return ChecksumInclusionResolver.SHA1_INSTANCE;
                case ChecksumInclusionResolver.SHA2_NAMESPACE:
                    return ChecksumInclusionResolver.SHA2_INSTANCE;
                default:
                    return null;
            }
        }

    }


    private class SourceLabelProviderImpl implements SourceLabelProvider {


        private SourceLabelProviderImpl() {

        }

        @Override
        public Labels getLabels(Subset subset, FormatSourceKey formatSourceKey) {
            Labels labels = innerGetLabels(subset, formatSourceKey);
            if (labels == null) {
                return LabelUtils.EMPTY_LABELS;
            } else {
                return labels;
            }
        }

        private Labels innerGetLabels(Subset mainSubset, FormatSourceKey formatSourceKey) {
            Subset labelSubset = mainSubset;
            SubsetPathKey subsetPathKey = formatSourceKey.getSubsetPathKey();
            if (subsetPathKey != null) {
                SubsetKey lastMatchingSubsetKey = subsetPathKey.getMatchingSubsetKey(subsetPathKey.getLength() - 1);
                if (lastMatchingSubsetKey != null) {
                    labelSubset = fichotheque.getSubset(lastMatchingSubsetKey);
                    if (labelSubset == null) {
                        return null;
                    }
                }
            }
            if (labelSubset instanceof Corpus) {
                return getCorpusLabelHolder((Corpus) labelSubset, formatSourceKey);
            } else if (labelSubset instanceof Thesaurus) {
                return getThesaurusLabels((Thesaurus) labelSubset, formatSourceKey);
            } else {
                return null;
            }
        }

        private Labels getCorpusLabelHolder(Corpus corpus, FormatSourceKey formatSourceKey) {
            switch (formatSourceKey.getSourceType()) {
                case FormatSourceKey.FIELDKEY_TYPE:
                    CorpusField corpusField = corpus.getCorpusMetadata().getCorpusField((FieldKey) formatSourceKey.getKeyObject());
                    if (corpusField == null) {
                        return null;
                    } else {
                        return corpusField.getLabels();
                    }
                case FormatSourceKey.INCLUDEKEY_TYPE:
                    return getSubsetIncludeLabels(corpus, (IncludeKey) formatSourceKey.getKeyObject());
                case FormatSourceKey.ID_TYPE:
                    return corpus.getCorpusMetadata().getCorpusField(FieldKey.ID).getLabels();
                case FormatSourceKey.LANG_TYPE:
                    return corpus.getCorpusMetadata().getCorpusField(FieldKey.LANG).getLabels();
                case FormatSourceKey.SPECIALINCLUDENAME_TYPE:
                    return getSpecialIncludeLabels(corpus, (String) formatSourceKey.getKeyObject());
                default:
                    return null;
            }
        }


        private Labels getThesaurusLabels(Thesaurus thesaurus, FormatSourceKey formatSourceKey) {
            switch (formatSourceKey.getSourceType()) {
                case FormatSourceKey.THESAURUSFIELDKEY_TYPE:
                    return L10nUtils.toLabels(l10nManager, (ThesaurusFieldKey) formatSourceKey.getKeyObject(), langConfiguration.getWorkingLangs());
                case FormatSourceKey.INCLUDEKEY_TYPE:
                    return getDefaultLabels((IncludeKey) formatSourceKey.getKeyObject());
                case FormatSourceKey.ID_TYPE:
                    return L10nUtils.toLabels(l10nManager, ThesaurusFieldKey.ID, langConfiguration.getWorkingLangs());
                case FormatSourceKey.LANG_TYPE:
                    return L10nUtils.toLabels(l10nManager, ThesaurusFieldKey.BABELIENLANG, langConfiguration.getWorkingLangs());
                case FormatSourceKey.SPECIALINCLUDENAME_TYPE:
                    return L10nUtils.toLabels(l10nManager, (String) formatSourceKey.getKeyObject(), langConfiguration.getWorkingLangs());
                default:
                    return null;
            }
        }

        private Labels getSubsetIncludeLabels(Corpus corpus, IncludeKey includeKey) {
            UiComponents uiComponents = uiManager.getMainUiComponents(corpus);
            if (uiComponents == null) {
                return null;
            }
            IncludeUi includeUi = (IncludeUi) uiComponents.getUiComponent(includeKey);
            Labels labels = null;
            if (includeUi != null) {
                labels = includeUi.getCustomLabels();
            }
            if (labels == null) {
                labels = getDefaultLabels(includeKey);
            }
            return labels;
        }

        private Labels getSpecialIncludeLabels(Corpus corpus, String specialIncludeName) {
            UiComponents uiComponents = uiManager.getMainUiComponents(corpus);
            if (uiComponents == null) {
                return null;
            }
            IncludeUi includeUi = (IncludeUi) uiComponents.getUiComponent(specialIncludeName);
            Labels labels = null;
            if (includeUi != null) {
                labels = includeUi.getCustomLabels();
            }
            if (labels == null) {
                labels = L10nUtils.toLabels(l10nManager, specialIncludeName, langConfiguration.getWorkingLangs());
            }
            return labels;
        }

        private Labels getDefaultLabels(IncludeKey includeKey) {
            SubsetKey subsetKey = includeKey.getSubsetKey();
            if (subsetKey.isThesaurusSubset()) {
                Thesaurus thesaurus = (Thesaurus) fichotheque.getSubset(subsetKey);
                if (thesaurus != null) {
                    return thesaurus.getThesaurusMetadata().getTitleLabels();
                }
            } else if (subsetKey.isCorpusSubset()) {
                Corpus corpus = (Corpus) fichotheque.getSubset(subsetKey);
                if (corpus != null) {
                    return corpus.getCorpusMetadata().getTitleLabels();
                }
            }
            return null;
        }

    }


    private class FormatColDefCheckerImpl implements FormatColDefChecker {

        private final Subset subset;
        private final FichothequeFormatDefEngine.Parameters parameters;

        private FormatColDefCheckerImpl(Subset subset) {
            this.subset = subset;
            this.parameters = FichothequeFormatDefEngine.parameters(formatContext);
        }


        @Override
        public boolean checkFormatColDef(String colName, FichothequeFormatDef formatDef, int lineNumber, LineMessageHandler lineMessageHandler) {
            SourceFormatter formatter = FichothequeFormatDefEngine.compute(subset, formatDef, TableDefUtils.toFormatDefMessageHandler(lineMessageHandler, lineNumber), parameters);
            return (formatter != null);
        }

    }


}
