package speiger.src.collections.PACKAGE.utils;

import java.util.Collection;
import java.util.function.Predicate;
#if PRIMITIVES
import java.util.function.JAVA_PREDICATE;
#endif
import java.util.function.Consumer;

import speiger.src.collections.PACKAGE.collections.ABSTRACT_COLLECTION;
import speiger.src.collections.PACKAGE.collections.COLLECTION;
import speiger.src.collections.PACKAGE.collections.ITERATOR;
import speiger.src.collections.objects.utils.ObjectArrays;
#if !TYPE_OBJECT
import speiger.src.collections.PACKAGE.functions.CONSUMER;
import speiger.src.collections.PACKAGE.utils.ARRAYS;
#endif

/**
 * A Helper class for Collections
 */
public class COLLECTIONS
{
	/**
	 * Empty Collection Reference
	 */
	public static final COLLECTION NO_GENERIC_TYPE EMPTY = new EmptyCollectionBRACES();
	
	/**
	 * Returns a Immutable EmptyCollection instance that is automatically casted.
	 * @Type(T)
	 * @return an empty collection
	 */
	public static GENERIC_KEY_BRACES COLLECTION KEY_GENERIC_TYPE empty() {
#if TYPE_OBJECT
		return (COLLECTION<KEY_TYPE>)EMPTY;
#else
		return EMPTY;
#endif
	}
	
	/**
	 * Returns a Immutable Collection instance based on the instance given.
	 * @param c that should be made immutable/unmodifiable
	 * @Type(T)
	 * @return a unmodifiable collection wrapper. If the Collection already a unmodifiable wrapper then it just returns itself.
	 */
	public static GENERIC_KEY_BRACES COLLECTION KEY_GENERIC_TYPE unmodifiable(COLLECTION KEY_GENERIC_TYPE c) {
		return c instanceof UnmodifiableCollection ? c : new UnmodifiableCollectionBRACES(c);
	}
	
	/**
	 * Returns a synchronized Collection instance based on the instance given.
	 * @param c that should be synchronized
	 * @Type(T)
	 * @return a synchronized collection wrapper. If the Collection already a synchronized wrapper then it just returns itself.
	 */
	public static GENERIC_KEY_BRACES COLLECTION KEY_GENERIC_TYPE synchronize(COLLECTION KEY_GENERIC_TYPE c) {
		return c instanceof SynchronizedCollection ? c : new SynchronizedCollectionBRACES(c);
	}
	
	/**
	 * Returns a synchronized Collection instance based on the instance given.
	 * @param c that should be synchronized
	 * @param mutex is the controller of the synchronization block.
	 * @Type(T)
	 * @return a synchronized collection wrapper. If the Collection already a synchronized wrapper then it just returns itself.
	 */
	public static GENERIC_KEY_BRACES COLLECTION KEY_GENERIC_TYPE synchronize(COLLECTION KEY_GENERIC_TYPE c, Object mutex) {
		return c instanceof SynchronizedCollection ? c : new SynchronizedCollectionBRACES(c, mutex);
	}
	
	/**
	 * Synchronized Collection Wrapper for the synchronizedCollection function
	 * @Type(T)
	 */
	public static class SynchronizedCollection KEY_GENERIC_TYPE implements COLLECTION KEY_GENERIC_TYPE {
		COLLECTION KEY_GENERIC_TYPE c;
		protected Object mutex;
		
		SynchronizedCollection(COLLECTION KEY_GENERIC_TYPE c) {
			this.c = c;
			mutex = this;
		}
		
		SynchronizedCollection(COLLECTION KEY_GENERIC_TYPE c, Object mutex) {
			this.c = c;
			this.mutex = mutex;
		}
		
		@Override
		public boolean add(KEY_TYPE o) { synchronized(mutex) { return c.add(o); } }
		@Override
		public boolean addAll(Collection<? extends CLASS_TYPE> c) { synchronized(mutex) { return this.c.addAll(c); } }
		
		@Override
		public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { synchronized(mutex) { return this.c.addAll(c); } }
		
#if !TYPE_OBJECT
		@Override
		public boolean contains(KEY_TYPE o) { synchronized(mutex) { return c.contains(o); } }
		
#else
		@Override
		public boolean contains(Object o) { synchronized(mutex) { return c.contains(o); } }
		
#endif
		@Override
		@Primitive
		public boolean containsAll(Collection<?> c) { synchronized(mutex) { return this.c.containsAll(c); } }
		
		@Override
		@Primitive
		public boolean containsAny(Collection<?> c) { synchronized(mutex) { return this.c.containsAny(c); } }
		
		@Override
		public boolean containsAll(COLLECTION KEY_GENERIC_TYPE c) { synchronized(mutex) { return this.c.containsAll(c); } }
		
		@Override
		public boolean containsAny(COLLECTION KEY_GENERIC_TYPE c) { synchronized(mutex) { return this.c.containsAny(c); } }
		
		@Override
		public int size() { synchronized(mutex) { return c.size(); } }
		
		@Override
		public boolean isEmpty() { synchronized(mutex) { return c.isEmpty(); } }
		
		@Override
		public ITERATOR KEY_GENERIC_TYPE iterator() {
			return c.iterator();
		}
		
		@Override
		@Primitive
		public boolean remove(Object o) { synchronized(mutex) { return c.remove(o); } }
		@Override
		@Primitive
		public boolean removeAll(Collection<?> c) { synchronized(mutex) { return this.c.removeAll(c); } }
		@Override
		@Primitive
		public boolean retainAll(Collection<?> c) { synchronized(mutex) { return this.c.retainAll(c); } }
#if !TYPE_OBJECT
		@Override
		public boolean REMOVE_KEY(KEY_TYPE o) { synchronized(mutex) { return c.REMOVE_KEY(o); } }
#endif
		@Override
		public boolean removeAll(COLLECTION KEY_GENERIC_TYPE c) { synchronized(mutex) { return this.c.removeAll(c); } }
		@Override
		public boolean retainAll(COLLECTION KEY_GENERIC_TYPE c) { synchronized(mutex) { return this.c.retainAll(c); } }
#if PRIMITIVES
		@Override
		public boolean remIf(JAVA_PREDICATE filter){ synchronized(mutex) { return c.remIf(filter); } }
#endif
		@Override
		public void clear() { synchronized(mutex) { c.clear(); } }
		@Override
		public Object[] toArray() { synchronized(mutex) { return c.toArray(); } }
#if !TYPE_OBJECT
		@Override
		public <T> T[] toArray(T[] a) { synchronized(mutex) { return c.toArray(a); } }
		@Override
		public KEY_TYPE[] TO_ARRAY() { synchronized(mutex) { return c.TO_ARRAY(); } }
		@Override
		public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { synchronized(mutex) { return c.TO_ARRAY(a); } }
		@Override
		public void forEach(CONSUMER action) { synchronized(mutex) { c.forEach(action); } }
		@Override
		@Deprecated
		public void forEach(Consumer<? super CLASS_TYPE> action) { synchronized(mutex) { c.forEach(action); } }
#else
		@Override
		public <E> E[] toArray(E[] a) { synchronized(mutex) { return c.toArray(a); } }
		@Override
		public void forEach(Consumer<? super CLASS_TYPE> action) { synchronized(mutex) { c.forEach(action); } }
#endif
		@Override
		public int hashCode() { synchronized(mutex) { return c.hashCode(); } }
		@Override
		public boolean equals(Object obj) { synchronized(mutex) { return c.equals(obj); } }
		@Override
		public String toString() { synchronized(mutex) { return c.toString(); } }
	}
	
	/**
	 * Unmodifyable Collection Wrapper for the unmodifyableCollection method
	 * @Type(T)
	 */
	public static class UnmodifiableCollection KEY_GENERIC_TYPE implements COLLECTION KEY_GENERIC_TYPE {
		COLLECTION KEY_GENERIC_TYPE c;
		
		UnmodifiableCollection(COLLECTION KEY_GENERIC_TYPE c) {
			this.c = c;
		}
		
		@Override
		public boolean add(KEY_TYPE o) { throw new UnsupportedOperationException(); }
		@Override
		public boolean addAll(Collection<? extends CLASS_TYPE> c) { throw new UnsupportedOperationException(); }
		@Override
		public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { throw new UnsupportedOperationException(); }
#if !TYPE_OBJECT
		@Override
		public boolean contains(KEY_TYPE o) { return c.contains(o); }
#else
		@Override
		public boolean contains(Object o) { return c.contains(o); }
#endif
		@Override
		public boolean containsAll(COLLECTION KEY_GENERIC_TYPE c) { return this.c.containsAll(c); }
		@Override
		public boolean containsAny(COLLECTION KEY_GENERIC_TYPE c) { return this.c.containsAny(c); }
		@Override
		@Primitive
		public boolean containsAny(Collection<?> c) { return this.c.containsAny(c); }
		@Override
		@Primitive
		public boolean containsAll(Collection<?> c) { return this.c.containsAll(c); }
		@Override
		public int size() { return c.size(); }
		@Override
		public boolean isEmpty() { return c.isEmpty(); }
		@Override
		public ITERATOR KEY_GENERIC_TYPE iterator() { return ITERATORS.unmodifiable(c.iterator()); }
		@Override
		@Deprecated
		public boolean remove(Object o) { throw new UnsupportedOperationException(); }
		@Override
		@Primitive
		public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(); }
		@Override
		@Primitive
		public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); }
		@Override
		@Primitive
		public boolean removeIf(Predicate<? super CLASS_TYPE> filter) { throw new UnsupportedOperationException(); }
#if !TYPE_OBJECT
		@Override
		public boolean REMOVE_KEY(KEY_TYPE o) { throw new UnsupportedOperationException(); }
#endif
		@Override
		public boolean removeAll(COLLECTION KEY_GENERIC_TYPE c) { throw new UnsupportedOperationException(); }
		@Override
		public boolean retainAll(COLLECTION KEY_GENERIC_TYPE c) { throw new UnsupportedOperationException(); }
#if PRIMITIVES
		@Override
		public boolean remIf(JAVA_PREDICATE filter){ throw new UnsupportedOperationException(); }
#endif
		@Override
		public void clear() { throw new UnsupportedOperationException(); }
		@Override
		public Object[] toArray() { return c.toArray(); }
#if !TYPE_OBJECT
		@Override
		public <T> T[] toArray(T[] a) { return c.toArray(a); }
		@Override
		public KEY_TYPE[] TO_ARRAY() { return c.TO_ARRAY(); }
		@Override
		public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { return c.TO_ARRAY(a); }
		@Override
		public void forEach(CONSUMER action) { c.forEach(action); }
		@Override
		@Deprecated
		public void forEach(Consumer<? super CLASS_TYPE> action) { c.forEach(action); }
#else
		@Override
		public <E> E[] toArray(E[] a) { return c.toArray(a); }
		@Override
		public void forEach(Consumer<? super CLASS_TYPE> action) { c.forEach(action); }
#endif
		@Override
		public int hashCode() { return c.hashCode(); }
		@Override
		public boolean equals(Object obj) { return c.equals(obj); }
		@Override
		public String toString() { return c.toString(); }
	}
	
	/**
	 * Empty Collection implementation for the empty collection function
	 * @Type(T)
	 */
	public static class EmptyCollection KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION KEY_GENERIC_TYPE {
		@Override
		public boolean add(KEY_TYPE o) { throw new UnsupportedOperationException(); }
		
		@Override
		public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { throw new UnsupportedOperationException(); }
		
#if !TYPE_OBJECT
		@Override
		public boolean contains(KEY_TYPE o) { return false; }
		@Override
		public boolean containsAll(COLLECTION KEY_GENERIC_TYPE c) { return false; }
		@Override
		public boolean containsAny(COLLECTION KEY_GENERIC_TYPE c) { return false; }
#else
		@Override
		public boolean contains(Object o) { return false; }
#endif
		@Override
		@Primitive
		public boolean containsAny(Collection<?> c) { return false; }
		@Override
		@Primitive
		public boolean containsAll(Collection<?> c) { return false; }
		@Override
		public int hashCode() { return 0; }
		
		@Override
		public boolean equals(Object o) {
			if(o == this) return true;
		  	if(!(o instanceof Collection)) return false;
		  	return ((Collection<?>)o).isEmpty();
		}
		
		@Override
		@Deprecated
		public boolean remove(Object o) { throw new UnsupportedOperationException(); }
		@Override
		@Primitive
		public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(); }
		@Override
		@Primitive
		public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); }
		@Override
		@Primitive
		public boolean removeIf(Predicate<? super CLASS_TYPE> filter) { throw new UnsupportedOperationException(); }
#if !TYPE_OBJECT
		@Override
		public boolean REMOVE_KEY(KEY_TYPE o) { throw new UnsupportedOperationException(); }
#endif
		@Override
		public boolean removeAll(COLLECTION KEY_GENERIC_TYPE c) { throw new UnsupportedOperationException(); }
		@Override
		public boolean retainAll(COLLECTION KEY_GENERIC_TYPE c) { throw new UnsupportedOperationException(); }
#if PRIMITIVES
		@Override
		public boolean remIf(JAVA_PREDICATE filter){ throw new UnsupportedOperationException(); }
#endif
		@Override
		public Object[] toArray() { return ObjectArrays.EMPTY_ARRAY; }
#if !TYPE_OBJECT
		@Override
		public <T> T[] toArray(T[] a) { return a; }
		@Override
		public KEY_TYPE[] TO_ARRAY() { return ARRAYS.EMPTY_ARRAY; }
		@Override
		public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { return a; }
#else
		@Override
		public <E> E[] toArray(E[] a) { return a; }
#endif
		@Override
		public ITERATOR KEY_GENERIC_TYPE iterator() { return ITERATORS.empty(); }
		@Override
		public void clear() {}
		@Override
		public int size() { return 0; }
	}
}