package targetast;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Method;
import java.util.Vector;

import static org.junit.jupiter.api.Assertions.assertEquals;

@Disabled("FIXME")
public class InheritTest2 {
    private static Class<?> classToTest, classToTestAA, classToTestBB, classToTestCC, classToTestDD;
    private static Object instanceOfClass, instanceOfClassAA, instanceOfClassBB, instanceOfClassCC, instanceOfClassDD;

    @BeforeAll
    public static void setUpBeforeClass() throws Exception {
        var classLoader = TestCodegen.createClassLoader();
        classToTest   = TestCodegen.generateClassFiles(classLoader, "Inherit2.jav").get("Inherit2");
        classToTestAA = TestCodegen.generateClassFiles(classLoader, "AA.jav").get("AA");
        classToTestBB = TestCodegen.generateClassFiles(classLoader, "BB.jav").get("BB");
        classToTestCC = TestCodegen.generateClassFiles(classLoader, "CC.jav").get("CC");
        classToTestDD = TestCodegen.generateClassFiles(classLoader,"DD.jav").get("DD");

        instanceOfClass   = classToTest.getDeclaredConstructor().newInstance();
        instanceOfClassAA = classToTestAA.getDeclaredConstructor().newInstance();
        instanceOfClassBB = classToTestBB.getDeclaredConstructor().newInstance();
        instanceOfClassCC = classToTestCC.getDeclaredConstructor().newInstance();
        instanceOfClassDD = classToTestDD.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testmainAA() throws Exception {
        Method m2 = classToTestAA.getDeclaredMethod("m2", classToTestAA);
        assertEquals(m2.invoke(instanceOfClassAA, instanceOfClassAA), "AA");
        Method main = classToTest.getDeclaredMethod("main", classToTestAA);
        assertEquals(main.invoke(instanceOfClass, instanceOfClassAA), "AA");
    }

    @Test
    public void testmainBB() throws Exception {
        Method m2 = classToTestAA.getDeclaredMethod("m2", classToTestAA);
        assertEquals(m2.invoke(instanceOfClassAA, instanceOfClassAA), "AA");
        Method main = classToTest.getDeclaredMethod("main", classToTestAA);
        assertEquals(main.invoke(instanceOfClass, instanceOfClassBB), "AA");
    }

    @Test
    public void testmainCC() throws Exception {
        Method m2 = classToTestCC.getDeclaredMethod("m2", classToTestCC);
        assertEquals(m2.invoke(instanceOfClassCC, instanceOfClassCC), "CC");
        Method main = classToTest.getDeclaredMethod("main", classToTestCC);
        assertEquals(main.invoke(instanceOfClass, instanceOfClassCC), "CC");
    }

    @Test
    public void testmainDD() throws Exception {
        Method m2 = classToTestCC.getDeclaredMethod("m2", classToTestCC);
        assertEquals(m2.invoke(instanceOfClassCC, instanceOfClassCC), "CC");
        Method main = classToTest.getDeclaredMethod("main", classToTestCC);
        assertEquals(main.invoke(instanceOfClass, instanceOfClassDD), "CC");
    }


    //PL 2020-05-12: Die folgenden Test funktionieren erst, wenn Generics im Bytecode implementiert sind
    @Test
    public void testmainVectorAA() throws Exception {
        Method m2 = classToTestAA.getDeclaredMethod("m2", classToTestAA);
        assertEquals(m2.invoke(instanceOfClassAA, instanceOfClassAA), "AA");
        Vector v = new Vector<>();
        v.add(instanceOfClassAA);
        Method main = classToTest.getDeclaredMethod("main", Vector.class);
        try {
            assertEquals(main.invoke(instanceOfClass, v), "AA");
        }
        catch (java.lang.reflect.InvocationTargetException e) {
            testmainVectorCC();
        }
    }

    @Test
    public void testmainVectorBB() throws Exception {
        Method m2 = classToTestAA.getDeclaredMethod("m2", classToTestAA);
        assertEquals(m2.invoke(instanceOfClassAA, instanceOfClassAA), "AA");
        Vector v = new Vector<>();
        v.add(instanceOfClassBB);
        Method main = classToTest.getDeclaredMethod("main", Vector.class);
        try {
            assertEquals(main.invoke(instanceOfClass, v), "AA");
        }
        catch (java.lang.reflect.InvocationTargetException e) {
            testmainVectorCC();
        }
    }

    @Test
    public void testmainVectorCC() throws Exception {
        Method m2 = classToTestCC.getDeclaredMethod("m2", classToTestCC);
        assertEquals(m2.invoke(instanceOfClassCC, instanceOfClassCC), "CC");
        Vector v = new Vector<>();
        v.add(instanceOfClassCC);
        Method main = classToTest.getDeclaredMethod("main", Vector.class);
        String erg;
        assertEquals(erg= (String) main.invoke(instanceOfClass, v),
                erg.equals("CC")? "CC": "AA");
    }

    @Test
    public void testmainVectorDD() throws Exception {
        Method m2 = classToTestCC.getDeclaredMethod("m2", classToTestCC);
        assertEquals(m2.invoke(instanceOfClassCC, instanceOfClassCC), "CC");
        Vector v = new Vector<>();
        v.add(instanceOfClassDD);
        Method main = classToTest.getDeclaredMethod("main", Vector.class);
        String erg;
        assertEquals(erg= (String) main.invoke(instanceOfClass, v),
                erg.equals("CC")? "CC": "AA");
    }
}
