/* 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.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.junction.Junction;
import net.fichotheque.junction.Junctions;
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;


/**
 *
 * @author Vincent Calame
 */
abstract class AbstractSubsetItem implements SubsetItem {

    private final Map<SubsetKey, InternalJunctions> junctionsMap = new HashMap<SubsetKey, InternalJunctions>();
    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 Junctions getJunctions(Subset subset) {
        InternalJunctions junctions = junctionsMap.get(subset.getSubsetKey());
        if (junctions == null) {
            return JunctionUtils.EMPTY_JUNCTIONS;
        }
        return junctions.getCache();
    }

    void link(SubsetItem otherSubsetItem, JunctionImpl junctionImpl) {
        SubsetKey otherSubsetKey = otherSubsetItem.getSubsetKey();
        InternalJunctions junctions = junctionsMap.get(otherSubsetKey);
        if (junctions == null) {
            junctions = new InternalJunctions();
            junctionsMap.put(otherSubsetKey, junctions);
        }
        junctions.add(otherSubsetItem, junctionImpl);
    }

    void unlink(SubsetItem otherSubsetItem, JunctionImpl junctionImpl) {
        SubsetKey otherSubsetKey = otherSubsetItem.getSubsetKey();
        InternalJunctions junctions = junctionsMap.get(otherSubsetKey);
        if (junctions != null) {
            junctions.remove(junctionImpl);
        }
    }

    boolean hasJunction() {
        if (junctionsMap.isEmpty()) {
            return false;
        }
        for (InternalJunctions junctions : junctionsMap.values()) {
            if (!junctions.entryList.isEmpty()) {
                return true;
            }
        }
        return false;
    }


    private static class InternalJunctions {

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

        private InternalJunctions() {
        }

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

        private synchronized void add(SubsetItem subsetItem, Junction junction) {
            entryList.add(JunctionUtils.toEntry(subsetItem, junction));
            clearCache();
        }

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

        private void clearCache() {
            cache = null;
        }

        private synchronized Junctions initCache() {
            if (cache != null) {
                return cache;
            }
            Junctions newCache = JunctionUtils.toJunctions(entryList);
            cache = newCache;
            return newCache;
        }

    }

}
