/* UtilLib - Copyright (c) 2011-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.mapeadores.util.text;

import java.util.AbstractList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LangsUtils;


/**
 *
 * @author Vincent Calame
 */
public class LabelChangeBuilder {

    private final Set<Lang> removedLangSet = new LinkedHashSet<Lang>();
    private final Map<Lang, Label> labelMap = new LinkedHashMap<Lang, Label>();

    public LabelChangeBuilder() {

    }

    public LabelChangeBuilder putLabels(Labels labels) {
        for (Label label : labels) {
            putLabel(label);
        }
        return this;
    }

    public LabelChangeBuilder putLabel(Label label) {
        Lang lang = label.getLang();
        labelMap.put(lang, label);
        removedLangSet.remove(lang);
        return this;
    }

    public Label putLabel(Lang lang, CharSequence charSequence) {
        return putLabel(lang, CleanedString.newInstance(charSequence));
    }

    public Label putLabel(Lang lang, CleanedString cleanedString) {
        if (cleanedString == null) {
            removedLangSet.add(lang);
            labelMap.remove(lang);
            return null;
        } else {
            Label label = LabelUtils.toLabel(lang, cleanedString);
            labelMap.put(lang, label);
            removedLangSet.remove(lang);
            return label;
        }
    }

    public int getAddedLabelCount() {
        return labelMap.size();
    }

    public Label getLabel(Lang lang) {
        return labelMap.get(lang);
    }

    public void putRemovedLang(Lang lang) {
        removedLangSet.add(lang);
        labelMap.remove(lang);
    }

    public void merge(LabelChangeBuilder otherLabelChangeBuilder) {
        for (Lang otherLang : otherLabelChangeBuilder.removedLangSet) {
            if (!labelMap.containsKey(otherLang)) {
                removedLangSet.add(otherLang);
            }
        }
        for (Map.Entry<Lang, Label> entry : otherLabelChangeBuilder.labelMap.entrySet()) {
            Lang otherLang = entry.getKey();
            if (!labelMap.containsKey(otherLang)) {
                labelMap.put(otherLang, entry.getValue());
                removedLangSet.remove(otherLang);
            }
        }
    }

    public void clear() {
        removedLangSet.clear();
        labelMap.clear();
    }

    public LabelChangeBuilder filterRealChanges(Labels currentLabels, LabelChange labelChange) {
        for (Label label : labelChange.getChangedLabels()) {
            Label currentLabel = currentLabels.getLabel(label.getLang());
            if ((currentLabel == null) || (!currentLabel.getLabelString().equals(label.getLabelString()))) {
                putLabel(label);
            }
        }
        for (Lang lang : labelChange.getRemovedLangList()) {
            if (currentLabels.getLabel(lang) != null) {
                putRemovedLang(lang);
            }
        }
        return this;
    }

    public LabelChange toLabelChange() {
        List<Lang> removedLangList = LangsUtils.wrap(removedLangSet);
        return new InternalLabelChange(toLabels(), removedLangList);
    }

    public Labels toLabels() {
        return LabelUtils.toLabels(labelMap);
    }

    public Phrase toPhrase(String name) {
        if (name == null) {
            throw new IllegalArgumentException("name is null");
        }
        Label[] labelArray = labelMap.values().toArray(new Label[labelMap.size()]);
        return new InternalPhrase(name, new HashMap<Lang, Label>(labelMap), labelArray);
    }

    public static LabelChangeBuilder init() {
        return new LabelChangeBuilder();
    }


    private static class InternalLabelChange implements LabelChange {

        private final Labels changedLabels;
        private final List<Lang> removedLangList;

        private InternalLabelChange(Labels changedLabels, List<Lang> removedLangList) {
            this.changedLabels = changedLabels;
            this.removedLangList = removedLangList;
        }

        @Override
        public Labels getChangedLabels() {
            return changedLabels;
        }

        @Override
        public List<Lang> getRemovedLangList() {
            return removedLangList;
        }

    }


    private static class InternalPhrase extends AbstractList<Label> implements Phrase {

        private final String name;
        private final Label[] labelArray;
        private final Map<Lang, Label> labelMap;

        private InternalPhrase(String name, Map<Lang, Label> labelMap, Label[] labelArray) {
            this.name = name;
            this.labelArray = labelArray;
            this.labelMap = labelMap;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public Label getLabel(Lang lang) {
            return labelMap.get(lang);
        }

        @Override
        public int size() {
            return labelArray.length;
        }

        @Override
        public Label get(int index) {
            return labelArray[index];
        }

    }

}
