/* 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.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
import net.fichotheque.Fichotheque;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.include.IconSources;
import net.fichotheque.thesaurus.Motcle;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.thesaurus.ThesaurusEditor;
import net.fichotheque.thesaurus.metadata.ThesaurusFieldKey;
import net.fichotheque.thesaurus.metadata.ThesaurusLangChecker;
import net.fichotheque.thesaurus.policies.DynamicEditPolicy;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.exceptions.ImplementationException;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.Langs;
import net.mapeadores.util.text.Idalpha;
import net.mapeadores.util.text.Label;
import net.mapeadores.util.text.LabelChange;


/**
 * Classe d'utilitaires pour les thésaurus. Cette classe ne comprend que des
 * méthodes statiques.
 * <p>
 * Outre leur caractère utilitaire, certaines méthodes font des vérifications de
 * la validité de l'implémentation. Elles renvoient alors une exception
 * ImplementationException
 *
 * @author Vincent Calame
 */
public final class ThesaurusUtils {

    public final static List<Motcle> EMPTY_MOTCLELIST = Collections.emptyList();
    public final static DynamicEditPolicy.None NONE_POLICY = new DynamicEditPolicy.None() {

    };
    public final static DynamicEditPolicy.Allow ALLOW_POLICY = new DynamicEditPolicy.Allow() {

    };

    private ThesaurusUtils() {
    }

    public static List<ThesaurusFieldKey> computeCoreFieldList(Thesaurus thesaurus, ThesaurusLangChecker thesaurusLangChecker) {
        List<ThesaurusFieldKey> coreList = new ArrayList<ThesaurusFieldKey>();
        coreList.add(ThesaurusFieldKey.ID);
        coreList.add(ThesaurusFieldKey.PARENT_ID);
        if (thesaurus.isBabelienType()) {
            coreList.add(ThesaurusFieldKey.BABELIENLANG);
            coreList.add(ThesaurusFieldKey.BABELIENLABEL);
        } else {
            if (thesaurus.isIdalphaType()) {
                coreList.add(ThesaurusFieldKey.IDALPHA);
                coreList.add(ThesaurusFieldKey.PARENT_IDALPHA);
            }
            for (Lang lang : thesaurusLangChecker.getAuthorizedLangs(thesaurus)) {
                coreList.add(ThesaurusFieldKey.toLabelThesaurusFieldKey(lang));
            }
        }
        coreList.add(ThesaurusFieldKey.STATUS);
        return coreList;
    }

    public static Lang checkDisponibility(ThesaurusLangChecker thesaurusLangChecker, Thesaurus thesaurus, Lang lang) {
        if (thesaurus.isBabelienType()) {
            return lang;
        }
        Langs langs = thesaurusLangChecker.getAuthorizedLangs(thesaurus);
        if ((langs == null) || (langs.isEmpty())) {
            throw new ImplementationException("thesaurus.getMetadata.getAuthorizedLangIntegerList() is null or empty");
        }
        Lang rootLang = lang.getRootLang();
        boolean withRoot = false;
        for (Lang thesaurusLang : langs) {
            if (thesaurusLang.equals(lang)) {
                return lang;
            }
            if (thesaurusLang.equals(rootLang)) {
                withRoot = true;
            }
        }
        if (withRoot) {
            return lang;
        }
        return langs.get(0);
    }

    public static String getMotcleTitle(Motcle motcle, Lang lang, boolean withId, boolean onlySignificant) {
        StringBuilder buf = new StringBuilder();
        String idalpha = motcle.getIdalpha();
        if (idalpha != null) {
            if (!Idalpha.isSignificant(idalpha)) {
                if (!onlySignificant) {
                    buf.append("(");
                    buf.append(idalpha);
                    buf.append(") ");
                }
            } else if (motcle.getThesaurus().getThesaurusMetadata().isBracketsIdalphaStyle()) {
                buf.append("[");
                buf.append(idalpha);
                buf.append("] ");
            } else {
                buf.append(idalpha);
                buf.append(" \u2013 ");
            }
        }
        buf.append(motcle.getLabelString(lang));
        if ((idalpha == null) && (withId)) {
            buf.append(" (");
            buf.append("id=");
            buf.append(motcle.getId());
            buf.append(')');
        }
        return buf.toString();
    }

    public static void sortByIdalpha(ThesaurusEditor thesaurusEditor, Motcle motcle, boolean recursive) {
        List<Motcle> childList = (motcle != null) ? motcle.getChildList() : thesaurusEditor.getThesaurus().getFirstLevelList();
        int size = childList.size();
        if (size == 0) {
            return;
        }
        if (size == 1) {
            if (recursive) {
                sortByIdalpha(thesaurusEditor, childList.get(0), true);
                return;
            }
        }
        Motcle[] array = childList.toArray(new Motcle[size]);
        Arrays.sort(array, Comparators.IDALPHA);
        for (int i = 0; i < size; i++) {
            Motcle child = array[i];
            if (child.getChildIndex() != i) {
                thesaurusEditor.setChildIndex(child, i);
            }
            if (recursive) {
                sortByIdalpha(thesaurusEditor, child, true);
            }
        }
    }

    public static int removeRemoveableMotcle(ThesaurusEditor thesaurusEditor) {
        int count = 0;
        List<Motcle> firstLevel = thesaurusEditor.getThesaurus().getFirstLevelList();
        for (Motcle motcle : firstLevel) {
            count = count + removeRemoveable(motcle, thesaurusEditor);
        }
        return count;
    }

    private static int removeRemoveable(Motcle motcle, ThesaurusEditor thesaurusEditor) {
        Fichotheque fichotheque = motcle.getFichotheque();
        int count = 0;
        for (Motcle child : motcle.getChildList()) {
            count = count + removeRemoveable(child, thesaurusEditor);
        }
        if (fichotheque.isRemoveable(motcle)) {
            thesaurusEditor.removeMotcle(motcle);
            count++;
        }
        return count;
    }

    /**
     * Retourne la liste des clés des thésaurus babéliens. Cette liste est
     * établie à un instant t. Elle n'est pas synchronisée.
     *
     */
    public static List<Thesaurus> getBabelienThesaurusList(Fichotheque fichotheque) {
        List<Thesaurus> list = new ArrayList<Thesaurus>();
        for (Thesaurus thesaurus : fichotheque.getThesaurusList()) {
            if (thesaurus.isBabelienType()) {
                list.add(thesaurus);
            }
        }
        return list;
    }

    /**
     * Retourne la lslite des clés des thésaurus sans identifiant
     * alphanumériques.
     */
    public static List<Thesaurus> getNoIdalphaThesaurusList(Fichotheque fichotheque) {
        List<Thesaurus> list = new ArrayList<Thesaurus>();
        for (Thesaurus thesaurus : fichotheque.getThesaurusList()) {
            if (!thesaurus.isIdalphaType()) {
                list.add(thesaurus);
            }
        }
        return list;
    }

    public static String getGlobalPosition(Motcle motcle) {
        StringBuilder buf = new StringBuilder();
        try {
            appendGlobalPosition(buf, motcle);
        } catch (IOException ioe) {
        }
        return buf.toString();
    }

    public static void appendGlobalPosition(Appendable buf, Motcle motcle) throws IOException {
        Motcle parent = motcle.getParent();
        int position = motcle.getChildIndex() + 1;
        int size;
        if (parent != null) {
            appendGlobalPosition(buf, parent);
            buf.append('.');
            size = parent.getChildList().size();
        } else {
            size = motcle.getThesaurus().getFirstLevelList().size();
        }
        if (size < 99) {
            size = 99;
        }
        appendZero(buf, size, position);
        buf.append(String.valueOf(position));
    }

    private static void appendZero(Appendable buf, int size, int position) throws IOException {
        int dizaine = 10;
        while ((size >= dizaine)) {
            if (position < dizaine) {
                buf.append('0');
            }
            dizaine = dizaine * 10;
        }
    }

    public static List<Motcle> toHierarchicMotcleList(Thesaurus thesaurus) {
        List<Motcle> flatList = new ArrayList<Motcle>();
        appendMotcleList(flatList, thesaurus.getFirstLevelList());
        return flatList;
    }

    private static void appendMotcleList(List<Motcle> flatList, List<Motcle> motcleList) {
        for (Motcle motcle : motcleList) {
            flatList.add(motcle);
            appendMotcleList(flatList, motcle.getChildList());
        }
    }

    /**
     *
     * @throws IllegalStateException si le thésaurus de transfer n'est pas
     * babélien.
     */
    public static Thesaurus getTransferThesaurus(Fichotheque fichotheque, DynamicEditPolicy policy) {
        Thesaurus thesaurus = (Thesaurus) fichotheque.getSubset(((DynamicEditPolicy.Transfer) policy).getTransferThesaurusKey());
        if (!thesaurus.isBabelienType()) {
            throw new IllegalStateException("thesaurus is not babelien");
        }
        return thesaurus;
    }

    /**
     *
     * @throws IllegalStateException si un des thésaurusKey ne correspond pas à
     * un thésaurus de la base ou si c'est un thésaurus avec idalpha.
     */
    @Nullable
    public static Thesaurus[] getCheckThesaurusArray(Fichotheque fichotheque, DynamicEditPolicy policy) {
        if (!policy.getType().equals(DynamicEditPolicy.CHECK_TYPE)) {
            return null;
        }
        DynamicEditPolicy.Check checkPolicy = (DynamicEditPolicy.Check) policy;
        List<SubsetKey> checkList = checkPolicy.getCheckSubseKeyList();
        int checkCount = checkList.size();
        Thesaurus[] array = new Thesaurus[checkCount];
        for (int i = 0; i < checkCount; i++) {
            SubsetKey thesaurusKey = checkList.get(i);
            Thesaurus thesaurus = (Thesaurus) fichotheque.getSubset(thesaurusKey);
            if (thesaurus == null) {
                throw new IllegalStateException("thesaurusPolicy.getVerifThesaurusKeyList() contains a wrong key");
            }
            if (thesaurus.isIdalphaType()) {
                throw new IllegalStateException("thesaurus with idalpha cannot own to verif list");
            }
            array[i] = thesaurus;
        }
        return array;
    }

    public static Collection<SubsetItem> toSubsetItemList(Thesaurus thesaurus, int max) {
        List<SubsetItem> motcleList = new ArrayList<SubsetItem>();
        Motcle motcle = thesaurus.getFirst();
        while (motcle != null) {
            motcleList.add(motcle);
            if (max > 0) {
                if (motcleList.size() >= max) {
                    break;
                }
            }
            motcle = motcle.getNext();
        }
        return motcleList;
    }

    public static boolean changeMotcleLabels(ThesaurusEditor thesaurusEditor, Motcle motcle, LabelChange labelChange) {
        boolean done = false;
        for (Label label : labelChange.getChangedLabels()) {
            boolean stepdone = thesaurusEditor.putLabel(motcle, label);
            if (stepdone) {
                done = true;
            }
        }
        for (Lang lang : labelChange.getRemovedLangList()) {
            boolean stepdone = thesaurusEditor.removeLabel(motcle, lang);
            if (stepdone) {
                done = true;
            }
        }
        return done;
    }

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


    private static class MotcleList extends AbstractList<Motcle> implements RandomAccess {

        private final Motcle[] array;

        private MotcleList(Motcle[] array) {
            this.array = array;
        }

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

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

    }

    public static List<IconSources.Entry> wrap(IconSources.Entry[] array) {
        return new IconSourcesEntryList(array);
    }


    private static class IconSourcesEntryList extends AbstractList<IconSources.Entry> implements RandomAccess {

        private final IconSources.Entry[] array;

        private IconSourcesEntryList(IconSources.Entry[] array) {
            this.array = array;
        }

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

        @Override
        public IconSources.Entry get(int index) {
            return array[index];
        }

    }

}
