/* FichothequeLib_Tools - Copyright (c) 2017-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.corpus;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.utils.Comparators;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.primitives.FuzzyDate;


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

    private final static Comparator COMPARATOR = new FicheInfoComparator();
    private final SortedMap<String, Group> groupMap = new TreeMap<String, Group>();

    FicheDistribution() {

    }

    void addFiche(String name, FicheMeta ficheMeta, Object groupingObject) {
        Group group = groupMap.get(name);
        if (group == null) {
            group = new Group(name);
            groupMap.put(name, group);
        }
        group.add(ficheMeta, groupingObject);
    }

    public List<Group> toGroupList() {
        return new ArrayList<Group>(groupMap.values());
    }


    public static class Group {

        private final String name;
        private final SortedMap<Integer, Year> yearMap = new TreeMap<Integer, Year>();
        private final SortedMap<Lang, ByLang> langMap = new TreeMap<Lang, ByLang>();
        private final Year nullYear = new Year(null);

        private Group(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public List<Year> toYearList() {
            List<Year> list = new ArrayList<Year>();
            list.addAll(yearMap.values());
            Collections.reverse(list);
            if (!nullYear.isEmpty()) {
                list.add(nullYear);
            }
            return list;
        }

        public List<ByLang> getByLangList() {
            List<ByLang> list = new ArrayList<ByLang>();
            list.addAll(langMap.values());
            return list;
        }

        private void add(FicheMeta ficheMeta, Object groupingObject) {
            if (groupingObject == null) {
                nullYear.addFicheMeta(ficheMeta, null);
            } else if (groupingObject instanceof FuzzyDate) {
                FuzzyDate fuzzyDate = (FuzzyDate) groupingObject;
                Integer yearNumber = fuzzyDate.getYear();
                Year year = yearMap.get(yearNumber);
                if (year == null) {
                    year = new Year(yearNumber);
                    yearMap.put(yearNumber, year);
                }
                year.addFicheMeta(ficheMeta, fuzzyDate);
            } else if (groupingObject instanceof Lang) {
                Lang lang = (Lang) groupingObject;
                ByLang byLang = langMap.get(lang);
                if (byLang == null) {
                    byLang = new ByLang(lang);
                    langMap.put(lang, byLang);
                }
                byLang.addFicheMeta(ficheMeta);
            }
        }

    }


    public static class Year {

        private final Integer yearInteger;
        private final Map<FicheMeta, FicheInfo> ficheInfoMap = new HashMap<FicheMeta, FicheInfo>();

        private Year(Integer yearInteger) {
            this.yearInteger = yearInteger;
        }

        private boolean isEmpty() {
            return ficheInfoMap.isEmpty();
        }

        public Integer getYearInteger() {
            return yearInteger;
        }

        public List<FicheInfo> toFicheInfoList() {
            List<FicheInfo> infoList = new ArrayList<FicheInfo>(ficheInfoMap.values());
            Collections.sort(infoList, COMPARATOR);
            return infoList;
        }

        private void addFicheMeta(FicheMeta ficheMeta, FuzzyDate date) {
            ficheInfoMap.put(ficheMeta, new FicheInfo(ficheMeta, date));
        }

    }


    public static class ByLang {

        private final Lang lang;
        private final Map<FicheMeta, FicheInfo> ficheInfoMap = new HashMap<FicheMeta, FicheInfo>();

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

        public Lang getLang() {
            return lang;
        }

        public List<FicheInfo> toFicheInfoList() {
            List<FicheInfo> infoList = new ArrayList<FicheInfo>(ficheInfoMap.values());
            Collator collator = Collator.getInstance(lang.toLocale());
            collator.setStrength(Collator.PRIMARY);
            Collections.sort(infoList, new TitleComparator(Comparators.title(collator, false)));
            return infoList;
        }

        private void addFicheMeta(FicheMeta ficheMeta) {
            ficheInfoMap.put(ficheMeta, new FicheInfo(ficheMeta, ficheMeta.getCreationDate()));
        }

    }


    public static class FicheInfo {

        private final FicheMeta ficheMeta;
        private final FuzzyDate fuzzyDate;

        private FicheInfo(FicheMeta ficheMeta, FuzzyDate fuzzyDate) {
            this.ficheMeta = ficheMeta;
            this.fuzzyDate = fuzzyDate;
        }

        public FicheMeta getFicheMeta() {
            return ficheMeta;
        }

        public FuzzyDate getFuzzyDate() {
            return fuzzyDate;
        }

    }

    private static int compareFicheMeta(FicheMeta fm1, FicheMeta fm2) {
        int comp = fm1.getSubsetKey().compareTo(fm2.getSubsetKey());
        if (comp != 0) {
            return comp;
        }
        int id1 = fm1.getId();
        int id2 = fm2.getId();
        if (id1 < id2) {
            return 1;
        } else if (id1 > id2) {
            return -1;
        } else {
            return 0;
        }
    }


    private static class TitleComparator implements Comparator<FicheInfo> {

        private final Comparator<FicheMeta> ficheMetaComparator;

        private TitleComparator(Comparator<FicheMeta> ficheMetaComparator) {
            this.ficheMetaComparator = ficheMetaComparator;
        }

        @Override
        public int compare(FicheInfo fi1, FicheInfo fi2) {
            int comp = ficheMetaComparator.compare(fi1.getFicheMeta(), fi2.getFicheMeta());
            if (comp != 0) {
                return comp;
            }
            return compareFicheMeta(fi1.getFicheMeta(), fi2.getFicheMeta());
        }

    }


    private static class FicheInfoComparator implements Comparator<FicheInfo> {

        private FicheInfoComparator() {

        }

        @Override
        public int compare(FicheInfo fi1, FicheInfo fi2) {
            FuzzyDate date1 = fi1.getFuzzyDate();
            FuzzyDate date2 = fi2.getFuzzyDate();
            if (date1 == null) {
                if (date2 == null) {
                    return compareFicheMeta(fi1.getFicheMeta(), fi2.getFicheMeta());
                } else {
                    return 1;
                }
            } else if (date2 == null) {
                return -1;
            }
            int comp = date1.compareTo(date2);
            if (comp != 0) {
                return -comp;
            }
            return compareFicheMeta(fi1.getFicheMeta(), fi2.getFicheMeta());
        }


    }


}
