/* FichothequeLib_Tools - Copyright (c) 2010-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 net.fichotheque.tools.eligibility;

import java.util.HashMap;
import java.util.HashSet;
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.addenda.Addenda;
import net.fichotheque.album.Album;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.Fiches;
import net.fichotheque.eligibility.SubsetEligibility;
import net.fichotheque.utils.EligibilityUtils;
import net.fichotheque.utils.FichothequeUtils;
import net.fichotheque.junction.Junctions;


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

    private final Map<SubsetKey, InternalPredicate> subsetKeyMap = new HashMap<SubsetKey, InternalPredicate>();

    private SelectionSubsetEligibility() {
    }

    public static SubsetEligibility build(Fichotheque fichotheque, Fiches fiches) {
        SelectionSubsetEligibility result = new SelectionSubsetEligibility();
        Addenda[] addendaArray = FichothequeUtils.toAddendaArray(fichotheque);
        int addendaCount = addendaArray.length;
        InternalPredicate[] addendaEligibilityArray = new InternalPredicate[addendaCount];
        for (int i = 0; i < addendaCount; i++) {
            addendaEligibilityArray[i] = new InternalPredicate(addendaArray[i].getSubsetKey());
        }
        Album[] albumArray = FichothequeUtils.toAlbumArray(fichotheque);
        int albumCount = albumArray.length;
        InternalPredicate[] albumEligibilityArray = new InternalPredicate[albumCount];
        for (int i = 0; i < albumCount; i++) {
            albumEligibilityArray[i] = new InternalPredicate(albumArray[i].getSubsetKey());
        }
        for (Fiches.Entry entry : fiches.getEntryList()) {
            Corpus corpus = entry.getCorpus();
            InternalPredicate ficheEligibility = new InternalPredicate(corpus.getSubsetKey());
            for (FicheMeta ficheMeta : entry.getFicheMetaList()) {
                ficheEligibility.add(ficheMeta.getId());
                for (int k = 0; k < addendaCount; k++) {
                    Junctions junctions = fichotheque.getJunctions(ficheMeta, addendaArray[k]);
                    if (!junctions.isEmpty()) {
                        InternalPredicate subsetItemEligibility = addendaEligibilityArray[k];
                        for (Junctions.Entry junctionEntry : junctions.getEntryList()) {
                            subsetItemEligibility.add(junctionEntry.getSubsetItem().getId());
                        }
                    }
                }
                for (int k = 0; k < albumCount; k++) {
                    Junctions junctions = fichotheque.getJunctions(ficheMeta, albumArray[k]);
                    if (!junctions.isEmpty()) {
                        InternalPredicate subsetItemEligibility = albumEligibilityArray[k];
                        for (Junctions.Entry junctionEntry : junctions.getEntryList()) {
                            subsetItemEligibility.add(junctionEntry.getSubsetItem().getId());
                        }
                    }
                }
            }
            result.addEligibility(ficheEligibility);
        }
        for (int i = 0; i < addendaCount; i++) {
            result.addEligibility(addendaEligibilityArray[i]);
        }
        for (int i = 0; i < albumCount; i++) {
            result.addEligibility(albumEligibilityArray[i]);
        }
        return result;
    }

    private void addEligibility(InternalPredicate subsetItemEligibility) {
        if (subsetItemEligibility.size() > 0) {
            subsetKeyMap.put(subsetItemEligibility.subsetKey, subsetItemEligibility);
        }
    }

    @Override
    public boolean accept(SubsetKey subsetKey) {
        switch (subsetKey.getCategory()) {
            case SubsetKey.CATEGORY_SPHERE:
            case SubsetKey.CATEGORY_THESAURUS:
                return true;
            default:
                return subsetKeyMap.containsKey(subsetKey);
        }
    }

    @Override
    public Predicate<SubsetItem> getPredicate(Subset subset) {
        SubsetKey subsetKey = subset.getSubsetKey();
        switch (subsetKey.getCategory()) {
            case SubsetKey.CATEGORY_SPHERE:
            case SubsetKey.CATEGORY_THESAURUS:
                return EligibilityUtils.ALL_SUBSETITEM_PREDICATE;
            default:
                InternalPredicate predicate = subsetKeyMap.get(subsetKey);
                if (predicate == null) {
                    return EligibilityUtils.NONE_SUBSETITEM_PREDICATE;
                } else {
                    return predicate;
                }
        }
    }


    private static class InternalPredicate implements Predicate<SubsetItem> {

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

        private InternalPredicate(SubsetKey subsetKey) {
            this.subsetKey = subsetKey;
        }

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

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

        private int size() {
            return idSet.size();
        }

    }

}
