package uk.co.webamoeba.mockito.collections.inject;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Set;

import org.mockito.Mock;

import uk.co.webamoeba.mockito.collections.annotation.CollectionOfMocks;
import uk.co.webamoeba.mockito.collections.exception.MockitoCollectionsException;
import uk.co.webamoeba.mockito.collections.util.AnnotatedFieldRetriever;
import uk.co.webamoeba.mockito.collections.util.FieldValueMutator;
import uk.co.webamoeba.mockito.collections.util.GenericCollectionTypeResolver;
import uk.co.webamoeba.mockito.collections.util.HashOrderedSet;
import uk.co.webamoeba.mockito.collections.util.OrderedSet;

/**
 * The {@link CollectionOfMocksInitialiser} is responsible for handling the instantiation of {@link Collection
 * Collections} and Mockito {@link Mock}s within those {@link Collection Collections} on {@link Field Fields} annotated
 * with the {@link CollectionOfMocks} annotation.
 * 
 * @author James Kennard
 */
public class CollectionOfMocksInitialiser {

	private AnnotatedFieldRetriever annotatedFieldRetriever;

	private GenericCollectionTypeResolver genericCollectionTypeResolver;

	private CollectionFactory collectionFactory;

	private MockStrategy mockStrategy;

	public CollectionOfMocksInitialiser(AnnotatedFieldRetriever annotatedFieldRetriever,
			GenericCollectionTypeResolver genericCollectionTypeResolver, CollectionFactory collectionFactory,
			MockStrategy mockStrategy) {
		this.annotatedFieldRetriever = annotatedFieldRetriever;
		this.genericCollectionTypeResolver = genericCollectionTypeResolver;
		this.collectionFactory = collectionFactory;
		this.mockStrategy = mockStrategy;
	}

	/**
	 * Initialises an {@link Object}, generally anticipated to be a test class, with {@link Collection Collections} for
	 * {@link Field Fields} annotated with the {@link CollectionOfMocks} annotation.
	 * 
	 * @param object
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void initialise(Object object) {
		Set<Field> fields = annotatedFieldRetriever.getAnnotatedFields(object.getClass(), CollectionOfMocks.class);
		// TODO tidy this up, looks like a nasty big block of code...
		for (Field field : fields) {
			Type type = field.getGenericType();
			ParameterizedType parameterizedType = (ParameterizedType) type; // FIXME ensure is paramerterized type
			// should be safe, ParamerterizedType should only ever return a Class from this method
			Class rawType = (Class) parameterizedType.getRawType();
			Class collectionType = genericCollectionTypeResolver.getCollectionFieldType(field); // FIXME null check
			CollectionOfMocks annotation = field.getAnnotation(CollectionOfMocks.class);
			assert annotation != null : "Field is missing CollectionOfMocks annotation, unexpected field retrieved from annotatedFieldRetriever";
			OrderedSet<?> mocks = createMocks(collectionType, getNumberOfMocks(annotation));
			Collection collection = collectionFactory.createCollection(rawType, mocks);
			new FieldValueMutator(object, field).mutateTo(collection);
		}
	}

	/**
	 * @param annotation
	 * @return The {@link CollectionOfMocks#numberOfMocks() number of mocks} declared on the annotation.
	 */
	private int getNumberOfMocks(CollectionOfMocks annotation) {
		int numberOfMocks = annotation.numberOfMocks();
		if (numberOfMocks < 0) {
			throw new MockitoCollectionsException(
					"Unexpected numberOfMocks, the minimum number of mocks you can specify using the "
							+ CollectionOfMocks.class + " is zero.");
		}
		return numberOfMocks;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private OrderedSet<?> createMocks(Class collectionType, int numberOfMocks) {
		OrderedSet mocks = new HashOrderedSet();
		for (int i = 0; i < numberOfMocks; i++) {
			Object mock = mockStrategy.createMock(collectionType);
			mocks.add(mock);
		}
		return mocks;
	}
}

@@ -1983,34 +1983,32 @@
 uppressWarnings(
-%7B 
 %22rawtypes%22, %22unc
@@ -1997,39 +1997,24 @@
 s(%22rawtypes%22
-, %22unchecked%22 %7D
 )%0A%09public vo
@@ -2160,947 +2160,950 @@
 ;%0A%09%09
-// TODO tidy this up, looks like a nasty big block of code...%0A%09%09for (Field field : fields) %7B%0A%09%09%09Type type = field.getGenericType();%0A%09%09%09ParameterizedType parameterizedType = (ParameterizedType) type; // FIXME ensure is paramerterized type%0A%09%09%09// should be safe, ParamerterizedType should only ever return a Class from this method%0A%09%09%09Class rawType = (Class) parameterizedType.getRawType();%0A%09%09%09Class collectionType = genericCollectionTypeResolver.getCollectionFieldType(field); // FIXME null check%0A%09%09%09CollectionOfMocks annotation = field.getAnnotation(CollectionOfMocks.class);%0A%09%09%09assert annotation != null : %22Field is missing CollectionOfMocks annotation, unexpected field retrieved from annotatedFieldRetriever%22;%0A%09%09%09OrderedSet%3C?%3E mocks = createMocks(collectionType, getNumberOfMocks(annotation));%0A%09%09%09Collection collection = collectionFactory.createCollection(rawType, mocks);%0A%09%09%09new FieldValueMutator(object, field).mutateTo(collection);%0A%09%09%7D
+for (Field field : fields) %7B%0A%09%09%09Collection collection = createCollectionForField(field);%0A%09%09%09new FieldValueMutator(object, field).mutateTo(collection);%0A%09%09%7D%0A%09%7D%0A%0A%09private Collection createCollectionForField(Field field) %7B%0A%09%09OrderedSet%3C?%3E mocks = createMocks(getMockClass(field), getNumberOfMocks(field));%0A%09%09Class collectionClass = getCollectionClass(field.getGenericType());%0A%09%09return collectionFactory.createCollection(collectionClass, mocks);%0A%09%7D%0A%0A%09private Class getMockClass(Field field) %7B%0A%09%09Class mockClass = genericCollectionTypeResolver.getCollectionFieldType(field); // FIXME null check%0A%09%09return mockClass;%0A%09%7D%0A%0A%09private Class getCollectionClass(Type type) %7B%0A%09%09ParameterizedType parameterizedType = (ParameterizedType) type;%0A%09%09// FIXME ensure is paramerterized type%0A%09%09// should be safe, ParamerterizedType should only ever return a Class from this method%0A%09%09// FIXME ensure is Collection class%0A%09%09return (Class) parameterizedType.getRawType();
 %0A%09%7D%0A
@@ -3119,26 +3119,21 @@
  @param 
-annotation
+field
 %0A%09 * @re
@@ -3262,16 +3262,33 @@
 OfMocks(
+Field field) %7B%0A%09%09
 Collecti
@@ -3307,19 +3307,64 @@
 notation
-) %7B
+ = field.getAnnotation(CollectionOfMocks.class);
 %0A%09%09int n
@@ -3557,20 +3557,16 @@
 y using 
-the 
 %22%0A%09%09%09%09%09%09

Refactor CollectionOfMocksInitialiser#initialise into its component parts