/* FichothequeLib_API - Copyright (c) 2012-2024 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.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.RandomAccess;
import java.util.Set;
import java.util.function.Predicate;
import net.fichotheque.Fichotheque;
import net.fichotheque.Subset;
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.include.ExtendedIncludeKey;
import net.fichotheque.include.IncludeKey;
import net.fichotheque.junction.Junction;
import net.fichotheque.selection.DocumentCondition;
import net.fichotheque.selection.DocumentQuery;
import net.fichotheque.selection.FicheCondition;
import net.fichotheque.selection.FicheQuery;
import net.fichotheque.selection.FicheSelector;
import net.fichotheque.selection.FieldContentCondition;
import net.fichotheque.selection.IllustrationCondition;
import net.fichotheque.selection.IllustrationQuery;
import net.fichotheque.selection.JunctionCondition;
import net.fichotheque.selection.MotcleCondition;
import net.fichotheque.selection.MotcleQuery;
import net.fichotheque.selection.MotcleSelector;
import net.fichotheque.selection.PeriodCondition;
import net.fichotheque.selection.RangeCondition;
import net.fichotheque.selection.RedacteurQuery;
import net.fichotheque.selection.StatusCondition;
import net.fichotheque.selection.SubsetCondition;
import net.fichotheque.selection.UserCondition;
import net.fichotheque.sphere.Sphere;
import net.fichotheque.thesaurus.Motcle;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.utils.selection.RangeConditionBuilder;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.conditions.TextCondition;
import net.mapeadores.util.primitives.Range;
import net.mapeadores.util.primitives.Ranges;
import net.mapeadores.util.text.StringUtils;


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

    public final static List<FicheQuery> EMPTY_FICHEQUERYLIST = Collections.emptyList();
    public final static List<MotcleQuery> EMPTY_MOTCLEQUERYLIST = Collections.emptyList();
    public final static List<IllustrationQuery> EMPTY_ILLUSTRATIONQUERYLIST = Collections.emptyList();
    public final static List<DocumentQuery> EMPTY_DOCUMENTQUERYLIST = Collections.emptyList();
    public final static List<MotcleCondition.Entry> EMPTY_MOTCLECONDITIONENTRYLIST = Collections.emptyList();
    public final static List<FicheCondition.Entry> EMPTY_FICHECONDITIONENTRYLIST = Collections.emptyList();
    public final static List<DocumentCondition.Entry> EMPTY_DOCUMENTCONDITIONENTRYLIST = Collections.emptyList();
    public final static List<IllustrationCondition.Entry> EMPTY_ILLUSTRATIONCONDITIONENTRYLIST = Collections.emptyList();
    public final static List<UserCondition.Entry> EMPTY_USERCONDITIONENTRYLIST = Collections.emptyList();
    public final static DocumentQuery EMPTY_DOCUMENTQUERY = new EmptyDocumentQuery();
    public final static MotcleQuery EMPTY_MOTCLEQUERY = new EmptyMotcleQuery();
    public final static IllustrationQuery EMPTY_ILLUSTRATIONQUERY = new EmptyIllustrationQuery();
    public final static FicheQuery EMPTY_FICHEQUERY = new EmptyFicheQuery();
    public final static RedacteurQuery EMPTY_REDACTEURQUERY = new EmptyRedacteurQuery();
    public final static MotcleSelector NONE_MOTCLESELECTOR = new NoneMotcleSelector();
    public final static MotcleCondition.Entry EMPTY_MOTCLECONDITIONENTRY = new MotcleConditionEntry(EMPTY_MOTCLEQUERY, null, false);
    public final static FicheCondition.Entry EMPTY_FICHECONDITIONENTRY = new FicheConditionEntry(EMPTY_FICHEQUERY, null, false);
    public final static DocumentCondition.Entry EMPTY_DOCUMENTCONDITIONENTRY = new DocumentConditionEntry(EMPTY_DOCUMENTQUERY, null);
    public final static IllustrationCondition.Entry EMPTY_ILLUSTRATIONCONDITIONENTRY = new IllustrationConditionEntry(EMPTY_ILLUSTRATIONQUERY, null);
    public final static SubsetCondition EMPTY_SUBSETCONDITION = new SetSubsetCondition(Collections.emptySet(), false, false);
    public final static UserCondition NONE_USERCONDITION = new InternalUserCondition(UserCondition.FILTER_NONE);
    public final static UserCondition ANY_USERCONDITION = new InternalUserCondition(UserCondition.FILTER_ANY);

    private SelectionUtils() {
    }

    public static Corpus[] toCorpusArray(Fichotheque fichotheque, FicheQuery ficheQuery, Predicate<Subset> subsetAccessPredicate) {
        if (ficheQuery == null) {
            return FichothequeUtils.toCorpusArray(fichotheque, subsetAccessPredicate);
        }
        SubsetCondition corpusCondition = ficheQuery.getCorpusCondition();
        List<Corpus> list = new ArrayList<Corpus>();
        for (Corpus corpus : fichotheque.getCorpusList()) {
            if (!subsetAccessPredicate.test(corpus)) {
                continue;
            }
            if (!corpusCondition.accept(corpus.getSubsetKey())) {
                continue;
            }
            list.add(corpus);
        }
        return list.toArray(new Corpus[list.size()]);
    }

    public static Thesaurus[] toThesaurusArray(Fichotheque fichotheque, MotcleQuery motcleQuery, Predicate<Subset> subsetAccessPredicate) {
        if (motcleQuery == null) {
            return FichothequeUtils.toThesaurusArray(fichotheque, subsetAccessPredicate);
        }
        return toThesaurusArray(fichotheque, motcleQuery.getThesaurusCondition(), subsetAccessPredicate);
    }

    public static Thesaurus[] toThesaurusArray(Fichotheque fichotheque, SubsetCondition thesaurusCondition, Predicate<Subset> subsetAccessPredicate) {
        List<Thesaurus> list = new ArrayList<Thesaurus>();
        for (Thesaurus thesaurus : fichotheque.getThesaurusList()) {
            if (!subsetAccessPredicate.test(thesaurus)) {
                continue;
            }
            if (!thesaurusCondition.accept(thesaurus.getSubsetKey())) {
                continue;
            }
            list.add(thesaurus);
        }
        return list.toArray(new Thesaurus[list.size()]);
    }

    public static Album[] toAlbumArray(Fichotheque fichotheque, IllustrationQuery illustrationQuery, Predicate<Subset> subsetAccessPredicate) {
        List<SubsetKey> subsetKeyList = illustrationQuery.getAlbumKeyList();
        if (subsetKeyList.isEmpty()) {
            return FichothequeUtils.toAlbumArray(fichotheque, subsetAccessPredicate);
        }
        List<Album> list = new ArrayList<Album>();
        for (SubsetKey subsetKey : subsetKeyList) {
            Album album = (Album) fichotheque.getSubset(subsetKey);
            if ((album != null) && (subsetAccessPredicate.test(album))) {
                list.add(album);
            }
        }
        return list.toArray(new Album[list.size()]);
    }

    public static Addenda[] toAddendaArray(Fichotheque fichotheque, DocumentQuery documentQuery, Predicate<Subset> subsetAccessPredicate) {
        List<SubsetKey> subsetKeyList = documentQuery.getAddendaKeyList();
        if (subsetKeyList.isEmpty()) {
            return FichothequeUtils.toAddendaArray(fichotheque, subsetAccessPredicate);
        }
        List<Addenda> list = new ArrayList<Addenda>();
        for (SubsetKey subsetKey : subsetKeyList) {
            Addenda addenda = (Addenda) fichotheque.getSubset(subsetKey);
            if ((addenda != null) && (subsetAccessPredicate.test(addenda))) {
                list.add(addenda);
            }
        }
        return list.toArray(new Addenda[list.size()]);
    }

    public static Sphere[] toSphereArray(Fichotheque fichotheque, RedacteurQuery redacteurQuery, Predicate<Subset> subsetAccessPredicate) {
        List<SubsetKey> subsetKeyList = redacteurQuery.getSphereKeyList();
        if (subsetKeyList.isEmpty()) {
            return FichothequeUtils.toSphereArray(fichotheque, subsetAccessPredicate);
        }
        List<Sphere> list = new ArrayList<Sphere>();
        for (SubsetKey subsetKey : subsetKeyList) {
            Sphere sphere = (Sphere) fichotheque.getSubset(subsetKey);
            if ((sphere != null) && (subsetAccessPredicate.test(sphere))) {
                list.add(sphere);
            }
        }
        return list.toArray(new Sphere[list.size()]);
    }


    public static List<FicheQuery> wrap(FicheQuery[] array) {
        return new FicheQueryList(array);
    }

    public static SubsetCondition toSingletonSubsetCondition(SubsetKey subsetKey) {
        return new SetSubsetCondition(Collections.singleton(subsetKey), false, false);
    }

    public static SubsetCondition toSubsetCondition(Set<SubsetKey> subsetKeySet) {
        return new SetSubsetCondition(Collections.unmodifiableSet(subsetKeySet), false, false);
    }

    public static SubsetCondition toSubsetCondition(Set<SubsetKey> subsetKeySet, boolean withCurrent, boolean exclude) {
        return new SetSubsetCondition(Collections.unmodifiableSet(subsetKeySet), withCurrent, exclude);
    }

    public static StatusCondition toStatusCondition(Collection<String> status) {
        int size = status.size();
        if (size == 0) {
            return null;
        } else if (size == 1) {
            return new SetStatusCondition(Collections.singleton(status.iterator().next()));
        } else {
            return new SetStatusCondition(Collections.unmodifiableSet(new LinkedHashSet<String>(status)));
        }
    }

    public static JunctionCondition toJunctionCondition(IncludeKey includeKey) {
        return toJunctionCondition(includeKey.getMode(), includeKey.getWeightFilter());
    }

    public static JunctionCondition toJunctionCondition(ExtendedIncludeKey includeKey) {
        return toJunctionCondition(includeKey.getMode(), includeKey.getWeightFilter());
    }

    public static JunctionCondition toJunctionCondition(String mode, int weight) {
        List<String> singleton = Collections.singletonList(mode);
        RangeCondition weightRangeCondition = null;
        if (weight > 0) {
            Ranges ranges = new Ranges();
            ranges.addRange(new Range(weight, weight));
            weightRangeCondition = RangeConditionBuilder.init(ranges).toRangeCondition();
        }
        return new InternalJunctionCondition(singleton, weightRangeCondition);
    }

    public static Predicate<Motcle> toPredicate(MotcleSelector motcleSelector) {
        Set<Motcle> motcleSet = new HashSet<Motcle>();
        for (Thesaurus thesaurus : motcleSelector.getThesaurusList()) {
            for (Motcle motcle : thesaurus.getMotcleList()) {
                if (motcleSelector.test(motcle)) {
                    motcleSet.add(motcle);
                }
            }
        }
        return EligibilityUtils.toMotclePredicate(motcleSet);
    }

    public static Predicate<FicheMeta> toPredicate(FicheSelector ficheSelector) {
        Set<FicheMeta> ficheMetaSet = new HashSet<FicheMeta>();
        for (Corpus corpus : ficheSelector.getCorpusList()) {
            for (FicheMeta ficheMeta : corpus.getFicheMetaList()) {
                if (ficheSelector.test(ficheMeta)) {
                    ficheMetaSet.add(ficheMeta);
                }
            }
        }
        return EligibilityUtils.toFichePredicate(ficheMetaSet);
    }

    public static FicheQuery derive(FicheQuery ficheQuery, SubsetCondition corpusCondition) {
        return new DerivationFicheQuery(ficheQuery, corpusCondition);
    }

    public static MotcleQuery derive(MotcleQuery motcleQuery, SubsetCondition thesaurusCondition) {
        return new DerivationMotcleQuery(motcleQuery, thesaurusCondition);
    }


    private static class FicheQueryList extends AbstractList<FicheQuery> implements RandomAccess {

        private final FicheQuery[] array;

        private FicheQueryList(FicheQuery[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public FicheQuery get(int index) {
            return array[index];
        }

    }

    public static List<MotcleQuery> wrap(MotcleQuery[] array) {
        return new MotcleQueryList(array);
    }

    public static MotcleQuery deriveWithoutContentCondition(MotcleQuery motcleQuery) {
        return new WithoutContentConditionMotcleQuery(motcleQuery);
    }


    private static class MotcleQueryList extends AbstractList<MotcleQuery> implements RandomAccess {

        private final MotcleQuery[] array;

        private MotcleQueryList(MotcleQuery[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public MotcleQuery get(int index) {
            return array[index];
        }

    }


    public static List<IllustrationQuery> wrap(IllustrationQuery[] array) {
        return new IllustrationQueryList(array);
    }


    private static class IllustrationQueryList extends AbstractList<IllustrationQuery> implements RandomAccess {

        private final IllustrationQuery[] array;

        private IllustrationQueryList(IllustrationQuery[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public IllustrationQuery get(int index) {
            return array[index];
        }

    }

    public static List<DocumentQuery> wrap(DocumentQuery[] array) {
        return new DocumentQueryList(array);
    }


    private static class DocumentQueryList extends AbstractList<DocumentQuery> implements RandomAccess {

        private final DocumentQuery[] array;

        private DocumentQueryList(DocumentQuery[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public DocumentQuery get(int index) {
            return array[index];
        }

    }


    private static class EmptyDocumentQuery implements DocumentQuery {

        private EmptyDocumentQuery() {
        }

        @Override
        public List<SubsetKey> getAddendaKeyList() {
            return FichothequeUtils.EMPTY_SUBSETKEYLIST;
        }

        @Override
        public TextCondition getNameCondition() {
            return null;
        }

    }


    private static class EmptyMotcleQuery implements MotcleQuery {

        private EmptyMotcleQuery() {
        }

        @Override
        public SubsetCondition getThesaurusCondition() {
            return EMPTY_SUBSETCONDITION;
        }

        @Override
        public RangeCondition getIdRangeCondition() {
            return null;
        }

        @Override
        public RangeCondition getLevelRangeCondition() {
            return null;
        }

        @Override
        public ContentCondition getContentCondition() {
            return null;
        }

        @Override
        public FicheCondition getFicheCondition() {
            return null;
        }

        @Override
        public StatusCondition getStatusCondition() {
            return null;
        }

    }


    private static class EmptyIllustrationQuery implements IllustrationQuery {

        private EmptyIllustrationQuery() {
        }

        @Override
        public List<SubsetKey> getAlbumKeyList() {
            return FichothequeUtils.EMPTY_SUBSETKEYLIST;
        }

        @Override
        public List<String> getAlbumDimNameList() {
            return StringUtils.EMPTY_STRINGLIST;
        }

    }


    private static class SetSubsetCondition implements SubsetCondition {

        private final Set<SubsetKey> set;
        private final boolean withCurrent;
        private final boolean exclude;

        private SetSubsetCondition(Set<SubsetKey> set, boolean withCurrent, boolean exclude) {
            this.set = set;
            this.withCurrent = withCurrent;
            this.exclude = exclude;
        }

        @Override
        public boolean isExclude() {
            return exclude;
        }

        @Override
        public boolean isWithCurrent() {
            return withCurrent;
        }

        @Override
        public Set<SubsetKey> getSubsetKeySet() {
            return set;
        }

    }


    private static class SetStatusCondition implements StatusCondition {

        private final Set<String> statusSet;

        private SetStatusCondition(Set<String> statusSet) {
            this.statusSet = statusSet;
        }

        public Set<String> getStatusSet() {
            return statusSet;
        }

    }


    private static class EmptyFicheQuery implements FicheQuery {

        private EmptyFicheQuery() {
        }

        @Override
        public SubsetCondition getCorpusCondition() {
            return EMPTY_SUBSETCONDITION;
        }

        @Override
        public MotcleCondition getMotcleCondition() {
            return null;
        }

        @Override
        public FicheCondition getFicheCondition() {
            return null;
        }

        @Override
        public FieldContentCondition getFieldContentCondition() {
            return null;
        }

        @Override
        public UserCondition getUserCondition() {
            return null;
        }

        @Override
        public PeriodCondition getPeriodCondition() {
            return null;
        }

        @Override
        public RangeCondition getIdRangeCondition() {
            return null;
        }

        @Override
        public String getDiscardFilter() {
            return FicheQuery.DISCARDFILTER_ALL;
        }

        @Override
        public boolean isWithGeoloc() {
            return false;
        }

    }


    private static class EmptyRedacteurQuery implements RedacteurQuery {

        private EmptyRedacteurQuery() {
        }

        @Override
        public List<SubsetKey> getSphereKeyList() {
            return FichothequeUtils.EMPTY_SUBSETKEYLIST;
        }

        @Override
        public TextCondition getNomcompletCondition() {
            return null;
        }

    }


    private static class NoneMotcleSelector implements MotcleSelector {

        private NoneMotcleSelector() {

        }

        @Override
        public boolean test(Motcle motcle) {
            return false;
        }

        @Override
        public List<Thesaurus> getThesaurusList() {
            return FichothequeUtils.EMPTY_THESAURUSLIST;
        }

        @Override
        public Junction isSelected(Motcle motcle, Junction junction) {
            return null;
        }

    }


    private static class WithoutContentConditionMotcleQuery implements MotcleQuery {

        private final MotcleQuery motcleQuery;

        private WithoutContentConditionMotcleQuery(MotcleQuery motcleQuery) {
            this.motcleQuery = motcleQuery;
        }

        @Override
        public SubsetCondition getThesaurusCondition() {
            return motcleQuery.getThesaurusCondition();
        }

        @Override
        public RangeCondition getIdRangeCondition() {
            return motcleQuery.getIdRangeCondition();
        }

        @Override
        public RangeCondition getLevelRangeCondition() {
            return motcleQuery.getLevelRangeCondition();
        }

        @Override
        public ContentCondition getContentCondition() {
            return null;
        }

        @Override
        public FicheCondition getFicheCondition() {
            return motcleQuery.getFicheCondition();
        }

        @Override
        public StatusCondition getStatusCondition() {
            return motcleQuery.getStatusCondition();
        }

    }


    private static class InternalJunctionCondition implements JunctionCondition {

        private final List<String> tieModeList;
        private final RangeCondition weightCondition;

        private InternalJunctionCondition(List<String> tieModeList, RangeCondition weightCondition) {
            this.tieModeList = tieModeList;
            this.weightCondition = weightCondition;
        }

        @Override
        public List<String> getTieModeList() {
            return tieModeList;
        }

        @Override
        public RangeCondition getWeightRangeCondition() {
            return weightCondition;
        }

    }


    private static class InternalUserCondition implements UserCondition {

        private final String filter;

        private InternalUserCondition(String filter) {
            this.filter = filter;
        }

        @Override
        public String getFilter() {
            return filter;
        }

        @Override
        public List<Entry> getEntryList() {
            return EMPTY_USERCONDITIONENTRYLIST;
        }

    }


    private static class DerivationFicheQuery implements FicheQuery {

        private final FicheQuery ficheQuery;
        private final SubsetCondition corpusCondition;

        private DerivationFicheQuery(FicheQuery ficheQuery, SubsetCondition corpusCondition) {
            this.ficheQuery = ficheQuery;
            this.corpusCondition = corpusCondition;
        }

        @Override
        public SubsetCondition getCorpusCondition() {
            return corpusCondition;
        }

        @Override
        public MotcleCondition getMotcleCondition() {
            return ficheQuery.getMotcleCondition();
        }

        @Override
        public FicheCondition getFicheCondition() {
            return ficheQuery.getFicheCondition();
        }

        @Override
        public FieldContentCondition getFieldContentCondition() {
            return ficheQuery.getFieldContentCondition();
        }

        @Override
        public UserCondition getUserCondition() {
            return ficheQuery.getUserCondition();
        }

        @Override
        public RangeCondition getIdRangeCondition() {
            return ficheQuery.getIdRangeCondition();
        }

        @Override
        public PeriodCondition getPeriodCondition() {
            return ficheQuery.getPeriodCondition();
        }

        @Override
        public String getDiscardFilter() {
            return ficheQuery.getDiscardFilter();
        }

        @Override
        public boolean isWithGeoloc() {
            return ficheQuery.isWithGeoloc();
        }


    }


    private static class DerivationMotcleQuery implements MotcleQuery {

        private final MotcleQuery motcleQuery;
        private final SubsetCondition thesaurusCondition;

        private DerivationMotcleQuery(MotcleQuery motcleQuery, SubsetCondition thesaurusCondition) {
            this.motcleQuery = motcleQuery;
            this.thesaurusCondition = thesaurusCondition;
        }

        @Override
        public SubsetCondition getThesaurusCondition() {
            return thesaurusCondition;
        }

        @Override
        public RangeCondition getIdRangeCondition() {
            return motcleQuery.getIdRangeCondition();
        }

        @Override
        public RangeCondition getLevelRangeCondition() {
            return motcleQuery.getLevelRangeCondition();
        }

        @Override
        public MotcleQuery.ContentCondition getContentCondition() {
            return motcleQuery.getContentCondition();
        }

        @Override
        public FicheCondition getFicheCondition() {
            return motcleQuery.getFicheCondition();
        }

        @Override
        public StatusCondition getStatusCondition() {
            return motcleQuery.getStatusCondition();
        }

    }

    public static MotcleCondition.Entry toMotcleConditionEntry(MotcleQuery motcleQuery) {
        return toMotcleConditionEntry(motcleQuery, null, false);
    }

    public static MotcleCondition.Entry toMotcleConditionEntry(MotcleQuery motcleQuery, @Nullable JunctionCondition junctionCondition, boolean includeSatellites) {
        return new MotcleConditionEntry(motcleQuery, junctionCondition, includeSatellites);
    }


    private static class MotcleConditionEntry implements MotcleCondition.Entry {

        private final MotcleQuery motcleQuery;
        private final JunctionCondition junctionCondition;
        private final boolean includeSatellites;


        private MotcleConditionEntry(MotcleQuery motcleQuery, JunctionCondition junctionCondition, boolean includeSatellites) {
            this.motcleQuery = motcleQuery;
            this.junctionCondition = junctionCondition;
            this.includeSatellites = includeSatellites;
        }

        @Override
        public MotcleQuery getMotcleQuery() {
            return motcleQuery;
        }

        @Override
        public JunctionCondition getJunctionCondition() {
            return junctionCondition;
        }

        @Override
        public boolean isWithMaster() {
            return includeSatellites;
        }

    }

    public static FicheCondition.Entry toFicheConditionEntry(FicheQuery ficheQuery) {
        return toFicheConditionEntry(ficheQuery, null, false);
    }

    public static FicheCondition.Entry toFicheConditionEntry(FicheQuery ficheQuery, @Nullable JunctionCondition junctionCondition, boolean includeSatellites) {
        return new FicheConditionEntry(ficheQuery, junctionCondition, includeSatellites);
    }


    private static class FicheConditionEntry implements FicheCondition.Entry {

        private final FicheQuery ficheQuery;
        private final JunctionCondition junctionCondition;
        private final boolean includeSatellites;


        private FicheConditionEntry(FicheQuery ficheQuery, JunctionCondition junctionCondition, boolean includeSatellites) {
            this.ficheQuery = ficheQuery;
            this.junctionCondition = junctionCondition;
            this.includeSatellites = includeSatellites;
        }

        @Override
        public FicheQuery getFicheQuery() {
            return ficheQuery;
        }

        @Override
        public JunctionCondition getJunctionCondition() {
            return junctionCondition;
        }

        @Override
        public boolean includeSatellites() {
            return includeSatellites;
        }

    }

    public static IllustrationCondition.Entry toIllustrationConditionEntry(IllustrationQuery illustrationQuery) {
        return toIllustrationConditionEntry(illustrationQuery, null);
    }

    public static IllustrationCondition.Entry toIllustrationConditionEntry(IllustrationQuery illustrationQuery, @Nullable JunctionCondition junctionCondition) {
        return new IllustrationConditionEntry(illustrationQuery, junctionCondition);
    }


    private static class IllustrationConditionEntry implements IllustrationCondition.Entry {

        private final IllustrationQuery illustrationQuery;
        private final JunctionCondition junctionCondition;


        private IllustrationConditionEntry(IllustrationQuery illustrationQuery, JunctionCondition junctionCondition) {
            this.illustrationQuery = illustrationQuery;
            this.junctionCondition = junctionCondition;
        }

        @Override
        public IllustrationQuery getIllustrationQuery() {
            return illustrationQuery;
        }

        @Override
        public JunctionCondition getJunctionCondition() {
            return junctionCondition;
        }

    }

    public static DocumentCondition.Entry toDocumentConditionEntry(DocumentQuery documentQuery) {
        return toDocumentConditionEntry(documentQuery, null);
    }

    public static DocumentCondition.Entry toDocumentConditionEntry(DocumentQuery documentQuery, @Nullable JunctionCondition junctionCondition) {
        return new DocumentConditionEntry(documentQuery, junctionCondition);
    }


    private static class DocumentConditionEntry implements DocumentCondition.Entry {

        private final DocumentQuery documentQuery;
        private final JunctionCondition junctionCondition;


        private DocumentConditionEntry(DocumentQuery documentQuery, JunctionCondition junctionCondition) {
            this.documentQuery = documentQuery;
            this.junctionCondition = junctionCondition;
        }

        @Override
        public DocumentQuery getDocumentQuery() {
            return documentQuery;
        }

        @Override
        public JunctionCondition getJunctionCondition() {
            return junctionCondition;
        }

    }

}
