/* FichothequeLib_API - Copyright (c) 2012-2023 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.pointeurs;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.pointeurs.MotclePointeur;
import net.fichotheque.thesaurus.Motcle;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.thesaurus.metadata.ThesaurusFieldKey;
import net.fichotheque.utils.JunctionUtils;
import net.fichotheque.utils.ThesaurusUtils;
import net.mapeadores.util.text.Label;
import net.fichotheque.junction.Junctions;


/**
 *
 * @author Vincent Calame
 */
class MotclePointeurImpl extends AbstractPointeur implements MotclePointeur {

    private final Thesaurus thesaurus;
    private final Map<SubsetKey, DescendantAxisBuffer> descendantAxisBufferMap = new HashMap<SubsetKey, DescendantAxisBuffer>();
    private Motcle currentMotcle;

    public MotclePointeurImpl(Thesaurus thesaurus) {
        super(thesaurus);
        this.thesaurus = thesaurus;
    }

    @Override
    protected void initCurrentSubsetItem(SubsetItem subsetItem) {
        Motcle motcle = (Motcle) subsetItem;
        this.currentMotcle = motcle;
        for (DescendantAxisBuffer descendantAxisBuffer : descendantAxisBufferMap.values()) {
            descendantAxisBuffer.setCurrentMotcle(motcle);
        }
    }

    @Override
    public Junctions getDescendantAxisJunctionList(Subset subset) {
        SubsetKey subsetKey = subset.getSubsetKey();
        DescendantAxisBuffer descendantAxisBuffer = descendantAxisBufferMap.get(subsetKey);
        if (descendantAxisBuffer == null) {
            descendantAxisBuffer = new DescendantAxisBuffer(subset);
            descendantAxisBuffer.setCurrentMotcle(currentMotcle);
            descendantAxisBufferMap.put(subsetKey, descendantAxisBuffer);
        }
        return descendantAxisBuffer.getJunctionList();
    }

    @Override
    public Object getFieldValue(ThesaurusFieldKey thesaurusFieldKey) {
        if (currentMotcle == null) {
            return null;
        }
        short category = thesaurusFieldKey.getCategory();
        if (category == ThesaurusFieldKey.SPECIAL_CATEGORY) {
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.ID)) {
                return currentMotcle.getId();
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.IDALPHA)) {
                return currentMotcle.getIdalpha();
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.STATUS)) {
                return currentMotcle.getStatus();
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.PARENT_ID)) {
                Motcle parent = currentMotcle.getParent();
                if (parent == null) {
                    return null;
                }
                return parent.getId();
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.PARENT_IDALPHA)) {
                Motcle parent = currentMotcle.getParent();
                if (parent == null) {
                    return null;
                }
                return parent.getIdalpha();
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.POSITION_LOCAL)) {
                return currentMotcle.getChildIndex() + 1;
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.POSITION_GLOBAL)) {
                return ThesaurusUtils.getGlobalPosition(currentMotcle);
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.LEVEL)) {
                return currentMotcle.getLevel();
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.THIS)) {
                return currentMotcle;
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.BABELIENLANG)) {
                if (thesaurus.isBabelienType()) {
                    return currentMotcle.getBabelienLabel().getLang();
                } else {
                    return null;
                }
            }
            if (thesaurusFieldKey.equals(ThesaurusFieldKey.BABELIENLABEL)) {
                if (thesaurus.isBabelienType()) {
                    return currentMotcle.getBabelienLabel().getLabelString();
                } else {
                    return null;
                }
            }
            throw new IllegalArgumentException("Unkwnon special thesaurusFieldKey : " + thesaurusFieldKey.toString());
        }
        if (category == ThesaurusFieldKey.LABEL_CATEGORY) {
            Label label = currentMotcle.getLabels().getLangPartCheckedLabel(thesaurusFieldKey.getLang());
            if (label != null) {
                return label.getLabelString();
            } else {
                return null;
            }
        }
        throw new IllegalArgumentException("Unkwnon thesaurusFieldKey : " + thesaurusFieldKey.toString());
    }


    private static class DescendantAxisBuffer {

        private final Subset otherSubset;
        private Junctions currentJunctions;
        private int currentMotcleid;

        private DescendantAxisBuffer(Subset otherSubset) {
            this.otherSubset = otherSubset;
            initToNull();
        }

        private void setCurrentMotcle(Motcle motcle) {
            if (motcle == null) {
                if (currentMotcleid != -1) {
                    initToNull();
                }
            } else {
                int newMotcleid = motcle.getId();
                if (newMotcleid != currentMotcleid) {
                    currentMotcleid = newMotcleid;
                    DescendantJunctionsBuilder builder = new DescendantJunctionsBuilder(otherSubset, otherSubset.getFichotheque().getJunctions(motcle, otherSubset));
                    builder.addDescendant(motcle.getChildList());
                    currentJunctions = builder.toJunctions();
                }
            }
        }

        private Junctions getJunctionList() {
            return currentJunctions;
        }

        private void initToNull() {
            currentMotcleid = -1;
            currentJunctions = JunctionUtils.EMPTY_JUNCTIONS;
        }

    }


    private static class DescendantJunctionsBuilder {

        private final Map<Integer, Junctions.Entry> entryMap = new LinkedHashMap<Integer, Junctions.Entry>();
        private final Subset otherSubset;

        private DescendantJunctionsBuilder(Subset otherSubset, Junctions junctions) {
            this.otherSubset = otherSubset;
            for (Junctions.Entry entry : junctions.getEntryList()) {
                entryMap.put(entry.getSubsetItem().getId(), entry);
            }
        }

        private void addDescendant(List<Motcle> motcleList) {
            for (Motcle motcle : motcleList) {
                Junctions junctions = otherSubset.getFichotheque().getJunctions(motcle, otherSubset);
                for (Junctions.Entry entry : junctions.getEntryList()) {
                    SubsetItem subsetItem = entry.getSubsetItem();
                    int id = subsetItem.getId();
                    if (!entryMap.containsKey(id)) {
                        entryMap.put(id, entry);
                    }
                }
                addDescendant(motcle.getChildList());
            }
        }

        private Junctions toJunctions() {
            return JunctionUtils.toJunctions(entryMap.values());
        }

    }

}
