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

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).
 *
 * @param <T> T
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class FlatCollection<T> extends AbstractCollection<T> implements Serializable {

	private static final long serialVersionUID = 1L;

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

    /**
     * Creates empty flat collection
     */
    public FlatCollection() {
        // do nothing
    }

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

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

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

    /**
     * Creates merged view of provided collections
     * @param values values
     * @param <T> T
     * @return view
     */
    @SafeVarargs
    public static <T> FlatCollection<T> of(Collection<T>... values) {
        return new FlatCollection<T>(values);
    }
    /**
     * Inserts into view specified collection.
     * Please note that it stores container, not actual elements from it.
     *
     * @param list list
     * @return this
     */
    public FlatCollection<T> join(Collection<T> list) {
        content.add(list);
        return this;
    }

    /***
     * Inserts specified collections into view.
     * Please note it stores containers, not actual elements from them.
     *
     * @param list list
     * @return this
     */
    public final FlatCollection<T> joinAll(Collection<Collection<T>> list) {
        content.addAll(list);
        return this;
    }

    /***
     * Inserts specified collections into view.
     * Please note it stores containers, not actual elements from them.
     *
     * @param stream stream
     * @return this
     */
    public final FlatCollection<T> joinAll(Stream<Collection<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();
    }

}
