/* BdfServer - Copyright (c) 2020-2021 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.tools.synthesis;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.managers.PermissionManager;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.SubsetKey;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.sphere.Sphere;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.text.Label;
import net.mapeadores.util.text.Labels;


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

    private final SubsetKey sphereKey;
    private final SortedMap<String, Sphere> sphereMap = new TreeMap<String, Sphere>();
    private final SortedMap<String, RedacteurSynthesis> redacteurMap = new TreeMap<String, RedacteurSynthesis>();
    private final LabelOccurrenceManager labelOccurrenceManager = new LabelOccurrenceManager();

    public SphereSynthesis(SubsetKey sphereKey) {
        this.sphereKey = sphereKey;
    }

    public String getSphereName() {
        return sphereKey.getSubsetName();
    }

    public SubsetKey getSphereKey() {
        return sphereKey;
    }

    public boolean isInFichotheque(String fichothequeName) {
        return sphereMap.containsKey(fichothequeName);
    }

    public void addSphere(BdfServer bdfServer, Sphere sphere) {
        String fichothequeName = bdfServer.getName();
        PermissionManager permissionManager = bdfServer.getPermissionManager();
        sphereMap.put(fichothequeName, sphere);
        for (Redacteur redacteur : sphere.getRedacteurList()) {
            String login = redacteur.getLogin();
            RedacteurSynthesis synthesis = redacteurMap.get(login);
            if (synthesis == null) {
                synthesis = new RedacteurSynthesis(login);
                redacteurMap.put(login, synthesis);
            }
            boolean admin = permissionManager.isAdmin(redacteur);
            synthesis.addRedacteur(redacteur, fichothequeName, admin);
        }
        labelOccurrenceManager.checkSphere(sphere, fichothequeName);
    }

    public int getFichothequeSize() {
        return sphereMap.size();
    }

    public Sphere getSphere(String fichothequeName) {
        return sphereMap.get(fichothequeName);
    }

    public String[] getFichothequeNameArray() {
        return sphereMap.keySet().toArray(new String[sphereMap.size()]);
    }

    public RedacteurSynthesis[] getRedacteurSynthesisArray() {
        return redacteurMap.values().toArray(new RedacteurSynthesis[redacteurMap.size()]);
    }

    public RedacteurSynthesis getRedacteurSynthesis(String login) {
        return redacteurMap.get(login);
    }

    public Labels getLabels() {
        return labelOccurrenceManager;
    }

    public SpecificInfo getSpecificInfo(String fichothequeName) {
        return labelOccurrenceManager.getSpecificInfo(fichothequeName);
    }


    private static class LabelOccurrenceManager extends AbstractList<Label> implements Labels {

        private final Map<Lang, LabelOccurrenceMap> byLangMap = new HashMap<Lang, LabelOccurrenceMap>();
        private final List<LabelOccurrenceMap> list = new ArrayList<LabelOccurrenceMap>();

        private LabelOccurrenceManager() {

        }

        private void checkSphere(Sphere sphere, String fichothequeName) {
            for (Label label : sphere.getSphereMetadata().getTitleLabels()) {
                Lang lang = label.getLang();
                LabelOccurrenceMap occurrenceMap = byLangMap.get(lang);
                if (occurrenceMap == null) {
                    occurrenceMap = new LabelOccurrenceMap(lang);
                    byLangMap.put(lang, occurrenceMap);
                    list.add(occurrenceMap);
                }
                occurrenceMap.add(fichothequeName, label.getLabelString());
            }
        }

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

        @Override
        public Label get(int index) {
            return list.get(index);
        }

        @Override
        public Label getLabel(Lang lang) {
            return byLangMap.get(lang);
        }

        private SpecificInfo getSpecificInfo(String fichothequeName) {
            List<Label> labelList = new ArrayList<Label>();
            for (LabelOccurrenceMap map : list) {
                if (!map.isInMax(fichothequeName)) {
                    LabelOccurrence labelOccurrence = map.getLabelOccurrence(fichothequeName);
                    if (labelOccurrence != null) {
                        labelList.add(labelOccurrence);
                    }
                }
            }
            if (!labelList.isEmpty()) {
                return new SpecificInfo(labelList);
            } else {
                return null;
            }
        }

    }


    private static class LabelOccurrenceMap implements Label {

        private final Lang lang;
        private final Map<String, LabelOccurrence> map = new LinkedHashMap<String, LabelOccurrence>();
        private LabelOccurrence maxLabelOccurrence;

        private LabelOccurrenceMap(Lang lang) {
            this.lang = lang;
        }

        private void add(String fichothequeName, String labelString) {
            LabelOccurrence labelOccurrence = map.get(labelString);
            if (labelOccurrence == null) {
                labelOccurrence = new LabelOccurrence(lang, labelString);
                map.put(labelString, labelOccurrence);
            }
            labelOccurrence.add(fichothequeName);
            if (maxLabelOccurrence == null) {
                maxLabelOccurrence = labelOccurrence;
            }
            if (labelOccurrence.size() > maxLabelOccurrence.size()) {
                maxLabelOccurrence = labelOccurrence;
            }
        }

        @Override
        public Lang getLang() {
            return lang;
        }

        @Override
        public String getLabelString() {
            return maxLabelOccurrence.getLabelString();
        }

        private boolean isInMax(String fichothequeName) {
            return maxLabelOccurrence.contains(fichothequeName);
        }

        private LabelOccurrence getLabelOccurrence(String fichothequeName) {
            for (LabelOccurrence labelOccurrence : map.values()) {
                if (labelOccurrence.contains(fichothequeName)) {
                    return labelOccurrence;
                }
            }
            return null;
        }

    }


    private static class LabelOccurrence implements Label {

        private final Lang lang;
        private final String labelString;
        private final Set<String> fichothequeNameSet = new HashSet<String>();

        private LabelOccurrence(Lang lang, String labelString) {
            this.lang = lang;
            this.labelString = labelString;
        }

        @Override
        public Lang getLang() {
            return lang;
        }

        @Override
        public String getLabelString() {
            return labelString;
        }

        private void add(String fichothequeName) {
            fichothequeNameSet.add(fichothequeName);
        }

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

        private boolean contains(String fichothequeName) {
            return fichothequeNameSet.contains(fichothequeName);
        }

    }


    public static class SpecificInfo {

        private final List<Label> labelList;

        private SpecificInfo(List<Label> labelList) {
            this.labelList = labelList;
        }

        public List<Label> getLabelList() {
            return labelList;
        }

    }


}
