/*
 * @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.collection.FlatCollection;
import net.ranides.assira.collection.iterators.IterableUtils;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Stream;

/**
 * Dynamic view with elements from merged collections.
 *
 * It supports removing already inserted elements (if member collection supports it).
 *
 * It does not support adding new elements (because it is unknown which collection should be modified).
 *
 * Method {@link #contains} complexity is proportional to number of merged sets.
 *
 * @param <T>
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class FlatSet<T> extends AbstractCollection<T> implements Set<T>, Serializable {

	private static final long serialVersionUID = 1L;

    private final Collection<Set<T>> content = new LinkedList<>();

    /**
     * Creates empty flat set
     */
    public FlatSet() {
        // do nothing
    }

    /**
     * Creates merged view of provided collections
     *
     * @param values values
     */
    @SafeVarargs
    public FlatSet(Set<T>... values) {
        joinAll(Arrays.asList(values));
    }

    /**
     * Creates merged view of provided collections
     *
     * @param values values
     */
    public FlatSet(Stream<Set<T>> values) {
        joinAll(values);
    }

    /**
     * Creates merged view of provided collections
     *
     * @param values values
     */
    public FlatSet(Collection<Set<T>> values) {
        joinAll(values);
    }

    /**
     * Creates merged view of provided collections
     *
     * @param values values
     * @param <T> type
     * @return view
     */
    @SafeVarargs
    public static <T> FlatSet<T> of(Set<T>... values) {
        return new FlatSet<T>(values);
    }

    /**
     * Inserts into view specified collection.
     * Note: It stores container, not actual elements from it.
     * @param set set
     * @return this
     */
    public FlatSet<T> join(Set<T> set) {
        content.add(set);
        return this;
    }

    /***
     * Inserts specified collections into view.
     * Note: It stores containers, not actual elements from them.
     * @param sets sets
     * @return this
     */
    public final FlatSet<T> joinAll(Collection<Set<T>> sets) {
        content.addAll(sets);
        return this;
    }

    /***
     * Inserts specified collections into view.
     * Note: It stores containers, not actual elements from them.
     * @param stream stream
     * @return this
     */
    public final FlatSet<T> joinAll(Stream<Set<T>> stream) {
        stream.forEachOrdered(content::add);
        return this;
    }

    @Override
    public int size() {
        int size = 0;
        for(Collection<T> list : content) {
            size += list.size();
        }
        return size;
    }

    @Override
    public boolean contains(Object value) {
        for(Collection<T> list : content) {
            if( list.contains(value) ) { return true; }
        }
        return false;
    }

    @Override
    public boolean remove(Object value) {
        for(Collection<T> c : content) {
            if(c.remove(value)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Iterator<T> iterator() {
        return IterableUtils.flat(content).iterator();
    }

    @Override
    public Object[] toArray() {
        return super.toArray();
    }

}
