/* 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.thesaurus;

import java.util.List;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.utils.JunctionUtils;
import net.mapeadores.util.exceptions.ImplementationException;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.text.Idalpha;
import net.mapeadores.util.text.Label;
import net.mapeadores.util.text.Labels;


/**
 * Dans le cas des thésaurus babéliens, un mot-clé doit comporter un libellé et
 * un seul.
 *
 * @author Vincent Calame
 */
public interface Motcle extends SubsetItem {

    /**
     * Retourne le parent du mot-clé ou <em>null</em> si le mot-clé appartient
     * au premier niveau.
     */
    public Motcle getParent();

    public MotcleIcon getMotcleIcon();

    public List<Motcle> getChildList();

    public int getChildIndex();

    /**
     * Retourne l'identifiant alphanumérique du mot-clé.
     */
    public String getIdalpha();

    /**
     * Retourne le niveau du mot-clé, c'est à dire sa profondeur par rapport à
     * l'ensemble du thésaurus. Retourne 1 si le mot-clé appartient au premier
     * niveau (getParent() == null), sinon on a getLevel() ==
     * getParent().getLevel() + 1
     */
    public int getLevel();

    public String getStatus();

    public Labels getLabels();

    public default Thesaurus getThesaurus() {
        return (Thesaurus) getSubset();
    }

    public default boolean isBabelienType() {
        return ((Thesaurus) getSubset()).isBabelienType();
    }

    public default Label getBabelienLabel() {
        if (!isBabelienType()) {
            throw new UnsupportedOperationException("motcle.getThesaurus is not babelien");
        }
        Labels labels = getLabels();
        if (labels.isEmpty()) {
            throw new ImplementationException("motcle.getLabelCount() == 0 in a babélien thesaurus");
        }
        return labels.get(0);
    }

    public default String getLabelString(Lang lang) {
        return getLabelString(lang, "?");
    }

    public default String getLabelString(Lang lang, String defaultString) {
        if (isBabelienType()) {
            return getBabelienLabel().getLabelString();
        } else {
            return getLabels().seekLabelString(lang, defaultString);
        }
    }

    public default String getSignificantIdalpha() {
        String idalpha = getIdalpha();
        if (Idalpha.isSignificant(idalpha)) {
            return idalpha;
        } else {
            return null;
        }
    }

    public default boolean shouldNotJunction(SubsetKey subsetKey) {
        switch (getStatus()) {
            case FichothequeConstants.GROUP_STATUS:
            case FichothequeConstants.OBSOLETE_STATUS:
                return true;
        }
        return JunctionUtils.isShouldNotJunction(this, subsetKey);
    }

    public default boolean isDescendantOf(Motcle ancestor) {
        Motcle parent = getParent();
        while (parent != null) {
            if (parent.equals(ancestor)) {
                return true;
            }
            parent = parent.getParent();
        }
        return false;
    }

    /**
     * Retourne le mot-clé suivant dans la liste des mots-clés du même père.
     * Autrement dit, cette méthode retourne le frère cadet ou <em>null</em> si
     * le mot-clé est le dernier de la fratrie.
     */
    public default Motcle getNextSibling() {
        int childIndex = getChildIndex();
        Motcle parent = getParent();
        List<Motcle> siblingList = (parent != null) ? parent.getChildList() : getThesaurus().getFirstLevelList();
        if (childIndex < siblingList.size() - 1) {
            return siblingList.get(childIndex + 1);
        } else {
            return null;
        }
    }

    /**
     * Retourne le mot-clé précédent le mot-clé dans la liste des mots-clés du
     * même père. Autrement dit, cette méthode retourne le frère aîné ou
     * <em>null</em> si le mot-clé est le premier de la fratrie.
     */
    public default Motcle getPreviousSibling() {
        int childIndex = getChildIndex();
        if (childIndex == 0) {
            return null;
        }
        Motcle parent = getParent();
        List<Motcle> siblingList = (parent != null) ? parent.getChildList() : getThesaurus().getFirstLevelList();
        return siblingList.get(childIndex - 1);
    }

    /**
     * Retourne le mot-clé suivant le mot-clé dans l'arbre de circulation du
     * thésaurus. La circulation dans un thésaurus se fait suivant le même
     * principe que Xpath du langage XSLT : le mot-clé suivant un mot-clé est
     * son fils aîné, sinon son frère cadet, sinon le frère cadet de son père,
     * etc. La méthode retourne <em>null</em> si le mot-clé est le dernier.
     */
    public default Motcle getNext() {
        List<Motcle> childList = getChildList();
        if (childList.size() > 0) {
            return childList.get(0);
        }
        Motcle sibling = getNextSibling();
        if (sibling != null) {
            return sibling;
        }
        Motcle parent = getParent();
        while (parent != null) {
            sibling = parent.getNextSibling();
            if (sibling != null) {
                return sibling;
            }
            parent = parent.getParent();
        }
        return null;
    }

    /**
     * Retourne le mot-clé précédent le mot-clé dans l'arbre de circulation du
     * thésaurus. Voir la méthode <em>getNext()</em>. Retourne
     * <em>null</em> si le mot-clé est le premier.
     */
    public default Motcle getPrevious() {
        Motcle previous = getPreviousSibling();
        if (previous == null) {
            return getParent();
        }
        List<Motcle> childList = previous.getChildList();
        while (!childList.isEmpty()) {
            previous = childList.get(childList.size() - 1);
            childList = previous.getChildList();
        }
        return previous;
    }

}
