/* BdfServer - Copyright (c) 2015-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 fr.exemole.bdfserver.impl;

import fr.exemole.bdfserver.api.ui.CommentUi;
import fr.exemole.bdfserver.api.ui.FieldUi;
import fr.exemole.bdfserver.api.ui.SpecialIncludeUi;
import fr.exemole.bdfserver.api.ui.SubsetIncludeUi;
import fr.exemole.bdfserver.api.ui.UiComponent;
import fr.exemole.bdfserver.api.ui.UiComponents;
import fr.exemole.bdfserver.tools.ui.UiUtils;
import fr.exemole.bdfserver.tools.ui.components.FieldUiBuilder;
import fr.exemole.bdfserver.tools.ui.components.IncludeUiBuilder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.fichotheque.Fichotheque;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.CorpusMetadata;
import net.fichotheque.corpus.metadata.FieldKey;


/**
 *
 * @author Vincent Calame
 */
class UiComponentsImpl implements UiComponents {

    private final Map<String, UiComponent> componentUiMap = new HashMap<String, UiComponent>();
    private final Corpus corpus;
    private final List<UiComponent> componentUiList = new ArrayList<UiComponent>();
    private List<UiComponent> cacheList;

    UiComponentsImpl(Corpus corpus) {
        this.corpus = corpus;
    }

    SubsetKey getSubsetKey() {
        return corpus.getSubsetKey();
    }

    @Override
    public List<UiComponent> getUiComponentList() {
        List<UiComponent> result = cacheList;
        if (result == null) {
            result = initCacheList();
        }
        return result;
    }

    @Override
    public UiComponent getUiComponent(String keyString) {
        return componentUiMap.get(keyString);
    }


    boolean setPositionArray(String[] positionArray) {
        List<UiComponent> newList = new ArrayList<UiComponent>();
        Map<String, Integer> duplicateMap = new HashMap<String, Integer>();
        for (String token : positionArray) {
            UiComponent componentUi = componentUiMap.get(token);
            if (componentUi != null) {
                if (!duplicateMap.containsKey(token)) {
                    newList.add(componentUi);
                    duplicateMap.put(token, 1);
                } else {
                    if (componentUi instanceof CommentUi) {
                        int number = duplicateMap.get(token);
                        number++;
                        newList.add(UiUtils.cloneCommentUi((CommentUi) componentUi, number));
                        duplicateMap.put(token, number);
                    }
                }
            }
        }
        for (UiComponent componentUi : componentUiList) {
            String token = componentUi.getName();
            if (!duplicateMap.containsKey(token)) {
                newList.add(componentUi);
            }
        }
        componentUiList.clear();
        componentUiList.addAll(newList);
        clearCache();
        return true;
    }

    void populate(Fichotheque fichotheque, CorpusMetadata corpusMetadata, UiComponents mainUiComponents) {
        Map<String, Integer> cloneMap = new HashMap<String, Integer>();
        for (UiComponent uiComponent : mainUiComponents.getUiComponentList()) {
            if (uiComponent instanceof FieldUi) {
                FieldUi fieldUi = (FieldUi) uiComponent;
                if (corpusMetadata.getCorpusField(fieldUi.getFieldKey()) != null) {
                    add(fieldUi);
                }
            } else if (uiComponent instanceof SubsetIncludeUi) {
                if (fichotheque.containsSubset(((SubsetIncludeUi) uiComponent).getSubsetKey())) {
                    add(uiComponent);
                }
            } else if (uiComponent instanceof CommentUi) {
                String name = uiComponent.getName();
                if (!cloneMap.containsKey(name)) {
                    add(uiComponent);
                    cloneMap.put(name, 1);
                } else {
                    int number = cloneMap.get(name);
                    number++;
                    componentUiList.add(UiUtils.cloneCommentUi((CommentUi) uiComponent, number));
                    cloneMap.put(name, number);
                }
            } else {
                add(uiComponent);
            }
        }
        clearCache();
    }

    void add(UiComponent componentUi) {
        String name = componentUi.getName();
        if (name.equals(FieldKey.SPECIAL_ID)) {
            return;
        }
        componentUiMap.put(name, componentUi);
        componentUiList.add(componentUi);
        clearCache();
    }

    boolean removeComponentUi(String key, boolean allowsFieldKeyRemove) {
        UiComponent componentUi = (UiComponent) componentUiMap.get(key);
        if (componentUi == null) {
            return false;
        }
        if (componentUi instanceof FieldUi) {
            if (!allowsFieldKeyRemove) {
                return false;
            }
        }
        componentUiMap.remove(key);
        componentUiList.remove(componentUi);
        if (componentUi instanceof CommentUi) {
            while (true) {
                boolean done = componentUiList.remove(componentUi);
                if (!done) {
                    break;
                }
            }
        }
        clearCache();
        return true;
    }

    boolean putComponentUi(UiComponent componentUi) {
        String key = componentUi.getName();
        if (key.equals(FieldKey.SPECIAL_ID)) {
            return false;
        }
        if (!componentUiMap.containsKey(key)) {
            componentUiList.add(componentUi);
        } else {
            int size = componentUiList.size();
            for (int i = 0; i < size; i++) {
                UiComponent otherComponentUi = componentUiList.get(i);
                if (otherComponentUi.getName().equals(key)) {
                    componentUiList.set(i, componentUi);
                }
            }
        }
        componentUiMap.put(key, componentUi);
        clearCache();
        return true;
    }


    void checkCorpus() {
        CorpusMetadata corpusMetadata = corpus.getCorpusMetadata();
        checkField(corpusMetadata, FieldKey.TITLE);
        checkField(corpusMetadata, FieldKey.SUBTITLE);
        checkField(corpusMetadata, FieldKey.OWNERS);
        for (CorpusField corpusField : corpusMetadata.getPropList()) {
            checkCorpusField(corpusField);
        }
        for (CorpusField corpusField : corpusMetadata.getInfoList()) {
            checkCorpusField(corpusField);
        }
        for (CorpusField corpusField : corpusMetadata.getSectionList()) {
            checkCorpusField(corpusField);
        }
        checkField(corpusMetadata, FieldKey.LANG);
    }

    boolean checkParentage() {
        if ((corpus.getMasterSubset() != null) || (!corpus.getSatelliteCorpusList().isEmpty())) {
            return checkSpecialIncludeUi(FichothequeConstants.PARENTAGE_NAME);
        } else if (componentUiMap.containsKey(FichothequeConstants.PARENTAGE_NAME)) {
            removeComponentUi(FichothequeConstants.PARENTAGE_NAME, true);
            return true;
        } else {
            return false;
        }
    }

    boolean removeAllSubsetInclude(SubsetKey subsetKey) {
        boolean done = false;
        for (Iterator<UiComponent> it = componentUiList.iterator(); it.hasNext();) {
            UiComponent uiComponent = it.next();
            if (uiComponent instanceof SubsetIncludeUi) {
                if (((SubsetIncludeUi) uiComponent).getSubsetKey().equals(subsetKey)) {
                    it.remove();
                    componentUiMap.remove(uiComponent.getName());
                    done = true;
                }
            }
        }
        clearCache();
        return done;
    }

    private void checkField(CorpusMetadata corpusMetadata, FieldKey fieldKey) {
        CorpusField corpusField = corpusMetadata.getCorpusField(fieldKey);
        if (corpusField != null) {
            checkCorpusField(corpusField);
        }
    }

    private void checkCorpusField(CorpusField corpusField) {
        FieldKey fieldKey = corpusField.getFieldKey();
        if (!componentUiMap.containsKey(fieldKey.getKeyString())) {
            FieldUi fieldUi = FieldUiBuilder.init(fieldKey).toFieldUi();
            componentUiList.add(fieldUi);
            componentUiMap.put(fieldKey.getKeyString(), fieldUi);
        }
        clearCache();
    }

    boolean checkSpecialIncludeUi(String name) {
        if (!componentUiMap.containsKey(name)) {
            SpecialIncludeUi includeUi = (SpecialIncludeUi) IncludeUiBuilder.initSpecial(name).toIncludeUi();
            componentUiList.add(includeUi);
            componentUiMap.put(name, includeUi);
            clearCache();
            return true;
        } else {
            return false;
        }
    }

    private void clearCache() {
        cacheList = null;
    }

    private synchronized List<UiComponent> initCacheList() {
        List<UiComponent> immutableList = UiUtils.wrap(componentUiList.toArray(new UiComponent[componentUiList.size()]));
        cacheList = immutableList;
        return immutableList;
    }


}
