/* FichothequeLib_Impl - Copyright (c) 2013-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.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.Fichotheque;
import net.fichotheque.Subset;
import net.fichotheque.junction.JunctionKey;
import net.fichotheque.junction.Tie;
import net.fichotheque.utils.JunctionUtils;
import net.mapeadores.util.attr.Attribute;
import net.mapeadores.util.attr.AttributeKey;
import net.mapeadores.util.attr.AttributeUtils;
import net.mapeadores.util.attr.Attributes;
import net.mapeadores.util.attr.AttributesCache;
import net.fichotheque.junction.JunctionChange;
import net.fichotheque.junction.Junction;


/**
 *
 * @author Vincent Calame
 */
class JunctionImpl implements Junction {

    private final JunctionKey junctionKey;
    private final Map<String, Tie> tieMap = new HashMap<String, Tie>();
    private AttributesCache attributesCache = null;
    private List<Tie> cacheList;
    AbstractSubsetItem subsetItem1;
    AbstractSubsetItem subsetItem2;


    private JunctionImpl(JunctionKey junctionKey, AbstractSubsetItem subsetItem1, AbstractSubsetItem subsetItem2) {
        this.junctionKey = junctionKey;
        this.subsetItem1 = subsetItem1;
        this.subsetItem2 = subsetItem2;
    }

    boolean isEmpty() {
        return tieMap.isEmpty();
    }

    @Override
    public JunctionKey getJunctionKey() {
        return junctionKey;
    }

    @Override
    public List<Tie> getTieList() {
        List<Tie> result = cacheList;
        if (result == null) {
            result = initCache();
        }
        return result;
    }

    @Override
    public Tie getTieByMode(String mode) {
        return tieMap.get(mode);
    }

    @Override
    public Attributes getAttributes() {
        if (attributesCache == null) {
            return AttributeUtils.EMPTY_ATTRIBUTES;
        }
        return attributesCache.getAttributes();
    }

    protected synchronized boolean innerRemoveAttribute(AttributeKey attributeKey) {
        if (attributesCache == null) {
            return false;
        }
        return attributesCache.removeAttribute(attributeKey);
    }

    protected synchronized boolean innerPutAttribute(Attribute attribute) {
        if (attributesCache == null) {
            attributesCache = new AttributesCache();
        }
        return attributesCache.putAttribute(attribute);
    }

    protected synchronized boolean change(JunctionChange junctionChange) {
        clearCache();
        boolean done = false;
        for (String mode : junctionChange.getRemovedModeList()) {
            Tie currentTie = tieMap.remove(mode);
            if (currentTie != null) {
                done = true;
            }
        }
        for (Tie changedTie : junctionChange.getChangedTieList()) {
            String mode = changedTie.getMode();
            Tie currentTie = tieMap.get(mode);
            if (currentTie != null) {
                if (!JunctionUtils.areEquals(currentTie, changedTie)) {
                    tieMap.put(mode, changedTie);
                    done = true;
                }
            } else {
                tieMap.put(mode, changedTie);
                done = true;
            }
        }
        return done;
    }

    private synchronized List<Tie> initCache() {
        if (cacheList != null) {
            return cacheList;
        }
        SortedMap<String, Tie> sortedMap = new TreeMap<String, Tie>(tieMap);
        List<Tie> list = JunctionUtils.wrap(sortedMap.values().toArray(new Tie[sortedMap.size()]));
        cacheList = list;
        return list;
    }

    void unlink() {
        subsetItem1.unlink(subsetItem2, this);
        subsetItem2.unlink(subsetItem1, this);
        subsetItem1 = null;
        subsetItem2 = null;
    }

    private void clearCache() {
        cacheList = null;
    }

    static JunctionImpl newInstance(Fichotheque fichotheque, JunctionKey junctionKey) {
        Subset subset1 = fichotheque.getSubset(junctionKey.getSubsetKey1());
        if (subset1 == null) {
            return null;
        }
        AbstractSubsetItem subsetItem1 = (AbstractSubsetItem) subset1.getSubsetItemById(junctionKey.getId1());
        if (subsetItem1 == null) {
            return null;
        }
        Subset subset2 = fichotheque.getSubset(junctionKey.getSubsetKey2());
        if (subset2 == null) {
            return null;
        }
        AbstractSubsetItem subsetItem2 = (AbstractSubsetItem) subset2.getSubsetItemById(junctionKey.getId2());
        if (subsetItem2 == null) {
            return null;
        }
        JunctionImpl junction = new JunctionImpl(junctionKey, subsetItem1, subsetItem2);
        subsetItem1.link(subsetItem2, junction);
        subsetItem2.link(subsetItem1, junction);
        return junction;
    }

    static JunctionImpl newInstance(JunctionKey junctionKey, AbstractSubsetItem subsetItem1, AbstractSubsetItem subsetItem2) {
        if (junctionKey.getOrder(subsetItem1) == 2) {
            AbstractSubsetItem temp = subsetItem1;
            subsetItem1 = subsetItem2;
            subsetItem2 = temp;
        }
        JunctionImpl junction = new JunctionImpl(junctionKey, subsetItem1, subsetItem2);
        subsetItem1.link(subsetItem2, junction);
        subsetItem2.link(subsetItem1, junction);
        return junction;
    }

}
