package org.simpleflatmapper.util;

import org.junit.Test;
import org.simpleflatmapper.test.beans.DbObject;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;

import static org.junit.Assert.*;

public class TypeHelperTest {


	@Test
	public void testToClass() {
		assertEquals(Map.class, TypeHelper.toClass(new TypeReference<Map<String, String>>() {}.getType()));

		ParameterizedType type = (ParameterizedType) new TypeReference<Map<? extends Number, String>>() {
		}.getType();
		assertEquals(Number.class, TypeHelper.toClass(type.getActualTypeArguments()[0]));

		assertEquals(Date.class, TypeHelper.toClass(MyComparable.class.getTypeParameters()[0]));

		assertEquals(Date.class, TypeHelper.toClass(Date.class));

		try {
			TypeHelper.toClass(new Type() {
				@Override
				public String getTypeName() {
					return null;
				}
			});
			fail();
		} catch (UnsupportedOperationException  e) {

		}

	}

	@Test
	public void testIs() {
		assertTrue(TypeHelper.isJavaLang(Long.class));
		assertFalse(TypeHelper.isJavaLang(Date.class));
		assertTrue(TypeHelper.isPrimitive(long.class));
		assertFalse(TypeHelper.isPrimitive(Long.class));

		assertTrue(TypeHelper.isArray(new Object[0].getClass()));
		assertFalse(TypeHelper.isArray(new Object().getClass()));


		assertTrue(TypeHelper.isNumber(Long.class));
		assertTrue(TypeHelper.isNumber(BigDecimal.class));
		assertFalse(TypeHelper.isNumber(Date.class));

		assertTrue(TypeHelper.isEnum(E.class));
		assertFalse(TypeHelper.isEnum(BigDecimal.class));
	}

	@Test
	public void testGetTypeMaps() {
		Type type = new TypeReference<Map<String, Number>>() {
		}.getType();
		Map<TypeVariable<?>, Type> typesMap = TypeHelper.getTypesMap(type);

		assertEquals(2, typesMap.size());
		assertEquals(String.class, typesMap.get(Map.class.getTypeParameters()[0]));
		assertEquals(Number.class,  typesMap.get(Map.class.getTypeParameters()[1]));
	}

	enum E {A, B}

	static class MyComparable<T extends Date> implements Comparable<T> {

		@Override
		public int compareTo(T o) {
			return 0;
		}
	}

	@Test
	public void testIsNumber() {
		assertTrue(TypeHelper.isNumber(byte.class));
		assertTrue(TypeHelper.isNumber(Byte.class));
		assertTrue(TypeHelper.isNumber(BigDecimal.class));
		assertFalse(TypeHelper.isNumber(char.class));
		assertFalse(TypeHelper.isNumber(String.class));
	}

	@Test
	public void testAreCompatible() {
		assertTrue(TypeHelper.areCompatible(Integer.class, int.class));
		assertTrue(TypeHelper.areCompatible(Integer.class, Integer.class));
		assertTrue(TypeHelper.areCompatible(Number.class, int.class));
		assertTrue(TypeHelper.areCompatible(Number.class, Integer.class));
		assertTrue(TypeHelper.areCompatible(Object.class, int.class));
		assertTrue(TypeHelper.areCompatible(Object.class, Integer.class));
		assertFalse(TypeHelper.areCompatible(Double.class, Integer.class));
	}
	
	@Test
	public void testWrap() {
		assertEquals(Character.class, TypeHelper.wrap(char.class));
	}

	@Test
	public void testGetComponentTypeOfArray() {
		assertEquals(String.class, TypeHelper.getComponentTypeOfListOrArray(String[].class));
	}

	@Test
	public void testGetComponentTypeOfList() {
		assertEquals(String.class, TypeHelper.getComponentTypeOfListOrArray(new TypeReference<List<String>>(){}.getType()));
	}

	@Test
	public void testGetComponentTypeOfStringList() {
		assertEquals(String.class, TypeHelper.getComponentTypeOfListOrArray(StringList.class));
	}

	@Test
	public void testGetKeyValueTypeOfMapOnGenericInterface() {
		assertEquals(new TypeHelper.MapEntryTypes(String.class, Integer.class),
				TypeHelper.getKeyValueTypeOfMap(new TypeReference<Map<String, Integer>>() {
				}.getType()));
	}
	@Test
	public void testGetKeyValueTypeOfMapOnSpecifiedClass() {
		assertEquals(new TypeHelper.MapEntryTypes(String.class, Integer.class),
				TypeHelper.getKeyValueTypeOfMap(StringIntegerMap.class));
	}

	@Test
	public void testGetKeyValueTypeOfMapOnConcurrentMap() {
		assertEquals(new TypeHelper.MapEntryTypes(String.class, Integer.class),
				TypeHelper.getKeyValueTypeOfMap(new TypeReference<ConcurrentMap<String, Integer>>(){}.getType()));
	}

	@Test
	public void testMapperEntryTypes() {
		assertEquals(new TypeHelper.MapEntryTypes(String.class, Integer.class), new TypeHelper.MapEntryTypes(String.class, Integer.class));
		assertNotEquals(new TypeHelper.MapEntryTypes(Number.class, Integer.class), new TypeHelper.MapEntryTypes(String.class, Integer.class));

		assertEquals(new TypeHelper.MapEntryTypes(String.class, Integer.class).hashCode(), new TypeHelper.MapEntryTypes(String.class, Integer.class).hashCode());
		assertNotEquals(new TypeHelper.MapEntryTypes(Number.class, Integer.class).hashCode(), new TypeHelper.MapEntryTypes(String.class, Integer.class).hashCode());

		new TypeHelper.MapEntryTypes(String.class, Integer.class).toString();
	}

	@Test
	public void testGetGenericParametersForInterface() {
		Type type = new TypeReference<MyClass<DbObject, Integer>>() {
		}.getType();

		assertArrayEquals(new Type[] {String.class, Integer.class}, TypeHelper.getGenericParameterForClass(type, Map.class));
		assertArrayEquals(new Type[]{DbObject.class}, TypeHelper.getGenericParameterForClass(type, Callable.class));


	}

	@Test
	public void testGetGenericParametersForInterfaceNotImplement() {
		Type type = new TypeReference<MyClass<DbObject, String>>() {
		}.getType();

		try {
			TypeHelper.getGenericParameterForClass(type, Collection.class);
			fail("expect exception");
		} catch(IllegalArgumentException e) {
			//expected
		}
	}


	public static class MyClass<T, K> implements Map<String, K>, Callable<T>  {

		@Override
		public T call() throws Exception {
			return null;
		}

		@Override
		public int size() {
			return 0;
		}

		@Override
		public boolean isEmpty() {
			return false;
		}

		@Override
		public boolean containsKey(Object key) {
			return false;
		}

		@Override
		public boolean containsValue(Object value) {
			return false;
		}

		@Override
		public K get(Object key) {
			return null;
		}

		@Override
		public K put(String key, K value) {
			return null;
		}

		@Override
		public K remove(Object key) {
			return null;
		}

		@Override
		public void putAll(Map<? extends String, ? extends K> m) {

		}

		@Override
		public void clear() {

		}

		@Override
		public Set<String> keySet() {
			return null;
		}

		@Override
		public Collection<K> values() {
			return null;
		}

		@Override
		public Set<Entry<String, K>> entrySet() {
			return null;
		}
	}

	public static class StringList extends MyList<String> {
	}

	public static class MyList<P> extends ArrayList<P> {

	}

	public static class StringIntegerMap extends HashMap<String, Integer> {
	}

}

@@ -953,22 +953,8 @@
 ) %7B%0A
-%09%09%09%09@Override%0A
 %09%09%09%09

fix java7