/* FichothequeLib_Tools - Copyright (c) 2023-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.extraction;

import java.util.ArrayList;
import java.util.List;
import net.fichotheque.SubsetKey;
import net.fichotheque.extraction.ExtractionConstants;
import net.fichotheque.extraction.ExtractionContext;
import net.fichotheque.extraction.def.CorpusExtractDef;
import net.fichotheque.extraction.def.ExtractionDef;
import net.fichotheque.extraction.def.FicheFilter;
import net.fichotheque.extraction.def.MotcleFilter;
import net.fichotheque.extraction.def.TagNameInfo;
import net.fichotheque.extraction.def.ThesaurusExtractDef;
import net.fichotheque.extraction.filterunit.CorpusExtractFilterUnit;
import net.fichotheque.extraction.filterunit.FilterUnit;
import net.fichotheque.extraction.filterunit.ThesaurusExtractFilterUnit;
import net.fichotheque.permission.PermissionSummary;
import net.fichotheque.selection.FicheCondition;
import net.fichotheque.selection.FicheQuery;
import net.fichotheque.selection.MotcleCondition;
import net.fichotheque.selection.MotcleQuery;
import net.fichotheque.selection.SubsetCondition;
import net.fichotheque.tools.extraction.builders.FicheFilterBuilder;
import net.fichotheque.utils.ExtractionUtils;
import net.fichotheque.utils.FilterUnitCatalog;


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

    private final PermissionSummary permissionSummary;

    private ExtractionDefFilterEngine(PermissionSummary permissionSummary) {
        this.permissionSummary = permissionSummary;
    }

    private CorpusExtractDef filter(CorpusExtractDef corpusExtractDef) {
        if (corpusExtractDef == null) {
            return null;
        }
        List<FicheCondition.Entry> filteredConditionEntryList;
        List<FicheCondition.Entry> originalConditionEntryList = corpusExtractDef.getConditionEntryList();
        if (originalConditionEntryList.isEmpty()) {
            filteredConditionEntryList = originalConditionEntryList;
        } else {
            filteredConditionEntryList = new ArrayList<FicheCondition.Entry>();
            for (FicheCondition.Entry entry : originalConditionEntryList) {
                if (isValid(entry)) {
                    filteredConditionEntryList.add(entry);
                }
            }
            if (filteredConditionEntryList.isEmpty()) {
                return null;
            }
        }
        FicheFilter filteredFicheFilter;
        FicheFilter originalFicheFilter = corpusExtractDef.getFicheFilter();
        if (originalFicheFilter.getFilterUnitList().isEmpty()) {
            filteredFicheFilter = originalFicheFilter;
        } else {
            FicheFilterBuilder builder = FicheFilterBuilder.init()
                    .setTagNameInfo(originalFicheFilter.getTagNameInfo());
            for (FilterUnit originalFilterUnit : originalFicheFilter.getFilterUnitList()) {
                FilterUnit filteredFilterUnit = filter(originalFilterUnit);
                if (filteredFilterUnit != null) {
                    builder.add(filteredFilterUnit);
                }
            }
            filteredFicheFilter = builder.toFicheFilter();
        }
        return new FilteredCorpusExtractDef(corpusExtractDef, filteredFicheFilter, filteredConditionEntryList);
    }

    private ThesaurusExtractDef filter(ThesaurusExtractDef thesaurusExtractDef) {
        if (thesaurusExtractDef == null) {
            return null;
        }
        List<MotcleCondition.Entry> filteredConditionEntryList;
        List<MotcleCondition.Entry> originalConditionEntryList = thesaurusExtractDef.getConditionEntryList();
        if (originalConditionEntryList.isEmpty()) {
            filteredConditionEntryList = originalConditionEntryList;
        } else {
            filteredConditionEntryList = new ArrayList<MotcleCondition.Entry>();
            for (MotcleCondition.Entry entry : originalConditionEntryList) {
                if (isValid(entry)) {
                    filteredConditionEntryList.add(entry);
                }
            }
            if (filteredConditionEntryList.isEmpty()) {
                return null;
            }
        }
        MotcleFilter filteredMotcleFilter;
        MotcleFilter originalMotcleFilter = thesaurusExtractDef.getMotcleFilter();
        List<FilterUnit> originalFilterUnitList = originalMotcleFilter.getFilterUnitList();
        if (originalFilterUnitList.isEmpty()) {
            filteredMotcleFilter = originalMotcleFilter;
        } else {
            List<FilterUnit> filteredList = new ArrayList<FilterUnit>();
            for (FilterUnit originalFilterUnit : originalFilterUnitList) {
                FilterUnit filteredFilterUnit = filter(originalFilterUnit);
                if (filteredFilterUnit != null) {
                    filteredList.add(filteredFilterUnit);
                }
            }
            filteredMotcleFilter = new FilteredMotcleFilter(originalMotcleFilter, ExtractionUtils.toImmutableList(filteredList));
        }
        return new FilteredThesaurusExtractDef(thesaurusExtractDef, filteredMotcleFilter, filteredConditionEntryList);
    }

    private boolean isValid(FicheCondition.Entry entry) {
        if (entry.includeSatellites()) {
            return true;
        }
        FicheQuery ficheQuery = entry.getFicheQuery();
        return isValid(ficheQuery.getCorpusCondition());
    }

    private boolean isValid(MotcleCondition.Entry entry) {
        if (entry.isWithMaster()) {
            return true;
        }
        MotcleQuery motcleQuery = entry.getMotcleQuery();
        return isValid(motcleQuery.getThesaurusCondition());
    }

    private boolean isValid(SubsetCondition subsetCondition) {
        if ((subsetCondition.isExclude()) || (subsetCondition.isAll())) {
            return true;
        }
        for (SubsetKey subsetKey : subsetCondition.getSubsetKeySet()) {
            if (permissionSummary.hasAccess(subsetKey)) {
                return true;
            }
        }
        return false;
    }

    private FilterUnit filter(FilterUnit originalFilterUnit) {
        List<String> roles = originalFilterUnit.getParameters().getParameter(ExtractionConstants.ROLES_PARAM);
        if (!roles.isEmpty()) {
            boolean check = false;
            for (String role : roles) {
                if (permissionSummary.hasRole(role)) {
                    check = true;
                    break;
                }
            }
            if (!check) {
                return null;
            }
        }
        if (originalFilterUnit instanceof CorpusExtractFilterUnit) {
            CorpusExtractDef filteredCorpusExtractDef = filter(((CorpusExtractFilterUnit) originalFilterUnit).getCorpusExtractDef());
            if (filteredCorpusExtractDef != null) {
                return FilterUnitCatalog.corpusExtract(filteredCorpusExtractDef, originalFilterUnit.getParameters());
            } else {
                return null;
            }
        } else if (originalFilterUnit instanceof ThesaurusExtractFilterUnit) {
            ThesaurusExtractDef filteredThesaurusExtractDef = filter(((ThesaurusExtractFilterUnit) originalFilterUnit).getThesaurusExtractDef());
            if (filteredThesaurusExtractDef != null) {
                return FilterUnitCatalog.thesaurusExtract(filteredThesaurusExtractDef, originalFilterUnit.getParameters());
            } else {
                return null;
            }
        } else {
            return originalFilterUnit;
        }
    }

    public static ExtractionDef run(ExtractionDef extractionDef, ExtractionContext extractionContext) {
        PermissionSummary permissionSummary = extractionContext.getPermissionSummary();
        if (permissionSummary.isFichothequeAdmin()) {
            return extractionDef;
        }
        ExtractionDefFilterEngine engine = new ExtractionDefFilterEngine(permissionSummary);
        CorpusExtractDef filteredDynamicCorpusExtractDef = engine.filter(extractionDef.getDynamicCorpusExtractDef());
        ThesaurusExtractDef filteredDynamicThesaurusExtractDef = engine.filter(extractionDef.getDynamicThesaurusExtractDef());
        List<CorpusExtractDef> filteredStaticCorpusList;
        List<CorpusExtractDef> originalStaticCorpusList = extractionDef.getStaticCorpusExtractDefList();
        if (originalStaticCorpusList.isEmpty()) {
            filteredStaticCorpusList = originalStaticCorpusList;
        } else {
            filteredStaticCorpusList = new ArrayList<CorpusExtractDef>();
            for (CorpusExtractDef originalCorpusExtractDef : originalStaticCorpusList) {
                CorpusExtractDef filteredCorpusExtractDef = engine.filter(originalCorpusExtractDef);
                if (filteredCorpusExtractDef != null) {
                    filteredStaticCorpusList.add(filteredCorpusExtractDef);
                }
            }
        }
        List<ThesaurusExtractDef> filteredStaticThesaurusList;
        List<ThesaurusExtractDef> originalStaticThesaurusList = extractionDef.getStaticThesaurusExtractDefList();
        if (originalStaticThesaurusList.isEmpty()) {
            filteredStaticThesaurusList = originalStaticThesaurusList;
        } else {
            filteredStaticThesaurusList = new ArrayList<ThesaurusExtractDef>();
            for (ThesaurusExtractDef originalThesaurusExtractDef : originalStaticThesaurusList) {
                ThesaurusExtractDef filteredThesaurusExtractDef = engine.filter(originalThesaurusExtractDef);
                if (filteredThesaurusExtractDef != null) {
                    filteredStaticThesaurusList.add(filteredThesaurusExtractDef);
                }
            }
        }
        return new FilteredExtractionDef(extractionDef, filteredDynamicCorpusExtractDef, filteredDynamicThesaurusExtractDef, filteredStaticCorpusList, filteredStaticThesaurusList);
    }


    private static class FilteredExtractionDef implements ExtractionDef {

        private final ExtractionDef originalExtractionDef;
        private final CorpusExtractDef dynamicCorpusExtractDef;
        private final ThesaurusExtractDef dynamicThesaurusExtractDef;
        private final List<CorpusExtractDef> staticCorpusList;
        private final List<ThesaurusExtractDef> staticThesaurusList;

        private FilteredExtractionDef(ExtractionDef originalExtractionDef, CorpusExtractDef dynamicCorpusExtractDef, ThesaurusExtractDef dynamicThesaurusExtractDef, List<CorpusExtractDef> staticCorpusList, List<ThesaurusExtractDef> staticThesaurusList) {
            this.originalExtractionDef = originalExtractionDef;
            this.dynamicCorpusExtractDef = dynamicCorpusExtractDef;
            this.dynamicThesaurusExtractDef = dynamicThesaurusExtractDef;
            this.staticCorpusList = staticCorpusList;
            this.staticThesaurusList = staticThesaurusList;
        }

        @Override
        public TagNameInfo getTagNameInfo() {
            return originalExtractionDef.getTagNameInfo();
        }

        @Override
        public CorpusExtractDef getDynamicCorpusExtractDef() {
            return dynamicCorpusExtractDef;
        }

        @Override
        public ThesaurusExtractDef getDynamicThesaurusExtractDef() {
            return dynamicThesaurusExtractDef;
        }

        @Override
        public TagNameInfo getStaticTagNameInfo() {
            return originalExtractionDef.getStaticTagNameInfo();
        }

        @Override
        public List<CorpusExtractDef> getStaticCorpusExtractDefList() {
            return staticCorpusList;
        }

        @Override
        public List<ThesaurusExtractDef> getStaticThesaurusExtractDefList() {
            return staticThesaurusList;
        }

    }


    private static class FilteredCorpusExtractDef implements CorpusExtractDef {

        private final CorpusExtractDef originalCorpusExtractDef;
        private final FicheFilter filteredFicheFilter;
        private final List<FicheCondition.Entry> filteredConditionEntryList;

        private FilteredCorpusExtractDef(CorpusExtractDef originalCorpusExtractDef, FicheFilter filteredFicheFilter, List<FicheCondition.Entry> filteredConditionEntryList) {
            this.originalCorpusExtractDef = originalCorpusExtractDef;
            this.filteredFicheFilter = filteredFicheFilter;
            this.filteredConditionEntryList = filteredConditionEntryList;
        }

        @Override
        public String getName() {
            return originalCorpusExtractDef.getName();
        }

        @Override
        public TagNameInfo getTagNameInfo() {
            return originalCorpusExtractDef.getTagNameInfo();
        }

        @Override
        public FicheFilter getFicheFilter() {
            return filteredFicheFilter;
        }

        @Override
        public List<FicheCondition.Entry> getConditionEntryList() {
            return filteredConditionEntryList;
        }

        @Override
        public boolean hasClauseObjects() {
            return originalCorpusExtractDef.hasClauseObjects();
        }

        @Override
        public Object getClauseObject(String name) {
            return originalCorpusExtractDef.getClauseObject(name);
        }

        @Override
        public boolean hasBooleanParameters() {
            return originalCorpusExtractDef.hasBooleanParameters();
        }

        @Override
        public boolean getBooleanParameter(String name) {
            return originalCorpusExtractDef.getBooleanParameter(name);
        }

    }


    private static class FilteredThesaurusExtractDef implements ThesaurusExtractDef {

        private final ThesaurusExtractDef originalThesaurusExtractDef;
        private final MotcleFilter filteredMotcleFilter;
        private final List<MotcleCondition.Entry> filteredConditionEntryList;

        private FilteredThesaurusExtractDef(ThesaurusExtractDef originalThesaurusExtractDef, MotcleFilter filteredMotcleFilter, List<MotcleCondition.Entry> filteredConditionEntryList) {
            this.originalThesaurusExtractDef = originalThesaurusExtractDef;
            this.filteredMotcleFilter = filteredMotcleFilter;
            this.filteredConditionEntryList = filteredConditionEntryList;
        }

        @Override
        public String getName() {
            return originalThesaurusExtractDef.getName();
        }

        @Override
        public TagNameInfo getTagNameInfo() {
            return originalThesaurusExtractDef.getTagNameInfo();
        }

        @Override
        public MotcleFilter getMotcleFilter() {
            return filteredMotcleFilter;
        }

        @Override
        public List<MotcleCondition.Entry> getConditionEntryList() {
            return filteredConditionEntryList;
        }

        @Override
        public boolean hasBooleanParameters() {
            return originalThesaurusExtractDef.hasBooleanParameters();
        }

        @Override
        public boolean getBooleanParameter(String name) {
            return originalThesaurusExtractDef.getBooleanParameter(name);
        }

    }


    private static class FilteredMotcleFilter implements MotcleFilter {

        private final MotcleFilter originalMotcleFilter;
        private final List<FilterUnit> filterUnitList;

        private FilteredMotcleFilter(MotcleFilter originalMotcleFilter, List<FilterUnit> filterUnitList) {
            this.originalMotcleFilter = originalMotcleFilter;
            this.filterUnitList = filterUnitList;
        }

        @Override
        public short getType() {
            return originalMotcleFilter.getType();
        }

        @Override
        public TagNameInfo getTagNameInfo() {
            return originalMotcleFilter.getTagNameInfo();
        }

        @Override
        public boolean isNoneFiltering() {
            return originalMotcleFilter.isNoneFiltering();
        }

        @Override
        public boolean withIcon() {
            return originalMotcleFilter.withIcon();
        }

        @Override
        public boolean withLevel() {
            return originalMotcleFilter.withLevel();
        }

        @Override
        public boolean withLabels() {
            return originalMotcleFilter.withLabels();
        }

        @Override
        public boolean withFicheStylePhrase() {
            return originalMotcleFilter.withFicheStylePhrase();
        }

        @Override
        public MotcleFilter getChildrenFilter() {
            return originalMotcleFilter.getChildrenFilter();
        }

        @Override
        public MotcleFilter getParentFilter() {
            return originalMotcleFilter.getParentFilter();
        }

        @Override
        public MotcleFilter getNextFilter() {
            return originalMotcleFilter.getNextFilter();
        }

        @Override
        public MotcleFilter getPreviousFilter() {
            return originalMotcleFilter.getPreviousFilter();
        }

        @Override
        public List<FilterUnit> getFilterUnitList() {
            return filterUnitList;
        }

    }

}
