/* FichothequeLib_API - Copyright (c) 2015-2025 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.text.Collator;
import java.util.Comparator;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.SortConstants;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.croisement.Liaison;
import net.fichotheque.externalsource.ExternalItem;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.thesaurus.Motcle;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.exceptions.SwitchException;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.primitives.FuzzyDate;
import net.mapeadores.util.text.Label;


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

    public final static Comparator<Motcle> IDALPHA = new IdalphaComparator(false);
    public final static Comparator<Motcle> MOTCLEID_ASC = new MotcleIdComparator(false);
    public final static Comparator<Motcle> MOTCLEID_DESC = new MotcleIdComparator(true);
    public final static Comparator<FicheMeta> FICHEID_ASC = new FicheIdComparator(false);
    public final static Comparator<FicheMeta> FICHEID_DESC = new FicheIdComparator(true);
    public final static Comparator<FicheMeta> CREATIONDATE_ASC = new CreationDateComparator(false);
    public final static Comparator<FicheMeta> CREATIONDATE_DESC = new CreationDateComparator(true);
    public final static Comparator<FicheMeta> MODIFICATIONDATE_ASC = new ModificationDateComparator(false);
    public final static Comparator<FicheMeta> MODIFICATIONDATE_DESC = new ModificationDateComparator(true);
    public final static Comparator<Liaison> LIAISONID = new LiaisonIdComparator();
    public final static Comparator<FieldKey> FIELDKEY = new FieldKeyComparator();
    public final static Comparator<Subset> SIZE = new SizeComparator();
    public final static Comparator<Redacteur> REDACTEUR = new RedacteurComparator();
    public final static Comparator<ExternalItem> EXTERNALITEMID_ASC = new ExternalItemIdComparator(false);
    public final static Comparator<ExternalItem> EXTERNALITEMID_DESC = new ExternalItemIdComparator(true);

    private Comparators() {

    }

    public static Comparator<FicheMeta> title(Collator collator, boolean desc) {
        return new TitleComparator(collator, desc);
    }

    public static Comparator<Motcle> label(Lang lang) {
        Collator collator = Collator.getInstance(lang.toLocale());
        collator.setStrength(Collator.PRIMARY);
        return new LabelComparator(lang, collator);
    }

    public static Comparator<Motcle> label(Lang lang, Collator collator) {
        return new LabelComparator(lang, collator);
    }

    public static Comparator<Motcle> idalphaAndLabel(Lang lang) {
        return new IdalphaAndLabelComparator(lang);
    }

    public static boolean needCollator(String sortType) {
        switch (sortType) {
            case SortConstants.TITLE_ASC:
            case SortConstants.TITLE_DESC:
                return true;
            default:
                return false;
        }
    }

    @Nullable
    public static Comparator<FicheMeta> getComparator(String sortType, @Nullable Lang sortLang) {
        Collator collator = null;
        if (Comparators.needCollator(sortType)) {
            if (sortLang == null) {
                throw new IllegalArgumentException("lang is null");
            }
            collator = Collator.getInstance(sortLang.toLocale());
            collator.setStrength(Collator.PRIMARY);
        }
        return getComparator(sortType, collator);
    }

    @Nullable
    public static Comparator<FicheMeta> getComparator(String sortType, @Nullable Collator collator) {
        switch (sortType) {
            case SortConstants.TITLE_ASC:
                if (collator == null) {
                    throw new IllegalArgumentException("collator is null");
                }
                return Comparators.title(collator, false);
            case SortConstants.TITLE_DESC:
                if (collator == null) {
                    throw new IllegalArgumentException("collator is null");
                }
                return Comparators.title(collator, true);
            case SortConstants.CREATIONDATE_ASC:
                return Comparators.CREATIONDATE_ASC;
            case SortConstants.CREATIONDATE_DESC:
                return Comparators.CREATIONDATE_DESC;
            case SortConstants.MODIFICATIONDATE_ASC:
                return Comparators.MODIFICATIONDATE_ASC;
            case SortConstants.MODIFICATIONDATE_DESC:
                return Comparators.MODIFICATIONDATE_DESC;
            case SortConstants.ID_ASC:
                return Comparators.FICHEID_ASC;
            case SortConstants.ID_DESC:
                return Comparators.FICHEID_DESC;
            case SortConstants.NONE:
                return null;
            default:
                throw new SwitchException("sortType=" + sortType);
        }
    }

    private static int compareId(SubsetItem piece1, SubsetItem piece2) {
        int result = piece1.getSubsetKey().compareTo(piece2.getSubsetKey());
        if (result != 0) {
            return result;
        }
        int id1 = piece1.getId();
        int id2 = piece2.getId();
        if (id1 < id2) {
            return -1;
        }
        if (id1 > id2) {
            return 1;
        }
        return 0;
    }


    private static int getWeight(FieldKey fieldKey) {
        switch (fieldKey.getCategory()) {
            case FieldKey.PROP_CATEGORY:
                return 30;
            case FieldKey.INFO_CATEGORY:
                return 20;
            case FieldKey.SECTION_CATEGORY:
                return 10;
            case FieldKey.SPECIAL_CATEGORY:
                switch (fieldKey.getKeyString()) {
                    case FieldKey.SPECIAL_ID:
                        return 150;
                    case FieldKey.SPECIAL_TITLE:
                        return 140;
                    case FieldKey.SPECIAL_SUBTITLE:
                        return 130;
                    case FieldKey.SPECIAL_LANG:
                        return 120;
                    case FieldKey.SPECIAL_OWNERS:
                        return 110;
                    default:
                        throw new SwitchException("Unknown special field = " + fieldKey.getKeyString());
                }
            default:
                throw new SwitchException("Unknown category = " + fieldKey.getCategory());
        }
    }


    private static class FicheIdComparator implements Comparator<FicheMeta> {

        private final boolean desc;

        private FicheIdComparator(boolean desc) {
            this.desc = desc;
        }

        @Override
        public int compare(FicheMeta ficheMeta1, FicheMeta ficheMeta2) {
            int result = compareId(ficheMeta1, ficheMeta2);
            if (desc) {
                return -result;
            } else {
                return result;
            }
        }

    }


    private static class IdalphaComparator implements Comparator<Motcle> {

        private final boolean desc;

        private IdalphaComparator(boolean desc) {
            this.desc = desc;
        }

        @Override
        public int compare(Motcle motcle1, Motcle motcle2) {
            String idalpha1 = motcle1.getIdalpha();
            String idalpha2 = motcle2.getIdalpha();
            int result = idalpha1.compareTo(idalpha2);
            if (desc) {
                return -result;
            } else {
                return result;
            }
        }

    }


    private static class MotcleIdComparator implements Comparator<Motcle> {

        private final boolean desc;

        private MotcleIdComparator(boolean desc) {
            this.desc = desc;
        }

        @Override
        public int compare(Motcle motcle1, Motcle motcle2) {
            int result = compareId(motcle1, motcle2);
            if (desc) {
                return -result;
            } else {
                return result;
            }
        }

    }


    private static class CreationDateComparator implements Comparator<FicheMeta> {

        private final boolean desc;

        private CreationDateComparator(boolean desc) {
            this.desc = desc;
        }

        @Override
        public int compare(FicheMeta ficheMeta1, FicheMeta ficheMeta2) {
            int result = FuzzyDate.compare(ficheMeta1.getCreationDate(), ficheMeta2.getCreationDate());
            if (result == 0) {
                result = compareId(ficheMeta1, ficheMeta2);
            }
            if (desc) {
                return -result;
            } else {
                return result;
            }
        }

    }


    private static class ModificationDateComparator implements Comparator<FicheMeta> {

        private final boolean desc;

        private ModificationDateComparator(boolean desc) {
            this.desc = desc;
        }

        @Override
        public int compare(FicheMeta ficheMeta1, FicheMeta ficheMeta2) {
            int result = FuzzyDate.compare(ficheMeta1.getModificationDate(), ficheMeta2.getModificationDate());
            if (result == 0) {
                result = FuzzyDate.compare(ficheMeta1.getCreationDate(), ficheMeta2.getCreationDate());
                if (result == 0) {
                    result = compareId(ficheMeta1, ficheMeta2);
                }
            }
            if (desc) {
                return -result;
            } else {
                return result;
            }
        }

    }


    private static class TitleComparator implements Comparator<FicheMeta> {

        private final Collator collator;
        private final boolean desc;

        private TitleComparator(Collator collator, boolean desc) {
            this.collator = collator;
            this.desc = desc;
        }

        @Override
        public int compare(FicheMeta ficheMeta1, FicheMeta ficheMeta2) {
            int result = collator.compare(ficheMeta1.getTitle(), ficheMeta2.getTitle());
            if (result == 0) {
                result = compareId(ficheMeta1, ficheMeta2);
            }
            if (desc) {
                return -result;
            } else {
                return result;
            }
        }

    }


    private static class LabelComparator implements Comparator<Motcle> {

        private final Lang lang;
        private final Collator collator;

        public LabelComparator(Lang lang, Collator collator) {
            this.lang = lang;
            this.collator = collator;
        }

        @Override
        public int compare(Motcle motcle1, Motcle motcle2) {
            int result = collator.compare(toComparableString(motcle1), toComparableString(motcle2));
            if (result == 0) {
                result = compareId(motcle1, motcle2);
            }
            return result;
        }

        private String toComparableString(Motcle motcle) {
            Label label = motcle.getLabels().getLabel(lang);
            if (label != null) {
                return label.getLabelString();
            } else {
                return "";
            }
        }

    }


    private static class IdalphaAndLabelComparator implements Comparator<Motcle> {

        private final Lang lang;

        public IdalphaAndLabelComparator(Lang lang) {
            this.lang = lang;
        }

        @Override
        public int compare(Motcle mc1, Motcle mc2) {
            String lib1 = toComparableString(mc1);
            String lib2 = toComparableString(mc2);
            int result;
            result = lib1.compareTo(lib2);
            if (result != 0) {
                return result;
            }
            result = mc1.getSubsetKey().compareTo(mc2.getSubsetKey());
            if (result != 0) {
                return result;
            }
            int id1 = mc1.getId();
            int id2 = mc2.getId();
            if (id1 < id2) {
                return -1;
            }
            if (id1 > id2) {
                return 1;
            }
            return 0;
        }

        private String toComparableString(Motcle motcle) {
            String idalpha = motcle.getIdalpha();
            Label label = motcle.getLabels().getLabel(lang);
            if (idalpha != null) {
                if (label != null) {
                    return idalpha + " – " + label.getLabelString();
                } else {
                    return idalpha;
                }
            } else {
                if (label != null) {
                    return label.getLabelString();
                } else {
                    return "";
                }
            }
        }

    }


    private static class FieldKeyComparator implements Comparator<FieldKey> {

        private FieldKeyComparator() {
        }

        @Override
        public int compare(FieldKey fieldKey1, FieldKey fieldKey2) {
            int weight1 = getWeight(fieldKey1);
            int weight2 = getWeight(fieldKey2);
            if (weight1 > weight2) {
                return -1;
            } else if (weight1 < weight2) {
                return 1;
            }
            if (weight1 > 100) {
                return 0;
            }
            return fieldKey1.getFieldName().compareTo(fieldKey2.getFieldName());
        }

    }


    private static class LiaisonIdComparator implements Comparator<Liaison> {

        private LiaisonIdComparator() {

        }

        @Override
        public int compare(Liaison liaison1, Liaison liaison2) {
            return compareId(liaison1.getSubsetItem(), liaison2.getSubsetItem());
        }

    }


    private static class SizeComparator implements Comparator<Subset> {

        private SizeComparator() {

        }

        @Override
        public int compare(Subset subset1, Subset subset2) {
            int size1 = subset1.size();
            int size2 = subset2.size();
            if (size1 > size2) {
                return -1;
            }
            if (size1 < size2) {
                return 1;
            }
            return subset1.getSubsetKey().compareTo(subset2.getSubsetKey());
        }

    }


    private static class RedacteurComparator implements Comparator<Redacteur> {

        private RedacteurComparator() {
        }

        @Override
        public int compare(Redacteur r1, Redacteur r2) {
            String id1 = r1.getLogin();
            String id2 = r2.getLogin();
            int result = id1.compareTo(id2);
            if (result != 0) {
                return result;
            }
            String nc1 = r1.getPersonCore().toDirectoryStyle(true);
            String nc2 = r2.getPersonCore().toDirectoryStyle(true);
            result = nc1.compareTo(nc2);
            if (result != 0) {
                return result;
            }
            return r1.getSubsetKey().compareTo(r2.getSubsetKey());
        }

    }


    private static class ExternalItemIdComparator implements Comparator<ExternalItem> {

        private final boolean desc;

        private ExternalItemIdComparator(boolean desc) {
            this.desc = desc;
        }

        @Override
        public int compare(ExternalItem item1, ExternalItem item2) {
            int result = innerCompare(item1, item2);
            if (desc) {
                return -result;
            } else {
                return result;
            }
        }

        private int innerCompare(ExternalItem item1, ExternalItem item2) {
            if (item1 instanceof ExternalItem.Id) {
                if (item2 instanceof ExternalItem.Id) {
                    return compareId((ExternalItem.Id) item1, (ExternalItem.Id) item2);
                } else {
                    return -1;
                }
            } else {
                if (item2 instanceof ExternalItem.Id) {
                    return 1;
                } else {
                    return compareIdalpha((ExternalItem.Idalpha) item1, (ExternalItem.Idalpha) item2);
                }
            }
        }

        private int compareId(ExternalItem.Id item1, ExternalItem.Id item2) {
            int id1 = item1.getId();
            int id2 = item2.getId();
            if (id1 < id2) {
                return -1;
            }
            if (id1 > id2) {
                return 1;

            }
            return 0;
        }

        private int compareIdalpha(ExternalItem.Idalpha item1, ExternalItem.Idalpha item2) {
            return item1.getIdalpha().compareTo(item2.getIdalpha());
        }

    }

}
