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

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.text.CleanedString;


/**
 *
 * @author Vincent Calame
 */
public class AttributesBuilder implements AttributeBuffer {

    private final SortedMap<AttributeKey, AttributeBuilder> attributeBuilderMap = new TreeMap<AttributeKey, AttributeBuilder>();

    public AttributesBuilder() {
    }

    public AttributesBuilder(@Nullable Attributes initAttributes) {
        if (initAttributes != null) {
            for (Attribute attribute : initAttributes) {
                put(attribute);
            }
        }
    }

    public boolean containsValue(AttributeKey attributeKey) {
        AttributeBuilder builder = attributeBuilderMap.get(attributeKey);
        if (builder == null) {
            return false;
        }
        return !builder.isEmpty();
    }

    @Override
    public AttributesBuilder appendValue(AttributeKey attributeKey, String value) {
        return (AttributesBuilder) AttributeBuffer.super.appendValue(attributeKey, value);
    }

    @Override
    public AttributesBuilder appendValue(AttributeKey attributeKey, CleanedString cleanedString) {
        if (cleanedString == null) {
            return this;
        }
        AttributeBuilder builder = getOrCreate(attributeKey);
        builder.addValue(cleanedString);
        return this;
    }

    public AttributeBuilder getAttributeBuilder(AttributeKey attributeKey) {
        return getOrCreate(attributeKey);
    }

    public AttributesBuilder appendValues(AttributeKey attributeKey, List<CleanedString> valueList) {
        if (valueList.isEmpty()) {
            return this;
        }
        AttributeBuilder builder = getOrCreate(attributeKey);
        for (CleanedString cs : valueList) {
            if (cs != null) {
                builder.addValue(cs);
            }
        }
        return this;
    }

    public AttributesBuilder appendValues(Attribute attribute) {
        return appendValues(attribute.getAttributeKey(), attribute);
    }

    public AttributesBuilder appendValues(AttributeKey attributeKey, Attribute attribute) {
        AttributeBuilder builder = getOrCreate(attributeKey);
        int valueLength = attribute.size();
        for (int i = 0; i < valueLength; i++) {
            builder.addValue(CleanedString.newInstance(attribute.get(i)));
        }
        return this;
    }

    public AttributesBuilder changeAttributes(AttributeChange attributeChange) {
        for (Attribute attribute : attributeChange.getChangedAttributes()) {
            put(attribute);
        }
        for (AttributeKey removedAttributeKey : attributeChange.getRemovedAttributeKeyList()) {
            attributeBuilderMap.remove(removedAttributeKey);
        }
        return this;
    }

    public AttributesBuilder merge(AttributesBuilder attributesBuilder) {
        for (AttributeBuilder otherAttributeBuilder : attributesBuilder.attributeBuilderMap.values()) {
            appendValues(otherAttributeBuilder.toAttribute());
        }
        return this;
    }

    public Attributes toAttributes() {
        if (attributeBuilderMap.isEmpty()) {
            return AttributeUtils.EMPTY_ATTRIBUTES;
        }
        List<Attribute> attributeList = new ArrayList<Attribute>();
        for (AttributeBuilder builder : attributeBuilderMap.values()) {
            Attribute attribute = builder.toAttribute();
            if (attribute != null) {
                attributeList.add(attribute);
            }
        }
        int size = attributeList.size();
        if (size == 0) {
            return AttributeUtils.EMPTY_ATTRIBUTES;
        }
        if (size == 1) {
            return AttributeUtils.toSingletonAttributes(attributeList.get(0));
        }
        Attribute[] array = attributeList.toArray(new Attribute[size]);
        return new InternalAttributes(array);
    }

    private void put(Attribute attribute) {
        AttributeKey attributeKey = attribute.getAttributeKey();
        AttributeBuilder builder = new AttributeBuilder(attributeKey);
        for (String value : attribute) {
            builder.addValue(CleanedString.newInstance(value));
        }
        attributeBuilderMap.put(attributeKey, builder);
    }

    private AttributeBuilder getOrCreate(AttributeKey attributeKey) {
        AttributeBuilder builder = attributeBuilderMap.get(attributeKey);
        if (builder == null) {
            builder = new AttributeBuilder(attributeKey);
            attributeBuilderMap.put(attributeKey, builder);
        }
        return builder;
    }

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

    public static AttributesBuilder init(Attributes initAttributes) {
        return new AttributesBuilder(initAttributes);
    }

    static Attributes toAttributes(Attribute[] attributeArray) {
        return new InternalAttributes(attributeArray);
    }


    private static class InternalAttributes extends AbstractList<Attribute> implements Attributes {

        private final Attribute[] attributeArray;

        private InternalAttributes(Attribute[] attributeArray) {
            this.attributeArray = attributeArray;
        }

        @Override
        public Attribute getAttribute(AttributeKey key) {
            for (Attribute attribute : attributeArray) {
                if (attribute.getAttributeKey().equals(key)) {
                    return attribute;
                }
            }
            return null;
        }

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

        @Override
        public Attribute get(int i) {
            return attributeArray[i];
        }

    }

}
