/* FichothequeLib_API - Copyright (c) 2014-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.utils;

import java.util.Set;
import java.util.function.Predicate;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.eligibility.SubsetEligibility;
import net.fichotheque.thesaurus.Motcle;


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

    public final static Predicate<SubsetItem> ALL_SUBSETITEM_PREDICATE = (SubsetItem subsetItem) -> {
        return true;
    };
    public final static Predicate<SubsetItem> NONE_SUBSETITEM_PREDICATE = (SubsetItem subsetItem) -> {
        return false;
    };
    public final static Predicate<FicheMeta> ALL_FICHE_PREDICATE = (FicheMeta ficheMeta) -> {
        return true;
    };
    public final static Predicate<Motcle> ALL_MOTCLE_PREDICATE = (Motcle motcle) -> {
        return true;
    };
    public final static Predicate<Subset> ALL_SUBSET_PREDICATE = (Subset subset) -> {
        return true;
    };
    public final static SubsetEligibility ALL_SUBSET_ELIGIBILITY = new AllSubsetEligibility();
    public final static SubsetEligibility NONE_SUBSET_ELIGIBILITY = new NoneSubsetEligibility();

    private EligibilityUtils() {
    }

    public static Predicate<SubsetItem> toPredicate(Set<SubsetItem> subsetItemSet) {
        return new SetPredicate(subsetItemSet);
    }

    public static Predicate<FicheMeta> toFichePredicate(Set<FicheMeta> ficheMetaSet) {
        return new FicheSetPredicate(ficheMetaSet);
    }

    public static Predicate<FicheMeta> toFichePredicate(Predicate<SubsetItem> predicate) {
        return (FicheMeta ficheMeta) -> {
            return predicate.test(ficheMeta);
        };
    }

    public static Predicate<Motcle> toMotclePredicate(Set<Motcle> motcleSet) {
        return new MotcleSetPredicate(motcleSet);
    }

    public static Predicate<SubsetItem> merge(Predicate<FicheMeta> fichePredicate, Predicate<Motcle> motclePredicate) {
        return new MergePredicate(fichePredicate, motclePredicate);
    }

    public static SubsetEligibility exclude(Subset subset) {
        return new ExcludeSubsetEligibility(subset.getSubsetKey());
    }


    private static class AllSubsetEligibility implements SubsetEligibility {

        private AllSubsetEligibility() {
        }

        @Override
        public boolean accept(SubsetKey subsetKey) {
            return true;
        }

        @Override
        public Predicate<SubsetItem> getPredicate(Subset subset) {
            return ALL_SUBSETITEM_PREDICATE;
        }

    }


    private static class NoneSubsetEligibility implements SubsetEligibility {

        private NoneSubsetEligibility() {
        }

        @Override
        public boolean accept(SubsetKey subsetKey) {
            return false;
        }

        @Override
        public Predicate<SubsetItem> getPredicate(Subset subset) {
            return NONE_SUBSETITEM_PREDICATE;
        }

    }


    private static class ExcludeSubsetEligibility implements SubsetEligibility {

        private final SubsetKey excludeSubsetKey;

        private ExcludeSubsetEligibility(SubsetKey subsetKey) {
            this.excludeSubsetKey = subsetKey;
        }

        @Override
        public boolean accept(SubsetKey subsetKey) {
            return (!subsetKey.equals(excludeSubsetKey));
        }

        @Override
        public Predicate<SubsetItem> getPredicate(Subset subset) {
            if (subset.getSubsetKey().equals(excludeSubsetKey)) {
                return NONE_SUBSETITEM_PREDICATE;
            } else {
                return ALL_SUBSETITEM_PREDICATE;
            }
        }

    }


    private static class SetPredicate implements Predicate<SubsetItem> {

        private final Set<SubsetItem> subsetItemSet;

        private SetPredicate(Set<SubsetItem> subsetItemSet) {
            this.subsetItemSet = subsetItemSet;
        }

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

    }


    private static class FicheSetPredicate implements Predicate<FicheMeta> {

        private final Set<FicheMeta> ficheMetaSet;

        private FicheSetPredicate(Set<FicheMeta> ficheMetaSet) {
            this.ficheMetaSet = ficheMetaSet;
        }

        @Override
        public boolean test(FicheMeta ficheMeta) {
            return ficheMetaSet.contains(ficheMeta);
        }

    }


    private static class MotcleSetPredicate implements Predicate<Motcle> {

        private final Set<Motcle> motcleSet;

        private MotcleSetPredicate(Set<Motcle> motcleSet) {
            this.motcleSet = motcleSet;
        }

        @Override
        public boolean test(Motcle motcle) {
            return motcleSet.contains(motcle);
        }

    }


    private static class MergePredicate implements Predicate<SubsetItem> {

        private final Predicate<FicheMeta> fichePredicate;
        private final Predicate<Motcle> motclePredicate;

        private MergePredicate(Predicate<FicheMeta> fichePredicate, Predicate<Motcle> motclePredicate) {
            if (fichePredicate == null) {
                this.fichePredicate = EligibilityUtils.ALL_FICHE_PREDICATE;
            } else {
                this.fichePredicate = fichePredicate;
            }
            if (motclePredicate == null) {
                this.motclePredicate = EligibilityUtils.ALL_MOTCLE_PREDICATE;
            } else {
                this.motclePredicate = motclePredicate;
            }
        }

        @Override
        public boolean test(SubsetItem subsetItem) {
            if (subsetItem instanceof FicheMeta) {
                return fichePredicate.test((FicheMeta) subsetItem);
            }
            if (subsetItem instanceof Motcle) {
                return motclePredicate.test((Motcle) subsetItem);
            }
            return true;
        }

    }

}
