package therian.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

import therian.Operation;
import therian.TypeLiteral;
import therian.operation.Convert;
import therian.operation.Copy;
import therian.operation.Size;
import therian.operation.Transform;
import therian.operator.size.SizeOfCollection;
import therian.operator.size.SizeOfIterable;
import therian.operator.size.SizeOfIterator;
import therian.testfixture.MetasyntacticVariable;

public class TypesTest {

    @Test
    public void testGetSimpleType() {
        assertEquals(new TypeLiteral<ArrayList<String>>() {}.value, Types.resolveAt(
            Size.of(Positions.<ArrayList<String>> readWrite(new TypeLiteral<ArrayList<String>>() {})),
            Size.class.getTypeParameters()[0]));
    }

    @Test
    public void testGetInheritedType() {
        final Copy<Integer, String> copy =
            Copy.to(Positions.<String> readWrite(String.class), Positions.readOnly(Integer.valueOf(666)));
        assertEquals(Integer.class, Types.resolveAt(copy, Transform.class.getTypeParameters()[0]));
        assertEquals(String.class, Types.resolveAt(copy, Transform.class.getTypeParameters()[1]));
        assertEquals(Integer.class, Types.resolveAt(copy, Copy.class.getTypeParameters()[0]));
        assertEquals(String.class, Types.resolveAt(copy, Copy.class.getTypeParameters()[1]));
        final Convert<Integer, String> convert = Convert.to(String.class, Positions.readOnly(Integer.valueOf(666)));
        assertEquals(Integer.class, Types.resolveAt(convert, Transform.class.getTypeParameters()[0]));
        assertEquals(String.class, Types.resolveAt(convert, Transform.class.getTypeParameters()[1]));
        assertEquals(Integer.class, Types.resolveAt(convert, Convert.class.getTypeParameters()[0]));
        assertEquals(String.class, Types.resolveAt(convert, Convert.class.getTypeParameters()[1]));
    }

    public void testNonTyped() {
        Assert.assertNull(Types.resolveAt(
            Copy.to(Positions.<String> readWrite(String.class), Positions.readOnly(Integer.valueOf(666))),
            Operation.class.getTypeParameters()[0]));
    }

    @Test(expected = IllegalArgumentException.class)
    public void testBadTypeVariable() throws Exception {
        Types.resolveAt(Convert.to(String.class, Positions.readOnly(Integer.valueOf(666))), getClass()
            .getDeclaredMethod("foo").getTypeParameters()[0]);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testNotAssignableTypeVariable() {
        Types.resolveAt(Convert.to(String.class, Positions.readOnly(Integer.valueOf(666))),
            Copy.class.getTypeParameters()[0]);
    }

    @Test
    public void testMatchesOperator() {
        final Size<List<MetasyntacticVariable>> size =
            Size.of(Positions.<List<MetasyntacticVariable>> readOnly(Arrays.asList(MetasyntacticVariable.values())));
        assertTrue(size.matches(new SizeOfCollection()));
        assertTrue(size.matches(new SizeOfIterable()));
        assertFalse(size.matches(new SizeOfIterator()));
    }

    @SuppressWarnings("unused")
    private <T> void foo() {

    }
}

@@ -202,16 +202,72 @@
 Arrays;%0A
+import java.util.Collection;%0Aimport java.util.Iterator;%0A
 import j
@@ -277,24 +277,24 @@
 .util.List;%0A
-
 %0Aimport org.
@@ -517,16 +517,226 @@
 nsform;%0A
+import therian.operator.FromSourceToTarget.FromSource;%0Aimport therian.operator.FromSourceToTarget.ToTarget;%0Aimport therian.operator.convert.CopyingConverter;%0Aimport therian.operator.convert.IterableToIterator;%0A
 import t
@@ -856,32 +856,65 @@
 SizeOfIterator;%0A
+import therian.testfixture.Book;%0A
 import therian.t
@@ -2270,32 +2270,32 @@
 ameters()%5B0%5D));%0A
-
         assertEq
@@ -2381,24 +2381,1058 @@
 %5D));%0A    %7D%0A%0A
+    @Test%0A    public void testGetInterfaceType() %7B%0A        final IterableToIterator iterableToIterator = new IterableToIterator();%0A%0A        assertTrue(Types.equals(Types.parameterize(Iterable.class, Types.WILDCARD_ALL),%0A            Types.resolveAt(iterableToIterator, FromSource.class.getTypeParameters()%5B0%5D)));%0A        assertEquals(Iterator.class, Types.resolveAt(iterableToIterator, ToTarget.class.getTypeParameters()%5B0%5D));%0A%0A        final CopyingConverter%3CObject, Book%3E forBook = CopyingConverter.forTargetType(Book.class);%0A        assertEquals(Object.class, Types.resolveAt(forBook, FromSource.class.getTypeParameters()%5B0%5D));%0A        assertEquals(Book.class, Types.resolveAt(forBook, ToTarget.class.getTypeParameters()%5B0%5D));%0A%0A        assertEquals(Object.class,%0A            Types.resolveAt(CopyingConverter.IMPLEMENTING_COLLECTION, FromSource.class.getTypeParameters()%5B0%5D));%0A        assertEquals(Collection.class,%0A            Types.resolveAt(CopyingConverter.IMPLEMENTING_COLLECTION, ToTarget.class.getTypeParameters()%5B0%5D));%0A    %7D%0A%0A
     public v

add tests to show that FromSource/ToTarget function properly