package eu.dnetlib.miscutils.collections;

import java.util.*;

import com.google.common.collect.Lists;

import eu.dnetlib.miscutils.functional.UnaryFunction;

public class FilteredCollection<T> implements Iterable<T> {
	final transient private Iterable<T> coll;
	transient UnaryFunction<Boolean, T> filter;

	class FilterIterator implements Iterator<T> {
		final transient private Iterator<T> iter;

		transient T nextElement;

		public FilterIterator() {
			this.iter = coll.iterator();
			scanNext();
		}

		private void scanNext() {
			while (iter.hasNext()) {
				final T element = iter.next();
				if (filter.evaluate(element)) {
					nextElement = element;
					return;
				}
			}
			nextElement = null; // NOPMD
		}

		@Override
		public boolean hasNext() {
			return nextElement != null;
		}

		@Override
		public T next() {
			final T res = nextElement;
			scanNext();
			return res;
		}

		@Override
		public void remove() {
			// no operation
		}
	}

	/**
	 * Static helper function, makes usage shorter.
	 * 
	 * @param <X>
	 * @param coll
	 * @param filter
	 * @return
	 */
	public static <X> Iterable<X> filter(final Iterable<X> coll, final UnaryFunction<Boolean, X> filter) {
		return new FilteredCollection<X>(coll, filter);
	}
	
	public static <X> Iterable<X> filter(final X[] coll, final UnaryFunction<Boolean, X> filter) {
		return new FilteredCollection<X>(coll, filter);
	}
	
	/**
	 * Static helper function, makes common usage shorter.
	 * 
	 * @param <X>
	 * @param <Y>
	 * @param coll
	 * @param mapper
	 * @return
	 */
	public static <X> List<X> listFilter(final Iterable<X> coll, final UnaryFunction<Boolean, X> filter) {
		return Lists.newArrayList(filter(coll, filter));
	}
	
	public static <X> List<X> listFilter(final X[] coll, final UnaryFunction<Boolean, X> filter) {
		return Lists.newArrayList(filter(coll, filter));
	}
	
	public FilteredCollection(final Collection<T> coll, final UnaryFunction<Boolean, T> filter) {
		this.coll = coll;
		this.filter = filter;
	}
	
	public FilteredCollection(final Iterable<T> coll, final UnaryFunction<Boolean, T> filter) {
		this.coll = coll;
		this.filter = filter;
	}
	
	public FilteredCollection(final T[] coll, final UnaryFunction<Boolean, T> filter) {
		this.coll = Lists.newArrayList(coll);
		this.filter = filter;
	}


	@Override
	public Iterator<T> iterator() {
		return new FilterIterator();
	}

	public static void main(final String[] args) {
		final List<Object> list = new ArrayList<Object>();
		list.add("ciao");
		list.add(2);
		list.add("bubu");

		final TypeFilteredCollection<Object, String> filtered = new TypeFilteredCollection<Object, String>(list, String.class);

		for (final String el : filtered)
			System.out.println("element: " + el); // NOPMD

		System.out.println("ok"); // NOPMD
	}
}