/* BdfServer - 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 fr.exemole.bdfserver.tools.exportation.table;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.instruction.BdfParameters;
import fr.exemole.bdfserver.api.ui.UiComponent;
import fr.exemole.bdfserver.api.ui.UiComponents;
import fr.exemole.bdfserver.tools.BdfServerUtils;
import fr.exemole.bdfserver.tools.ui.UiUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.exportation.table.CellConverterProvider;
import net.fichotheque.exportation.table.SubsetTable;
import net.fichotheque.exportation.table.SumCellConverter;
import net.fichotheque.exportation.table.TableDef;
import net.fichotheque.exportation.table.TableExport;
import net.fichotheque.exportation.table.TableExportDef;
import net.fichotheque.extraction.ExtractionContext;
import net.fichotheque.permission.PermissionSummary;
import net.fichotheque.tools.exportation.table.SubsetTableBuilder;
import net.fichotheque.tools.exportation.table.SumCellConverterFactory;
import net.fichotheque.tools.exportation.table.TableExportBuilder;
import net.fichotheque.utils.TableExportUtils;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.logging.ErrorMessageException;


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

    public final static Result EMPTY_RESULT = new Result(TableExportUtils.EMPTY_SUMCELLCONVERTER, Collections.emptyMap());

    private CellConversionInit() {

    }

    public static Result subsetTable(BdfServer bdfServer, ExtractionContext extractionContext, SubsetTable subsetTable) {
        return subsetTable(bdfServer, extractionContext, subsetTable, null, null, true);
    }

    public static Result subsetTable(BdfServer bdfServer, ExtractionContext extractionContext, SubsetTable subsetTable, @Nullable TableExportDef tableExportDef) {
        return subsetTable(bdfServer, extractionContext, subsetTable, tableExportDef, null, true);
    }

    public static Result subsetTable(BdfServer bdfServer, ExtractionContext extractionContext, SubsetTable subsetTable, @Nullable TableExportDef tableExportDef, @Nullable Predicate<SubsetItem> globalPredicate) {
        return subsetTable(bdfServer, extractionContext, subsetTable, tableExportDef, globalPredicate, true);
    }

    public static Result subsetTable(BdfServer bdfServer, ExtractionContext extractionContext, SubsetTable subsetTable, @Nullable TableExportDef tableExportDef, @Nullable Predicate<SubsetItem> globalPredicate, boolean withLangContextCheck) {
        if (withLangContextCheck) {
            extractionContext = BdfServerUtils.checkLangMode(extractionContext, bdfServer, tableExportDef);
        }
        CellConverterProvider cellConverterProvider = CellConverterProviderFactory.newInstance(bdfServer, extractionContext, globalPredicate, withLangContextCheck);
        SumCellConverter cellConverter = SumCellConverterFactory.newInstance(subsetTable, bdfServer.getTableExportContext(), extractionContext, cellConverterProvider, globalPredicate);
        return new Result(cellConverter, Collections.singletonMap(subsetTable.getSubsetKey(), subsetTable));
    }

    public static Result tableExport(BdfServer bdfServer, ExtractionContext extractionContext, TableExport tableExport) {
        return tableExport(bdfServer, extractionContext, tableExport, null, true);
    }

    public static Result tableExport(BdfServer bdfServer, ExtractionContext extractionContext, TableExport tableExport, Predicate<SubsetItem> globalPredicate) {
        return tableExport(bdfServer, extractionContext, tableExport, globalPredicate, true);
    }

    public static Result tableExport(BdfServer bdfServer, ExtractionContext extractionContext, TableExport tableExport, Predicate<SubsetItem> globalPredicate, boolean withLangContextCheck) {
        if (withLangContextCheck) {
            extractionContext = BdfServerUtils.checkLangMode(extractionContext, bdfServer, tableExport.getTableExportDef());
        }
        CellConverterProvider cellConverterProvider = CellConverterProviderFactory.newInstance(bdfServer, extractionContext, globalPredicate, withLangContextCheck);
        List<SubsetTable> list = tableExport.getSubsetTableList();
        Map<SubsetKey, SubsetTable> map = new HashMap<SubsetKey, SubsetTable>();
        for (SubsetTable subsetTable : list) {
            map.put(subsetTable.getSubset().getSubsetKey(), subsetTable);
        }
        SumCellConverter cellConverter = SumCellConverterFactory.newInstance(list, bdfServer.getTableExportContext(), extractionContext, cellConverterProvider, globalPredicate);
        return new Result(cellConverter, map);
    }

    public static Result fromTableExportName(BdfServer bdfServer, ExtractionContext extractionContext, String tableExportName) throws ErrorMessageException {
        return fromTableExportName(bdfServer, extractionContext, tableExportName, null, true);
    }

    public static Result fromTableExportName(BdfServer bdfServer, ExtractionContext extractionContext, String tableExportName, Predicate<SubsetItem> globalPredicate) throws ErrorMessageException {
        return fromTableExportName(bdfServer, extractionContext, tableExportName, globalPredicate, true);
    }

    public static Result fromTableExportName(BdfServer bdfServer, ExtractionContext extractionContext, String tableExportName, Predicate<SubsetItem> globalPredicate, boolean withLangContextCheck) throws ErrorMessageException {
        TableExport tableExport = bdfServer.getTableExportManager().getTableExport(tableExportName);
        if (tableExport == null) {
            throw new ErrorMessageException("_ error.unknown.tablexport", tableExportName);
        }
        return tableExport(bdfServer, extractionContext, tableExport, globalPredicate, withLangContextCheck);
    }

    public static Result specialTable(BdfParameters parameters, SubsetKey contextCorpusKey, String patternMode, @Nullable Corpus corpus) {
        return fromProvider(parameters, corpus, new TableFilterProvider(parameters, patternMode, contextCorpusKey));
    }

    public static Result components(BdfParameters parameters, List<String> componentNameList, String patternMode, @Nullable Corpus corpus) {
        return fromProvider(parameters, corpus, new SelectionFilterProvider(parameters, patternMode, componentNameList));
    }

    public static Result specialUserDefault(BdfParameters parameters, @Nullable Corpus corpus) {
        if (corpus != null) {
            return subsetDefault(parameters, corpus);
        }
        return fromProvider(parameters, corpus, new UserDefaultProvider(parameters));
    }

    public static Result subsetDefault(BdfParameters parameters, Subset subset) {
        BdfServer bdfServer = parameters.getBdfServer();
        PermissionSummary permissionSummary = parameters.getPermissionSummary();
        if (!permissionSummary.hasAccess(subset)) {
            return EMPTY_RESULT;
        }
        SubsetTable subsetTable = BdfTableExportUtils.toDefaultSubsetTable(bdfServer, subset, parameters.getBdfUser().getPrefs().getDefaultFicheTableParameters(), permissionSummary);
        return CellConversionInit.subsetTable(bdfServer, parameters.getDefaultExtractionContext(), subsetTable);
    }

    private static Result fromProvider(BdfParameters parameters, @Nullable Corpus corpus, SubsetTableProvider subsetTableProvider) {
        BdfServer bdfServer = parameters.getBdfServer();
        PermissionSummary permissionSummary = parameters.getPermissionSummary();
        if (corpus != null) {
            if (!permissionSummary.hasAccess(corpus)) {
                return EMPTY_RESULT;
            }
            SubsetTable subsetTable = subsetTableProvider.getSubsetTable(corpus);
            return CellConversionInit.subsetTable(bdfServer, parameters.getDefaultExtractionContext(), subsetTable);
        } else {
            TableExportBuilder tableExportBuilder = TableExportBuilder.init();
            for (Corpus crp : bdfServer.getFichotheque().getCorpusList()) {
                if (!permissionSummary.hasAccess(crp)) {
                    continue;
                }
                tableExportBuilder.addSubsetTable(subsetTableProvider.getSubsetTable(crp));
            }
            return CellConversionInit.tableExport(bdfServer, parameters.getDefaultExtractionContext(), tableExportBuilder.toTableExport());
        }
    }


    private static abstract class SubsetTableProvider {

        protected abstract SubsetTable getSubsetTable(Corpus corpus);

    }


    private static class UserDefaultProvider extends SubsetTableProvider {

        final BdfParameters parameters;

        UserDefaultProvider(BdfParameters parameters) {
            this.parameters = parameters;
        }

        @Override
        protected SubsetTable getSubsetTable(Corpus corpus) {
            return BdfTableExportUtils.toDefaultSubsetTable(parameters.getBdfServer(), corpus, parameters.getBdfUser().getPrefs().getDefaultFicheTableParameters(), parameters.getPermissionSummary());
        }

    }


    private static abstract class FilterProvider extends SubsetTableProvider {

        final BdfParameters parameters;
        final String patternMode;

        FilterProvider(BdfParameters parameters, String patternMode) {
            this.parameters = parameters;
            this.patternMode = patternMode;
        }

        abstract List<UiComponent> getUiComponent(Corpus corpus);


        @Override
        protected SubsetTable getSubsetTable(Corpus corpus) {
            List<UiComponent> uiComponentList = getUiComponent(corpus);
            TableDef tableDef = DefaultTableDefFactory.fromComponentList(parameters.getBdfServer(), corpus, uiComponentList, patternMode, parameters.getPermissionSummary());
            return SubsetTableBuilder.init(corpus)
                    .populate(tableDef, parameters.getBdfServer().getTableExportContext())
                    .toSubsetTable();
        }

    }


    private static class TableFilterProvider extends FilterProvider {

        private final SubsetKey contextCorpusKey;

        TableFilterProvider(BdfParameters parameters, String mode, SubsetKey contextCorpusKey) {
            super(parameters, mode);
            this.contextCorpusKey = contextCorpusKey;
        }

        @Override
        List<UiComponent> getUiComponent(Corpus corpus) {
            return UiUtils.filterFicheTableUiComponents(parameters.getBdfServer().getUiManager().getMainUiComponents(corpus), contextCorpusKey);

        }

    }


    private static class SelectionFilterProvider extends FilterProvider {

        private final List<String> componentNameList;

        SelectionFilterProvider(BdfParameters parameters, String mode, List<String> componentNameList) {
            super(parameters, mode);
            this.componentNameList = componentNameList;
        }

        @Override
        List<UiComponent> getUiComponent(Corpus corpus) {
            List<UiComponent> result = new ArrayList<UiComponent>();
            UiComponents uiComponents = parameters.getBdfServer().getUiManager().getMainUiComponents(corpus);
            for (String componentName : componentNameList) {
                UiComponent uiComponent = uiComponents.getUiComponent(componentName);
                if (uiComponent != null) {
                    result.add(uiComponent);
                }
            }
            return result;
        }

    }


    public static class Result {

        private final SumCellConverter sumCellConverter;
        private final Map<SubsetKey, SubsetTable> tableMap;

        private Result(SumCellConverter sumCellConverter, Map<SubsetKey, SubsetTable> tableMap) {
            this.sumCellConverter = sumCellConverter;
            this.tableMap = tableMap;
        }

        public SumCellConverter getCellConverter() {
            return sumCellConverter;
        }

        public SubsetTable getSubsetTable(Subset subset) {
            return tableMap.get(subset.getSubsetKey());
        }

    }

}
