package speiger.src.collections.PACKAGE.utils;

import speiger.src.collections.PACKAGE.collections.ITERABLE;
#if !TYPE_OBJECT
import speiger.src.collections.objects.collections.ObjectIterable;
import speiger.src.collections.objects.collections.ObjectIterator;
#endif
import speiger.src.collections.PACKAGE.collections.ITERATOR;
import speiger.src.collections.PACKAGE.functions.function.TO_OBJECT_FUNCTION;
import speiger.src.collections.PACKAGE.functions.function.PREDICATE;

/**
 * A Helper class for Iterables
 */
public class ITERABLES
{
	/**
	 * A Helper function that maps a Iterable into a new Type.
	 * @param iterable the iterable that should be mapped
	 * @param mapper the function that decides what the result turns into.
	 * @Type(T)
	 * @param <E> The return type.
	 * @return a iterable that is mapped to a new result
	 */
	public static GENERIC_KEY_SPECIAL_BRACES<E> ObjectIterable<E> map(ITERABLE KEY_GENERIC_TYPE iterable, TO_OBJECT_FUNCTION KKS_GENERIC_TYPE<E> mapper) {
		return new MappedIterable<>(iterable, mapper);
	}
	
	/**
	 * A Helper function that flatMaps a Iterable into a new Type.
	 * @param iterable the iterable that should be flatMapped
	 * @param mapper the function that decides what the result turns into.
	 * @Type(T)
	 * @param <V> The return type supplier.
	 * @param <E> The return type.
	 * @return a iterable that is flatMapped to a new result
	 */
	public static GENERIC_KEY_SPECIAL_BRACES<E, V extends Iterable<E>> ObjectIterable<E> flatMap(ITERABLE KEY_GENERIC_TYPE iterable, TO_OBJECT_FUNCTION KKS_GENERIC_TYPE<V> mapper) {
		return new FlatMappedIterable<>(iterable, mapper);
	}
	
	/**
	 * A Helper function that flatMaps a Iterable into a new Type.
	 * @param iterable the iterable that should be flatMapped
	 * @param mapper the function that decides what the result turns into.
	 * @Type(T)
	 * @param <E> The return type.
	 * @return a iterable that is flatMapped to a new result
	 */
	public static GENERIC_KEY_SPECIAL_BRACES<E> ObjectIterable<E> arrayFlatMap(ITERABLE KEY_GENERIC_TYPE iterable, TO_OBJECT_FUNCTION KKS_GENERIC_TYPE<E[]> mapper) {
		return new FlatMappedArrayIterable<>(iterable, mapper);
	}
	
	/**
	 * A Helper function that filters out all desired elements
	 * @param iterable that should be filtered.
	 * @param filter the filter that decides that should be let through
	 * @Type(T)
	 * @return a filtered iterable
	 */
	public static GENERIC_KEY_BRACES ITERABLE KEY_GENERIC_TYPE filter(ITERABLE KEY_GENERIC_TYPE iterable, PREDICATE KEY_GENERIC_TYPE filter) {
		return new FilteredIterableBRACES(iterable, filter);
	}
	
	private static class MappedIterable KSS_GENERIC_TYPE<E, T> implements ObjectIterable<T>
	{
		ITERABLE KEY_SPECIAL_GENERIC_TYPE<E> iterable;
		TO_OBJECT_FUNCTION KSS_GENERIC_TYPE<E, T> mapper;
		
		MappedIterable(ITERABLE KEY_SPECIAL_GENERIC_TYPE<E> iterable, TO_OBJECT_FUNCTION KSS_GENERIC_TYPE<E, T> mapper) {
			this.iterable = iterable;
			this.mapper = mapper;
		}
		
		public ObjectIterator<T> iterator() {
			return ITERATORS.map(iterable.iterator(), mapper);
		}
	}
	
	private static class FlatMappedIterable KSS_GENERIC_TYPE<E, T,[SPACE]V extends Iterable<T>> implements ObjectIterable<T>
	{
		ITERABLE KEY_SPECIAL_GENERIC_TYPE<E> iterable;
		TO_OBJECT_FUNCTION KSS_GENERIC_TYPE<E, V> mapper;
		
		FlatMappedIterable(ITERABLE KEY_SPECIAL_GENERIC_TYPE<E> iterable, TO_OBJECT_FUNCTION KSS_GENERIC_TYPE<E, V> mapper) {
			this.iterable = iterable;
			this.mapper = mapper;
		}
		
		@Override
		public ObjectIterator<T> iterator() {
			return ITERATORS.flatMap(iterable.iterator(), mapper);
		}
	}
	
	private static class FlatMappedArrayIterable KSS_GENERIC_TYPE<E, T> implements ObjectIterable<T>
	{
		ITERABLE KEY_SPECIAL_GENERIC_TYPE<E> iterable;
		TO_OBJECT_FUNCTION KSS_GENERIC_TYPE<E, T[]> mapper;
		
		FlatMappedArrayIterable(ITERABLE KEY_SPECIAL_GENERIC_TYPE<E> iterable, TO_OBJECT_FUNCTION KSS_GENERIC_TYPE<E, T[]> mapper) {
			this.iterable = iterable;
			this.mapper = mapper;
		}
		
		@Override
		public ObjectIterator<T> iterator() {
			return ITERATORS.arrayFlatMap(iterable.iterator(), mapper);
		}
	}
	
	private static class FilteredIterable KEY_GENERIC_TYPE implements ITERABLE KEY_GENERIC_TYPE
	{
		ITERABLE KEY_GENERIC_TYPE iterable;
		PREDICATE KEY_GENERIC_TYPE filter;
		
		public FilteredIterable(ITERABLE KEY_GENERIC_TYPE iterable, PREDICATE KEY_GENERIC_TYPE filter) {
			this.iterable = iterable;
			this.filter = filter;
		}
		
		@Override
		public ITERATOR KEY_GENERIC_TYPE iterator() {
			return ITERATORS.filter(iterable.iterator(), filter);
		}
	}
}