/* FichothequeLib_API - Copyright (c) 2006-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.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.RandomAccess;
import java.util.Set;
import java.util.function.Predicate;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.CorpusMetadata;
import net.fichotheque.corpus.metadata.FieldGeneration;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.corpus.metadata.FieldOptionConstants;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.money.Currencies;
import net.mapeadores.util.text.Label;


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

    public final static List<FieldKey> EMPTY_FIELDKEYLIST = Collections.emptyList();
    public final static List<CorpusField> EMPTY_CORPUSFIELDLIST = Collections.emptyList();
    public final static FieldGeneration EMPTY_FIELDGENERATION = new EmptyFieldGeneration();

    private CorpusMetadataUtils() {
    }

    public static String getDefaultLangScope(FieldKey fieldKey) {
        switch (fieldKey.getKeyString()) {
            case FieldKey.SPECIAL_LANG:
                return FieldOptionConstants.CONFIG_SCOPE;
            default:
                return FieldOptionConstants.ALL_SCOPE;
        }
    }

    public static boolean isCurrenciesProp(CorpusField corpusField) {
        if (!(corpusField.isProp())) {
            return false;
        }
        Currencies currencies = corpusField.getCurrencies();
        return (currencies != null);
    }

    public static boolean isCurrenciesInfo(CorpusField corpusField) {
        if (!(corpusField.isInfo())) {
            return false;
        }
        Currencies currencies = corpusField.getCurrencies();
        return (currencies != null);
    }

    public static String getFieldTitle(CorpusField corpusField, Lang lang) {
        return getFieldTitle(corpusField, lang, false);
    }

    public static String getFieldTitle(CorpusField corpusField, Lang lang, boolean withKey) {
        Label label = corpusField.getLabels().getLangPartCheckedLabel(lang);
        if (label == null) {
            if (withKey) {
                return "[" + corpusField.getFieldString() + "]";
            } else {
                return corpusField.getFieldString();
            }
        } else {
            if (withKey) {
                return "[" + corpusField.getFieldString() + "] " + label.getLabelString();
            } else {
                return label.getLabelString();
            }
        }
    }

    public static String getNewFicheLabel(Corpus corpus, Lang lang) {
        String label = FichothequeUtils.getPhraseLabel(corpus.getCorpusMetadata().getPhrases(), FichothequeConstants.NEWFICHE_PHRASE, lang);
        if (label != null) {
            return label;
        } else {
            return corpus.getSubsetName();
        }
    }

    public static String getSatelliteLabel(Corpus corpus, Lang lang) {
        String label = FichothequeUtils.getPhraseLabel(corpus.getCorpusMetadata().getPhrases(), FichothequeConstants.SATELLITE_PHRASE, lang);
        if (label != null) {
            return label;
        } else {
            return FichothequeUtils.getTitle(corpus, lang);
        }
    }

    public static String getFicheTitle(FicheMeta ficheMeta, Lang workingLang, Locale formatLocale) {
        return FichothequeUtils.getNumberPhrase(ficheMeta, FichothequeConstants.FICHE_PHRASE, workingLang, formatLocale) + " – " + ficheMeta.getTitle();
    }

    /**
     * Compile les différents champs d'une fiche en une seule liste. Cette liste
     * commence par les champs obligatoires. En particulier, le premier élément
     * est le champ de l'identifiant de corpus.
     */
    public static List<CorpusField> getCorpusFieldList(CorpusMetadata corpusMetadata) {
        List<CorpusField> list = getSpecialCorpusFieldList(corpusMetadata, true);
        list.addAll(corpusMetadata.getPropList());
        list.addAll(corpusMetadata.getInfoList());
        list.addAll(corpusMetadata.getSectionList());
        return list;
    }

    /**
     * Compile dans une seule liste les champs spéciaux d'une fiche. Le premier
     * élément est le champ de l'identifiant de corpus.
     */
    public static List<CorpusField> getSpecialCorpusFieldList(CorpusMetadata corpusMetadata, boolean withId) {
        List<CorpusField> list = new ArrayList<CorpusField>();
        if (withId) {
            list.add(corpusMetadata.getCorpusField(FieldKey.ID));
        }
        list.add(corpusMetadata.getCorpusField(FieldKey.LANG));
        list.add(corpusMetadata.getCorpusField(FieldKey.TITLE));
        list.add(corpusMetadata.getCorpusField(FieldKey.OWNERS));
        CorpusField subtitleField = corpusMetadata.getCorpusField(FieldKey.SUBTITLE);
        if (subtitleField != null) {
            list.add(subtitleField);
        }
        return list;
    }

    public static Predicate<FieldKey> getFieldKeyPredicate(CorpusMetadata corpusMetadata) {
        Set<FieldKey> currentSet = new HashSet<FieldKey>();
        currentSet.add(FieldKey.ID);
        currentSet.add(FieldKey.LANG);
        currentSet.add(FieldKey.TITLE);
        currentSet.add(FieldKey.OWNERS);
        for (CorpusField corpusField : corpusMetadata.getPropList()) {
            currentSet.add(corpusField.getFieldKey());
        }
        for (CorpusField corpusField : corpusMetadata.getInfoList()) {
            currentSet.add(corpusField.getFieldKey());
        }
        for (CorpusField corpusField : corpusMetadata.getSectionList()) {
            currentSet.add(corpusField.getFieldKey());
        }
        if (corpusMetadata.isWithSubtitle()) {
            currentSet.add(FieldKey.SUBTITLE);
        }
        return new SetPredicate(currentSet);
    }

    /**
     * Indique si le corpus possède au moins un champ susceptible d'être
     * supprimé.
     */
    public static boolean containsRemoveableField(CorpusMetadata corpusMetadata) {
        if (corpusMetadata.isWithSubtitle()) {
            return true;
        }
        if (!corpusMetadata.getPropList().isEmpty()) {
            return true;
        }
        if (!corpusMetadata.getInfoList().isEmpty()) {
            return true;
        }
        if (!corpusMetadata.getSectionList().isEmpty()) {
            return true;
        }
        return false;
    }


    public static List<CorpusField> getCorpusFieldListByFicheItemType(CorpusMetadata corpusMetadata, String ficheItemType, boolean startWithSpecial) {
        List<CorpusField> liste = new ArrayList<CorpusField>();
        if (startWithSpecial) {
            switch (ficheItemType) {
                case CorpusField.PERSON_FIELD:
                    liste.add(corpusMetadata.getCorpusField(FieldKey.OWNERS));
                    break;
                case CorpusField.LANGUAGE_FIELD:
                    liste.add(corpusMetadata.getCorpusField(FieldKey.LANG));
                    break;
            }
        }
        for (CorpusField corpusField : corpusMetadata.getPropList()) {
            if (corpusField.isType(ficheItemType)) {
                liste.add(corpusField);
            }
        }
        for (CorpusField corpusField : corpusMetadata.getInfoList()) {
            if (corpusField.isType(ficheItemType)) {
                liste.add(corpusField);
            }
        }
        return liste;
    }

    public static FieldKey[] getEnteteFieldKeyArray(CorpusMetadata corpusMetadata) {
        List<FieldKey> fieldKeyList = new ArrayList<FieldKey>();
        fieldKeyList.add(FieldKey.TITLE);
        fieldKeyList.add(FieldKey.LANG);
        fieldKeyList.add(FieldKey.OWNERS);
        if (corpusMetadata.isWithSubtitle()) {
            fieldKeyList.add(FieldKey.SUBTITLE);
        }
        for (CorpusField corpusField : corpusMetadata.getPropList()) {
            fieldKeyList.add(corpusField.getFieldKey());
        }
        for (CorpusField corpusField : corpusMetadata.getInfoList()) {
            fieldKeyList.add(corpusField.getFieldKey());
        }
        return fieldKeyList.toArray(new FieldKey[fieldKeyList.size()]);
    }

    public static FieldKey[] getFicheFieldKeyArray(CorpusMetadata corpusMetadata) {
        List<FieldKey> fieldKeyList = new ArrayList<FieldKey>();
        fieldKeyList.add(FieldKey.TITLE);
        fieldKeyList.add(FieldKey.LANG);
        fieldKeyList.add(FieldKey.OWNERS);
        if (corpusMetadata.isWithSubtitle()) {
            fieldKeyList.add(FieldKey.SUBTITLE);
        }
        for (CorpusField corpusField : corpusMetadata.getPropList()) {
            fieldKeyList.add(corpusField.getFieldKey());
        }
        for (CorpusField corpusField : corpusMetadata.getInfoList()) {
            fieldKeyList.add(corpusField.getFieldKey());
        }
        for (CorpusField corpusField : corpusMetadata.getSectionList()) {
            fieldKeyList.add(corpusField.getFieldKey());
        }
        return fieldKeyList.toArray(new FieldKey[fieldKeyList.size()]);
    }

    public static FieldKey[] toFieldKeyArray(List<CorpusField> corpusFieldList) {
        int length = corpusFieldList.size();
        FieldKey[] result = new FieldKey[length];
        for (int i = 0; i < length; i++) {
            result[i] = corpusFieldList.get(i).getFieldKey();
        }
        return result;
    }

    public static boolean areEquals(List<CorpusField> corpusFieldList, List<CorpusField> list) {
        int length = list.size();
        if (length != corpusFieldList.size()) {
            return false;
        }
        if (length == 0) {
            return true;
        }
        for (int i = 0; i < length; i++) {
            if (!list.get(i).equals(corpusFieldList.get(i))) {
                return false;
            }
        }
        return true;
    }

    /**
     * retourne nul si la liste devient vide.
     */
    public static List<CorpusField> removeCorpusField(List<CorpusField> corpusFieldList, CorpusField corpusField) {
        if (corpusFieldList == null) {
            return null;
        }
        List<CorpusField> result = new ArrayList<CorpusField>();
        boolean done = false;
        for (CorpusField otherCorpusField : corpusFieldList) {
            if (otherCorpusField.equals(corpusField)) {
                done = true;
            } else {
                result.add(otherCorpusField);
            }
        }
        if (!done) {
            return corpusFieldList;
        }
        int size = result.size();
        if (result.isEmpty()) {
            return null;
        }
        return wrap(result.toArray(new CorpusField[size]));
    }


    public static List<FieldKey> wrap(FieldKey[] array) {
        return new FieldKeyList(array);
    }

    public static List<CorpusField> wrap(CorpusField[] array) {
        return new CorpusFieldList(array);
    }


    private static class FieldKeyList extends AbstractList<FieldKey> implements RandomAccess {

        private final FieldKey[] array;

        private FieldKeyList(FieldKey[] array) {
            this.array = array;
        }

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

        @Override
        public FieldKey get(int index) {
            return array[index];
        }

    }


    private static class CorpusFieldList extends AbstractList<CorpusField> implements RandomAccess {

        private final CorpusField[] array;

        private CorpusFieldList(CorpusField[] array) {
            this.array = array;
        }

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

        @Override
        public CorpusField get(int index) {
            return array[index];
        }

    }


    private static class EmptyFieldGeneration implements FieldGeneration {

        private final List<FieldGeneration.Entry> list = Collections.emptyList();

        private EmptyFieldGeneration() {

        }

        @Override
        public String getRawString() {
            return "";
        }

        @Override
        public List<FieldGeneration.Entry> getEntryList() {
            return list;
        }

        @Override
        public boolean isEmpty() {
            return true;
        }

    }


    private static class SetPredicate implements Predicate<FieldKey> {

        private final Set<FieldKey> set;

        private SetPredicate(Set<FieldKey> set) {
            this.set = set;
        }

        @Override
        public boolean test(FieldKey fieldKey) {
            return set.contains(fieldKey);
        }

    }

}
