/* BdfServer_Multi - Copyright (c) 2018-2022 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.multi.commands.fichotheque;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import net.fichotheque.Fichotheque;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.eligibility.SubsetEligibility;
import net.fichotheque.utils.EligibilityUtils;
import net.fichotheque.junction.Junctions;


/**
 *
 * @author Vincent Calame
 */
public class CreationSubsetEligibility implements SubsetEligibility {

    public final static String ITEMS_NONE = "none";
    public final static String ITEMS_ALL = "all";
    public final static String ITEMS_SELECTION = "selection";
    private final Map<SubsetKey, SubsetInfo> subsetMap = new HashMap<SubsetKey, SubsetInfo>();
    private final List<Corpus> corpusList = new ArrayList<Corpus>();
    private final Fichotheque fichotheque;


    public CreationSubsetEligibility(Fichotheque fichotheque) {
        this.fichotheque = fichotheque;
    }

    public void add(SubsetKey subsetKey, String itemsPolicy) {
        subsetMap.put(subsetKey, new SubsetInfo(itemsPolicy));
        if (subsetKey.isCorpusSubset()) {
            Corpus corpus = (Corpus) fichotheque.getSubset(subsetKey);
            if (corpus != null) {
                corpusList.add(corpus);
            }
        }
    }


    @Override
    public boolean accept(Subset subset) {
        if (subset instanceof Corpus) {
            Subset masterSubset = ((Corpus) subset).getMasterSubset();
            if (masterSubset != null) {
                if (!accept(masterSubset)) {
                    return false;
                }
            }
        }
        return subsetMap.containsKey(subset.getSubsetKey());
    }

    @Override
    public boolean accept(SubsetKey subsetKey) {
        if (subsetKey.isCorpusSubset()) {
            Corpus corpus = (Corpus) fichotheque.getSubset(subsetKey);
            if (corpus != null) {
                return accept(corpus);
            } else {
                return false;
            }
        }
        return subsetMap.containsKey(subsetKey);
    }

    @Override
    public Predicate<SubsetItem> getPredicate(Subset subset) {
        if (subset instanceof Corpus) {
            Subset masterSubset = ((Corpus) subset).getMasterSubset();
            if (masterSubset != null) {
                if (getPolicy(masterSubset).equals(ITEMS_NONE)) {
                    return EligibilityUtils.NONE_SUBSETITEM_PREDICATE;
                }
            }
        }
        String policy = getPolicy(subset);
        if (policy.equals(ITEMS_NONE)) {
            return EligibilityUtils.NONE_SUBSETITEM_PREDICATE;
        }
        if (policy.equals(ITEMS_SELECTION)) {
            if (corpusList.isEmpty()) {
                return EligibilityUtils.NONE_SUBSETITEM_PREDICATE;
            } else {
                SubsetInfo subsetInfo = subsetMap.get(subset.getSubsetKey());
                InternalPredicate internalPredicate = subsetInfo.getInternalPredicate();
                if (internalPredicate == null) {
                    internalPredicate = initPredicate(subset);
                    subsetInfo.setInternalPredicate(internalPredicate);
                }
                return internalPredicate;
            }
        } else {
            return EligibilityUtils.ALL_SUBSETITEM_PREDICATE;
        }
    }

    private String getPolicy(Subset subset) {
        SubsetInfo subsetInfo = subsetMap.get(subset.getSubsetKey());
        if (subsetInfo == null) {
            return ITEMS_NONE;
        }
        return subsetInfo.getPolicy();
    }

    private InternalPredicate initPredicate(Subset subset) {
        InternalPredicate predicate = new InternalPredicate();
        for (SubsetItem subsetItem : subset.getSubsetItemList()) {
            for (Corpus corpus : corpusList) {
                Junctions junctions = fichotheque.getJunctions(subsetItem, corpus);
                if (!junctions.isEmpty()) {
                    predicate.add(subsetItem.getId());
                    break;
                }
            }
        }
        return predicate;
    }


    private static class SubsetInfo {

        private final String itemsPolicy;
        private InternalPredicate predicate;

        private SubsetInfo(String itemsPolicy) {
            this.itemsPolicy = itemsPolicy;
        }

        public String getPolicy() {
            return itemsPolicy;
        }

        public InternalPredicate getInternalPredicate() {
            return predicate;
        }

        private void setInternalPredicate(InternalPredicate predicate) {
            this.predicate = predicate;
        }

    }


    private static class InternalPredicate implements Predicate<SubsetItem> {

        private final Set<Integer> idSet = new HashSet<Integer>();

        private InternalPredicate() {
        }

        @Override
        public boolean test(SubsetItem subsetItem) {
            return idSet.contains(subsetItem.getId());
        }

        private void add(int id) {
            idSet.add(id);
        }

    }

}
