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

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.fichotheque.SubsetKey;
import net.fichotheque.croisement.Croisement;
import net.fichotheque.selection.RedacteurQuery;
import net.fichotheque.selection.RedacteurSelector;
import net.fichotheque.selection.SelectionContext;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.sphere.Sphere;
import net.fichotheque.utils.FichothequeUtils;
import net.fichotheque.utils.SelectionUtils;
import net.mapeadores.util.annotation.Nullable;


/**
 *
 * @author Vincent Calame
 */
public class RedacteurSelectorBuilder {

    private final SelectionContext selectionContext;
    private final Map<SubsetKey, List<RedacteurSelectEngine>> listMap = new HashMap<SubsetKey, List<RedacteurSelectEngine>>();
    private final List<Sphere> sphereList = new ArrayList<Sphere>();
    private RedacteurSelectEngine firstEngine;
    private int engineCount = 0;


    public RedacteurSelectorBuilder(SelectionContext selectionContext) {
        this.selectionContext = selectionContext;
    }

    public RedacteurSelectorBuilder add(RedacteurQuery redacteurQuery) {
        Sphere[] sphereArray = SelectionUtils.toSphereArray(selectionContext.getFichotheque(), redacteurQuery, selectionContext.getSubsetAccessPredicate());
        RedacteurSelectEngine documentSelectEngine = new RedacteurSelectEngine(redacteurQuery, selectionContext);
        if (firstEngine == null) {
            firstEngine = documentSelectEngine;
        }
        engineCount++;
        for (Sphere sphere : sphereArray) {
            List<RedacteurSelectEngine> list = checkSphere(sphere);
            list.add(documentSelectEngine);
        }
        return this;
    }

    public RedacteurSelectorBuilder addAll(Collection<RedacteurQuery> collection) {
        for (RedacteurQuery query : collection) {
            add(query);
        }
        return this;
    }

    public RedacteurSelector toRedacteurSelector() {
        switch (engineCount) {
            case 0: {
                List<Sphere> finalSphereList = FichothequeUtils.wrap(FichothequeUtils.toSphereArray(selectionContext.getFichotheque(), selectionContext.getSubsetAccessPredicate()));
                return new UniqueRedacteurSelector(finalSphereList, null, new RedacteurSelectEngine(SelectionUtils.EMPTY_REDACTEURQUERY, selectionContext));
            }
            case 1: {
                List<Sphere> finalSphereList = FichothequeUtils.wrap(sphereList.toArray(new Sphere[sphereList.size()]));
                return new UniqueRedacteurSelector(finalSphereList, new HashSet<SubsetKey>(listMap.keySet()), firstEngine);
            }
            default: {
                List<Sphere> finalSphereList = FichothequeUtils.wrap(sphereList.toArray(new Sphere[sphereList.size()]));
                Map<SubsetKey, RedacteurSelectEngine[]> arrayMap = new HashMap<SubsetKey, RedacteurSelectEngine[]>();
                for (Map.Entry<SubsetKey, List<RedacteurSelectEngine>> entry : listMap.entrySet()) {
                    List<RedacteurSelectEngine> list = entry.getValue();
                    RedacteurSelectEngine[] array = list.toArray(new RedacteurSelectEngine[list.size()]);
                    arrayMap.put(entry.getKey(), array);
                }
                return new MultiRedacteurSelector(finalSphereList, arrayMap);
            }
        }
    }

    private List<RedacteurSelectEngine> checkSphere(Sphere sphere) {
        List<RedacteurSelectEngine> currentList = listMap.get(sphere.getSubsetKey());
        if (currentList == null) {
            currentList = new ArrayList<RedacteurSelectEngine>();
            sphereList.add(sphere);
            listMap.put(sphere.getSubsetKey(), currentList);
        }
        return currentList;
    }

    public static RedacteurSelectorBuilder init(SelectionContext selectionContext) {
        return new RedacteurSelectorBuilder(selectionContext);
    }


    private static class MultiRedacteurSelector implements RedacteurSelector {

        private final List<Sphere> sphereList;
        private final Map<SubsetKey, RedacteurSelectEngine[]> arrayMap;

        private MultiRedacteurSelector(List<Sphere> sphereList, Map<SubsetKey, RedacteurSelectEngine[]> arrayMap) {
            this.sphereList = sphereList;
            this.arrayMap = arrayMap;
        }

        @Override
        public List<Sphere> getSphereList() {
            return sphereList;
        }

        @Override
        public boolean test(Redacteur redacteur) {
            RedacteurSelectEngine[] array = arrayMap.get(redacteur.getSubsetKey());
            if (array == null) {
                return false;
            }
            for (RedacteurSelectEngine engine : array) {
                if (engine.isSelected(redacteur)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public Croisement isSelected(Redacteur redacteur, Croisement croisement) {
            return null;
        }

    }


    private static class UniqueRedacteurSelector implements RedacteurSelector {

        private final List<Sphere> sphereList;
        private final Set<SubsetKey> keySet;
        private final RedacteurSelectEngine redacteurSelectEngine;

        private UniqueRedacteurSelector(List<Sphere> sphereList, @Nullable Set<SubsetKey> keySet, RedacteurSelectEngine redacteurSelectEngine) {
            this.sphereList = sphereList;
            this.keySet = keySet;
            this.redacteurSelectEngine = redacteurSelectEngine;
        }

        @Override
        public List<Sphere> getSphereList() {
            return sphereList;
        }

        @Override
        public boolean test(Redacteur redacteur) {
            if ((keySet != null) && (!keySet.contains(redacteur.getSubsetKey()))) {
                return false;
            }
            return redacteurSelectEngine.isSelected(redacteur);
        }

        @Override
        public Croisement isSelected(Redacteur redacteur, Croisement croisement) {
            return null;
        }

    }

}
