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

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.fiche.DateItem;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.croisement.Croisement;
import net.fichotheque.extraction.def.FicheFilter;
import net.fichotheque.extraction.def.GroupClause;
import net.fichotheque.extraction.run.FicheGroup;
import net.fichotheque.extraction.run.FicheItemGroupParams;
import net.fichotheque.utils.FicheUtils;
import net.mapeadores.util.primitives.FuzzyDate;


/**
 *
 * @author Vincent Calame
 */
public class AnneeEngine extends GroupEngine {

    private final GroupClause groupClause;
    private final GroupEngineProvider subGroupEngineProvider;
    private FieldKey fieldKey;
    private GroupBuilderWrapper emptyGroupBuilderWrapper;
    private final SortedMap<Integer, GroupBuilderWrapper> treeMap = new TreeMap<Integer, GroupBuilderWrapper>();
    private final InfoWrapperComparator comparator;

    AnneeEngine(GroupClause groupClause, GroupEngineProvider subGroupEngineProvider) {
        this.groupClause = groupClause;
        this.subGroupEngineProvider = subGroupEngineProvider;
        FicheItemGroupParams groupParams = (FicheItemGroupParams) groupClause.getGroupParams();
        if (groupParams != null) {
            fieldKey = groupParams.getFieldKey();
        }
        this.comparator = new InfoWrapperComparator((groupClause.isAscendingOrder()));
    }

    @Override
    public void add(FicheMeta ficheMeta, FicheFilter ficheFilter, Croisement croisement) {
        FuzzyDate date = null;
        if (fieldKey == null) {
            date = ficheMeta.getCreationDate();
        } else {
            Object obj = FicheUtils.getValue(ficheMeta, fieldKey);
            if (obj != null) {
                if (obj instanceof FicheItem) {
                    date = getValue((FicheItem) obj);
                } else if (obj instanceof FicheItems) {
                    date = getValue(((FicheItems) obj).get(0));
                }
            }
        }
        int annee = 0;
        if (date != null) {
            annee = date.getYear();
        }
        GroupBuilderWrapper groupBuilderWrapper = getGroupBuilderWrapper(annee);
        groupBuilderWrapper.add(ficheMeta, ficheFilter, croisement, date);
    }

    private FuzzyDate getValue(FicheItem ficheItem) {
        if (ficheItem instanceof DateItem) {
            return ((DateItem) ficheItem).getDate();
        } else {
            return null;
        }
    }

    @Override
    public FicheGroup[] toFicheGroupArray() {
        List<GroupBuilder> list = new ArrayList<GroupBuilder>();
        for (GroupBuilderWrapper wrapper : treeMap.values()) {
            wrapper.flush(comparator);
            list.add(wrapper.groupBuilder);
        }
        GroupBuilder emptyGroupBuilder = null;
        if (emptyGroupBuilderWrapper != null) {
            emptyGroupBuilderWrapper.flush(comparator);
            emptyGroupBuilder = emptyGroupBuilderWrapper.groupBuilder;
        }
        return toGroupArray(list, groupClause, emptyGroupBuilder);
    }

    private GroupBuilderWrapper getGroupBuilderWrapper(Integer year) {
        boolean withSubGroup = (subGroupEngineProvider != null);
        if (year == 0) {
            if (emptyGroupBuilderWrapper == null) {
                emptyGroupBuilderWrapper = new GroupBuilderWrapper(GroupBuilder.newInstance("_", groupClause.getTagNameInfo(), subGroupEngineProvider), withSubGroup);
            }
            return emptyGroupBuilderWrapper;
        }
        GroupBuilderWrapper groupBuilderWrapper = treeMap.get(year);
        if (groupBuilderWrapper == null) {
            groupBuilderWrapper = new GroupBuilderWrapper(GroupBuilder.newInstance(year.toString(), groupClause.getTagNameInfo(), subGroupEngineProvider), withSubGroup);
            treeMap.put(year, groupBuilderWrapper);
        }
        return groupBuilderWrapper;
    }


    private static class GroupBuilderWrapper {

        private GroupBuilder groupBuilder;
        private final boolean withSubGroup;
        private List<InfoWrapper> infoList;

        private GroupBuilderWrapper(GroupBuilder groupBuilder, boolean withSubGroup) {
            this.groupBuilder = groupBuilder;
            this.withSubGroup = withSubGroup;
            if (!withSubGroup) {
                infoList = new ArrayList<InfoWrapper>();
            }
        }

        private void add(FicheMeta ficheMeta, FicheFilter ficheFilter, Croisement croisement, FuzzyDate date) {
            if (withSubGroup) {
                groupBuilder.add(ficheMeta, ficheFilter, croisement);
            } else {
                infoList.add(new InfoWrapper(ficheMeta, ficheFilter, croisement, date));
            }
        }

        private void flush(InfoWrapperComparator infoWrapperComparator) {
            if (!withSubGroup) {
                Collections.sort(infoList, infoWrapperComparator);
                for (InfoWrapper infoWrapper : infoList) {
                    groupBuilder.add(infoWrapper.ficheMeta, infoWrapper.ficheFilter, infoWrapper.croisement);
                }
                infoList.clear();
            }
        }

    }


    private static class InfoWrapper {

        private FicheFilter ficheFilter;
        private FicheMeta ficheMeta;
        private Croisement croisement;
        private final FuzzyDate date;

        public InfoWrapper(FicheMeta ficheMeta, FicheFilter ficheFilter, Croisement croisement, FuzzyDate date) {
            this.ficheFilter = ficheFilter;
            this.ficheMeta = ficheMeta;
            this.croisement = croisement;
            this.date = date;
        }

    }


    private static class InfoWrapperComparator implements Comparator<InfoWrapper> {

        private final boolean asc;

        private InfoWrapperComparator(boolean asc) {
            this.asc = asc;
        }

        @Override
        public int compare(InfoWrapper feb1, InfoWrapper feb2) {
            int result = internalCompare(feb1, feb2);
            if (!asc) {
                return -result;
            }
            return result;
        }

        private int internalCompare(InfoWrapper iw1, InfoWrapper iw2) {
            int dateComp = FuzzyDate.compare(iw1.date, iw2.date);
            if (dateComp != 0) {
                return dateComp;
            }
            SubsetKey subsetKey1 = iw1.ficheMeta.getSubsetKey();
            SubsetKey subsetKey2 = iw2.ficheMeta.getSubsetKey();
            int comp = subsetKey1.compareTo(subsetKey2);
            if (comp != 0) {
                return comp;
            }
            int id1 = iw1.ficheMeta.getId();
            int id2 = iw2.ficheMeta.getId();
            if (id1 < id2) {
                return -1;
            }
            if (id1 > id2) {
                return 1;
            }
            return 0;
        }

    }

}
