/* FichothequeLib_Impl - Copyright (c) 2013-2018 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.croisement.Croisement;
import net.fichotheque.croisement.CroisementChange;
import net.fichotheque.croisement.CroisementKey;
import net.fichotheque.croisement.Lien;
import net.fichotheque.utils.CroisementUtils;
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;


/**
 *
 * @author Vincent Calame
 */
class CroisementImpl implements Croisement {

    private final CroisementKey croisementKey;
    private final Map<String, Lien> lienMap = new HashMap<String, Lien>();
    private AttributesCache attributesCache = null;
    private List<Lien> cacheList;
    AbstractSubsetItem subsetItem1;
    AbstractSubsetItem subsetItem2;


    private CroisementImpl(CroisementKey croisementKey, AbstractSubsetItem subsetItem1, AbstractSubsetItem subsetItem2) {
        this.croisementKey = croisementKey;
        this.subsetItem1 = subsetItem1;
        this.subsetItem2 = subsetItem2;
    }

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

    @Override
    public CroisementKey getCroisementKey() {
        return croisementKey;
    }

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

    @Override
    public Lien getLienByMode(String mode) {
        return lienMap.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(CroisementChange croisementChange) {
        clearCache();
        boolean done = false;
        for (String mode : croisementChange.getRemovedModeList()) {
            Lien currentLien = lienMap.remove(mode);
            if (currentLien != null) {
                done = true;
            }
        }
        for (Lien changedLien : croisementChange.getChangedLienList()) {
            String mode = changedLien.getMode();
            Lien currentLien = lienMap.get(mode);
            if (currentLien != null) {
                if (!CroisementUtils.areEquals(currentLien, changedLien)) {
                    lienMap.put(mode, changedLien);
                    done = true;
                }
            } else {
                lienMap.put(mode, changedLien);
                done = true;
            }
        }
        return done;
    }

    private synchronized List<Lien> initCache() {
        if (cacheList != null) {
            return cacheList;
        }
        SortedMap<String, Lien> sortedMap = new TreeMap<String, Lien>(lienMap);
        List<Lien> list = CroisementUtils.wrap(sortedMap.values().toArray(new Lien[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 CroisementImpl newInstance(Fichotheque fichotheque, CroisementKey croisementKey) {
        Subset subset1 = fichotheque.getSubset(croisementKey.getSubsetKey1());
        if (subset1 == null) {
            return null;
        }
        AbstractSubsetItem subsetItem1 = (AbstractSubsetItem) subset1.getSubsetItemById(croisementKey.getId1());
        if (subsetItem1 == null) {
            return null;
        }
        Subset subset2 = fichotheque.getSubset(croisementKey.getSubsetKey2());
        if (subset2 == null) {
            return null;
        }
        AbstractSubsetItem subsetItem2 = (AbstractSubsetItem) subset2.getSubsetItemById(croisementKey.getId2());
        if (subsetItem2 == null) {
            return null;
        }
        CroisementImpl croisement = new CroisementImpl(croisementKey, subsetItem1, subsetItem2);
        subsetItem1.link(subsetItem2, croisement);
        subsetItem2.link(subsetItem1, croisement);
        return croisement;
    }

    static CroisementImpl newInstance(CroisementKey croisementKey, AbstractSubsetItem subsetItem1, AbstractSubsetItem subsetItem2) {
        if (croisementKey.getOrder(subsetItem1) == 2) {
            AbstractSubsetItem temp = subsetItem1;
            subsetItem1 = subsetItem2;
            subsetItem2 = temp;
        }
        CroisementImpl croisement = new CroisementImpl(croisementKey, subsetItem1, subsetItem2);
        subsetItem1.link(subsetItem2, croisement);
        subsetItem2.link(subsetItem1, croisement);
        return croisement;
    }

}
