/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection.sets;

import net.ranides.assira.annotations.Meta;

import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Consumer;

/**
 * Fluent builder for maps.
 * It is parameterized by M type because can build different types of maps: HashMap, TreeMap, etc
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @param <K> key
 * @param <M> set
 */
public class SetBuilder<K, M extends Set<K>> {

    private final boolean array;

    private final M set;

    private SetBuilder(boolean array, M target) {
        this.array = array;
        this.set = target;
    }

    /**
     * Creates new builder which inserts all entries into provided map
     *
     * @param target target
     * @param <K> K
     * @param <M> M
     * @return this
     */
    public static <K, M extends Set<K>> SetBuilder<K,M> into(M target) {
        return new SetBuilder<>(false, target);
    }

    /**
     * Creates new builder which construct map with undefined order (some implementation of HashMap)
     *
     * @param <K> K
     * @return this
     */
    public static <K> SetBuilder<K, Set<K>> hash() {
        return new SetBuilder<>(false, new OpenSet<>());
    }

    /**
     * Creates new builder which construct map preserving order of insertions.
     *
     * @param <K> K
     * @return this
     */
    public static <K> SetBuilder<K, Set<K>> ordered() {
        return new SetBuilder<>(false, new LinkedHashSet<>());
    }

    /**
     * Creates new builder which construct map of strings preserving order of insertions.
     *
     * @return this
     */
    public static SetBuilder<String, Set<String>> dictionary() {
        return new SetBuilder<>(false, new LinkedHashSet<>());
    }

    /**
     * Creates new builder which construct TreeMap with default comparator
     * @param <K> K
     * @return this
     */
    public static <K extends Comparable<K>> SetBuilder<K, TreeSet<K>> tree() {
        return new SetBuilder<>(false, new TreeSet<>());
    }

    /**
     * Creates new builder which construct TreeMap with provided comparator
     *
     * @param cmp cmp
     * @param <K> K
     * @return this
     */
    public static <K> SetBuilder<K, TreeSet<K>> tree(Comparator<? super K> cmp) {
        return new SetBuilder<>(false, new TreeSet<>(cmp));
    }

    /**
     * Creates new builder which construct ArrayMap with default comparator
     *
     * @param <K> K
     * @return builder
     */
    public static <K extends Comparable<K>> SetBuilder<K, Set<K>> array() {
        return new SetBuilder<>(true, new TreeSet<>());
    }

    /**
     * Creates new builder which construct ArrayMap with provided comparator
     *
     * @param cmp cmp
     * @param <K> K
     * @return builder
     */
    public static <K> SetBuilder<K, Set<K>> array(Comparator<? super K> cmp) {
        return new SetBuilder<>(true, new TreeSet<>(cmp));
    }

    /**
     * Returns constructed map
     * Please note that returned by is still shared by builder
     *
     * @return map
     */
    @SuppressWarnings("unchecked")
    public M result() {
        return array ? (M)new ArraySet<>((SortedSet<K>)set) : set;
    }

    /**
     * Alias for {@link #result()}
     *
     * @return map
     */
    @Meta.Alias("result")
    public M set() {
        return result();
    }

    /**
     * Inserts new value to map
     *
     * @param key key
     * @return this
     */
    @Meta.Alias("add")
    public SetBuilder<K,M> append(K key) {
        return add(key);
    }

    /**
     * Inserts new values to map
     *
     * @param values values
     * @return this
     */
    @Meta.Alias("addAll")
    public SetBuilder<K,M> append(Collection<? extends K> values) {
        return addAll(values);
    }

    /**
     * Inserts new value to map
     *
     * @param key key
     * @return this
     */
    public SetBuilder<K,M> add(K key) {
        set.add(key);
        return this;
    }

    /**
     * Inserts new values to map
     *
     * @param values values
     * @return this
     */
    public SetBuilder<K,M> addAll(Collection<? extends K> values) {
        set.addAll(values);
        return this;
    }

    /**
     * Removes provided key
     *
     * @param key key
     * @return this
     */
    public SetBuilder<K,M> remove(Object key) {
        set.remove(key);
        return this;
    }

    /**
     * Removes all content from builder
     * @return this
     */
    public SetBuilder<K,M> clear() {
        set.clear();
        return this;
    }

    /**
     * Performs the given action for each entry in this map
     *
     * @param action action
     * @return this
     */
    public SetBuilder<K,M> forEach(Consumer<? super K> action) {
        set.forEach(action);
        return this;
    }

}
