/* FichothequeLib_API - Copyright (c) 2011-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.List;
import java.util.Map;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.croisement.Croisements;
import net.fichotheque.pointeurs.SubsetItemPointeur;
import net.fichotheque.utils.CroisementUtils;


/**
 *
 * @author Vincent Calame
 */
abstract class AbstractPointeur implements SubsetItemPointeur {

    private final Subset mainSubset;
    private final Map<SubsetKey, CroisementsBuffer> croisementListBufferMap = new HashMap<SubsetKey, CroisementsBuffer>();
    private SubsetItem currentSubsetItem;
    private Map<SubsetKey, SubsetItemPointeur> parentagePointeurMap;
    private boolean isParentageMapInit = false;
    private Map<String, Object> currentObjectMap;
    private Map<String, Object> pointeurObjectMap;
    private Map<SubsetKey, SubsetItemPointeur> associatedPointeurMap;

    public AbstractPointeur(Subset subset) {
        this.mainSubset = subset;
    }

    @Override
    public Subset getSubset() {
        return mainSubset;
    }

    @Override
    public SubsetItem getCurrentSubsetItem() {
        return currentSubsetItem;
    }

    @Override
    public SubsetItemPointeur getAssociatedPointeur(Subset subset) {
        SubsetKey subsetKey = subset.getSubsetKey();
        if (associatedPointeurMap == null) {
            associatedPointeurMap = new HashMap<SubsetKey, SubsetItemPointeur>();
        }
        SubsetItemPointeur pointeur = associatedPointeurMap.get(subsetKey);
        if (pointeur == null) {
            pointeur = PointeurFactory.newSubsetItemPointeur(subset);
            associatedPointeurMap.put(subsetKey, pointeur);
        }
        return pointeur;
    }

    @Override
    public void setCurrentSubsetItem(int id) {
        if (currentSubsetItem != null) {
            if (currentSubsetItem.getId() == id) {
                return;
            }
        }
        innerSetCurrentSubsetItem(mainSubset.getSubsetItemById(id));
    }

    @Override
    public void setCurrentSubsetItem(SubsetItem subsetItem) {
        if ((currentSubsetItem != null) && (subsetItem != null)) {
            if (currentSubsetItem.equals(subsetItem)) {
                return;
            }
        }
        innerSetCurrentSubsetItem(subsetItem);
    }

    private void innerSetCurrentSubsetItem(SubsetItem subsetItem) {
        this.currentSubsetItem = subsetItem;
        for (CroisementsBuffer buffer : croisementListBufferMap.values()) {
            buffer.setCurrent(subsetItem);
        }
        if (parentagePointeurMap != null) {
            for (SubsetItemPointeur pointeur : parentagePointeurMap.values()) {
                if (subsetItem == null) {
                    pointeur.setCurrentSubsetItem(null);
                } else {
                    pointeur.setCurrentSubsetItem(subsetItem.getId());
                }
            }
        }
        if (currentObjectMap != null) {
            currentObjectMap.clear();
        }
        initCurrentSubsetItem(subsetItem);
    }

    protected abstract void initCurrentSubsetItem(SubsetItem subsetItem);

    @Override
    public Croisements getCroisements(Subset subset) {
        SubsetKey subsetKey = subset.getSubsetKey();
        CroisementsBuffer buffer = croisementListBufferMap.get(subsetKey);
        if (buffer == null) {
            buffer = new CroisementsBuffer(subset);
            buffer.setCurrent(currentSubsetItem);
            croisementListBufferMap.put(subsetKey, buffer);
        }
        return buffer.getCroisements();
    }

    @Override
    public SubsetItemPointeur getParentagePointeur(SubsetKey parentageSubsetKey) {
        if (!isParentageMapInit) {
            initParentageMap();
        }
        SubsetItemPointeur result = null;
        if (parentagePointeurMap != null) {
            result = parentagePointeurMap.get(parentageSubsetKey);
        }
        if (result == null) {
            throw new IllegalArgumentException("Not a parentage subset: " + parentageSubsetKey);
        }
        if (currentSubsetItem == null) {
            result.setCurrentSubsetItem(null);
        } else {
            result.setCurrentSubsetItem(currentSubsetItem.getId());
        }
        return result;
    }

    @Override
    public Object getCurrentObject(String objectName) {
        if (currentObjectMap == null) {
            return null;
        }
        return currentObjectMap.get(objectName);
    }

    @Override
    public void putCurrentObject(String objectName, Object object) {
        if (currentObjectMap == null) {
            currentObjectMap = new HashMap<String, Object>();
        }
        currentObjectMap.put(objectName, object);
    }

    @Override
    public Object getPointeurObject(String objectName) {
        if (pointeurObjectMap == null) {
            return null;
        }
        return pointeurObjectMap.get(objectName);
    }

    @Override
    public void putPointeurObject(String objectName, Object object) {
        if (pointeurObjectMap == null) {
            pointeurObjectMap = new HashMap<String, Object>();
        }
        pointeurObjectMap.put(objectName, object);
    }

    private void initParentageMap() {
        List<Corpus> corpusList;
        Subset masterSubset = null;
        if (mainSubset instanceof Corpus) {
            Corpus corpus = (Corpus) mainSubset;
            masterSubset = corpus.getMasterSubset();
            if (masterSubset != null) {
                corpusList = masterSubset.getSatelliteCorpusList();
            } else {
                corpusList = corpus.getSatelliteCorpusList();
            }
        } else {
            corpusList = mainSubset.getSatelliteCorpusList();
        }
        if ((masterSubset != null) || (!corpusList.isEmpty())) {
            parentagePointeurMap = new HashMap<SubsetKey, SubsetItemPointeur>();
            if (masterSubset != null) {
                parentagePointeurMap.put(masterSubset.getSubsetKey(), PointeurFactory.newSubsetItemPointeur(masterSubset));
            }
            for (Corpus corpus : corpusList) {
                parentagePointeurMap.put(corpus.getSubsetKey(), new FichePointeurImpl(corpus, false));
            }
        }
        isParentageMapInit = true;
    }


    private static class CroisementsBuffer {

        private final Subset otherSubset;
        private Croisements currentCroisements;
        private int currentId;

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

        private Croisements getCroisements() {
            return currentCroisements;
        }

        private void initToNull() {
            currentId = -1;
            currentCroisements = CroisementUtils.EMPTY_CROISEMENTS;
        }

        private void setCurrent(SubsetItem subsetItem) {
            if (subsetItem == null) {
                if (currentId != -1) {
                    initToNull();
                }
            } else {
                int newId = subsetItem.getId();
                if (newId != currentId) {
                    currentId = newId;
                    currentCroisements = otherSubset.getFichotheque().getCroisements(subsetItem, otherSubset);
                }
            }
        }

    }

}
