/* FichothequeLib_Impl - Copyright (c) 2013-2024 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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.croisement.Croisement;
import net.fichotheque.croisement.Croisements;
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
 */
abstract class AbstractSubsetItem implements SubsetItem {

    private final Map<SubsetKey, InternalCroisements> croisementsMap = new HashMap<SubsetKey, InternalCroisements>();
    private AttributesCache attributesCache = null;

    @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;
        }
        boolean done = attributesCache.removeAttribute(attributeKey);
        if (done) {
            fireAttributeChange();
        }
        return done;
    }

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

    void fireAttributeChange() {

    }

    @Override
    public Croisements getCroisements(Subset subset) {
        InternalCroisements croisements = croisementsMap.get(subset.getSubsetKey());
        if (croisements == null) {
            return CroisementUtils.EMPTY_CROISEMENTS;
        }
        return croisements.getCache();
    }

    void link(SubsetItem otherSubsetItem, CroisementImpl croisement) {
        SubsetKey otherSubsetKey = otherSubsetItem.getSubsetKey();
        InternalCroisements croisements = croisementsMap.get(otherSubsetKey);
        if (croisements == null) {
            croisements = new InternalCroisements();
            croisementsMap.put(otherSubsetKey, croisements);
        }
        croisements.add(otherSubsetItem, croisement);
    }

    void unlink(SubsetItem otherSubsetItem, CroisementImpl implCroisement) {
        SubsetKey otherSubsetKey = otherSubsetItem.getSubsetKey();
        InternalCroisements croisements = croisementsMap.get(otherSubsetKey);
        if (croisements != null) {
            croisements.remove(implCroisement);
        }
    }

    boolean hasCroisement() {
        if (croisementsMap.isEmpty()) {
            return false;
        }
        for (InternalCroisements croisements : croisementsMap.values()) {
            if (!croisements.entryList.isEmpty()) {
                return true;
            }
        }
        return false;
    }


    private static class InternalCroisements {

        private final List<Croisements.Entry> entryList = new ArrayList<Croisements.Entry>();
        private Croisements cache;

        private InternalCroisements() {
        }

        private Croisements getCache() {
            Croisements result = cache;
            if (result == null) {
                result = initCache();
            }
            return result;
        }

        private synchronized void add(SubsetItem subsetItem, Croisement croisement) {
            entryList.add(CroisementUtils.toEntry(subsetItem, croisement));
            clearCache();
        }

        private synchronized void remove(CroisementImpl implCroisement) {
            int size = entryList.size();
            for (int i = 0; i < size; i++) {
                if (entryList.get(i).getCroisement().equals(implCroisement)) {
                    entryList.remove(i);
                    clearCache();
                    break;
                }
            }
        }

        private void clearCache() {
            cache = null;
        }

        private synchronized Croisements initCache() {
            if (cache != null) {
                return cache;
            }
            Croisements newCache = CroisementUtils.toCroisements(entryList);
            cache = newCache;
            return newCache;
        }

    }

}
