
import de.dhbwstuttgart.core.ConsoleInterface;
import de.dhbwstuttgart.util.Logger;
import de.dhbwstuttgart.util.Logger.LogLevel;

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

import java.lang.reflect.*;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;

import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import targetast.TestCodegen;

import static org.junit.jupiter.api.Assertions.*;
import static targetast.TestCodegen.createClassLoader;
import static targetast.TestCodegen.generateClassFiles;

@Execution(ExecutionMode.CONCURRENT)
public class TestComplete {
    @Test
    public void applyLambdaTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "applyLambda.jav");
        var applyLambda = classFiles.get("applyLambda");
        var instance = applyLambda.getDeclaredConstructor().newInstance();
        var m = applyLambda.getDeclaredMethod("m");
        var result = m.invoke(instance);

        assertEquals(result.getClass(), classFiles.get("Apply"));
    }

    @Test
    public void binaryTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "BinaryInMeth.jav");
        var binaryInMeth = classFiles.get("BinaryInMeth");
        var instance = binaryInMeth.getDeclaredConstructor().newInstance();

        var m2 = binaryInMeth.getDeclaredMethod("m2", Integer.class, Integer.class);
        var m3 = binaryInMeth.getDeclaredMethod("m3", Integer.class);

        assertEquals(6, m2.invoke(instance, 2, 3));
        assertEquals(4, m3.invoke(instance, 2));
    }

    @Test
    public void classGenLamTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "ClassGenLam.jav");
        classFiles.get("ClassGenLam").getDeclaredConstructor().newInstance();
    }

    @Test
    public void facTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Fac.jav");
        var fac = classFiles.get("Fac");
        var instance = fac.getDeclaredConstructor().newInstance();

        var getFac = fac.getDeclaredMethod("getFac", Integer.class);
        // assertEquals(6, getFac.invoke(instance, 3)); TODO This could be either an Integer or a Double so this simple test fails
    }

    @Test
    public void facultyTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Faculty.jav");

        var fac = classFiles.get("Faculty");
        var constructor = fac.getDeclaredConstructor();
        constructor.setAccessible(true);
        var instance = constructor.newInstance();

        var getFact = fac.getDeclaredMethod("getFact", Integer.class);
        assertEquals(6, getFact.invoke(instance, 3));
    }

    @Test
    public void fieldTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Field.jav");
        var field = classFiles.get("Field");
        var instance = field.getDeclaredConstructor().newInstance();
        assertEquals(1, field.getFields().length);
    }

    @Test
    public void fieldTph2Test() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "FieldTph2.jav");
        var fieldtph2 = classFiles.get("FieldTph2");
        var instance = fieldtph2.getDeclaredConstructor().newInstance();

        var a = fieldtph2.getDeclaredField("a");
        var m2 = fieldtph2.getDeclaredMethod("m2", Object.class);
        m2.invoke(instance, 1);

        var m = fieldtph2.getDeclaredMethod("m", Object.class);
        assertEquals(1, m.invoke(instance, 1));
    }

    @Test
    public void fieldTphConsMethTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "FieldTphConsMeth.jav");
        var fieldTphConsMeth = classFiles.get("FieldTphConsMeth");

        var ctor = fieldTphConsMeth.getDeclaredConstructor(Object.class);
        var instance = ctor.newInstance("C");
        var a = fieldTphConsMeth.getDeclaredField("a");
        var id = fieldTphConsMeth.getDeclaredMethod("id", Object.class);

        assertEquals(42, id.invoke(instance, 42));
        assertEquals("C", a.get(instance));
    }

    @Test
    public void fieldTphMMethTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "FieldTphMMeth.jav");
        var fieldTphMMeth = classFiles.get("FieldTphMMeth");
        var ctor = fieldTphMMeth.getDeclaredConstructor(Object.class, Object.class, Boolean.class);

        var instance1 = ctor.newInstance("C", 42, true);
        var instance2 = ctor.newInstance("C", 42, false);

        var m = fieldTphMMeth.getDeclaredMethod("m", Object.class, Object.class, Boolean.class);
        assertEquals(42, m.invoke(instance1, "C", 42, false));

        var a = fieldTphMMeth.getDeclaredField("a");
        assertEquals("C", a.get(instance1));
        assertEquals(42, a.get(instance2));
    }

    @Test
    public void genTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Gen.jav");
    }

    @Test
    public void idTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Id.jav");
        var instance = classFiles.get("Id").getDeclaredConstructor().newInstance();
    }

    @Test
    public void infTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Inf.jav");
        var instance = classFiles.get("Inf").getDeclaredConstructor().newInstance();
        // TODO check generics
    }

    @Test
    public void kompTphTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "KompTph.jav");
        var instance = classFiles.get("KompTph").getDeclaredConstructor().newInstance();
    }

    @Test
    public void lambdaCaptureTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "LambdaCapture.jav");
        var instance = classFiles.get("LambdaCapture").getDeclaredConstructor().newInstance();
    }

    @Test
    public void lambdaTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Lambda.jav");
        var classToTest = classFiles.get("Lambda");
        var instanceOfClass = classToTest.getDeclaredConstructor().newInstance();

        var m = classToTest.getDeclaredMethod("m");
        var lambda = m.invoke(instanceOfClass).getClass();
        var apply = lambda.getMethod("apply", Object.class);
        apply.setAccessible(true);

        var i = Integer.valueOf(77);
        assertEquals(i, apply.invoke(m.invoke(instanceOfClass), i));
    }

    @Test
    public void mathStrucInteger() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "mathStrucInteger.jav");
        var mathStrucInteger = classFiles.get("mathStrucInteger");
        mathStrucInteger.getDeclaredConstructor(Integer.class).newInstance(10);
    }

    @Test
    public void mathStruc() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "mathStruc.jav");
        var mathStruc = classFiles.get("mathStruc");
        mathStruc.getDeclaredConstructor(Object.class).newInstance("A");
    }

    @Test
    //@Disabled()
    public void matrixOpTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "MatrixOP.jav");
        var matrixOP = classFiles.get("MatrixOP");

        Vector<Vector<Integer>> vv = new Vector<>();
        Vector<Integer> v1 = new Vector<>();
        v1.addElement(2);
        v1.addElement(2);
        Vector<Integer> v2 = new Vector<>();
        v2.addElement(3);
        v2.addElement(3);
        vv.addElement(v1);
        vv.addElement(v2);

        var instanceOfClass_m1 = matrixOP.getDeclaredConstructor(Vector.class).newInstance(vv);

        Vector<Vector<Integer>> vv1 = new Vector<>();
        Vector<Integer> v3 = new Vector<>();
        v3.addElement(2);
        v3.addElement(2);
        Vector<Integer> v4 = new Vector<>();
        v4.addElement(3);
        v4.addElement(3);
        vv1.addElement(v3);
        vv1.addElement(v4);

        var instanceOfClass_m2 = matrixOP.getDeclaredConstructor(Vector.class).newInstance(vv1);// Matrix m2 = new Matrix(vv1);

        var mul = matrixOP.getField("mul");
        mul.setAccessible(true);

        var lambda = mul.get(instanceOfClass_m1).getClass();
        var apply = lambda.getMethod("apply", Object.class, Object.class);
        apply.setAccessible(true);

        var result = apply.invoke(mul.get(instanceOfClass_m1), instanceOfClass_m1, instanceOfClass_m2);
        System.out.println(instanceOfClass_m1.toString() + " * " + instanceOfClass_m2.toString() + " = " + result.toString());

        Vector<Vector<Integer>> res = new Vector<>();
        Vector<Integer> v5 = new Vector<>();
        v5.addElement(10);
        v5.addElement(10);
        Vector<Integer> v6 = new Vector<>();
        v6.addElement(15);
        v6.addElement(15);
        res.addElement(v5);
        res.addElement(v6);

        var instanceOfClass_m3 = matrixOP.getDeclaredConstructor(Vector.class).newInstance(res);
        assertEquals(result, instanceOfClass_m3);
    }

    @Test
    //@Disabled("This is too complex")
    public void matrixTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Matrix.jav");
        var matrix = classFiles.get("Matrix");

        Vector<Vector<Integer>> vv = new Vector<>();
        Vector<Integer> v1 = new Vector<>();
        v1.addElement(2);
        v1.addElement(2);
        Vector<Integer> v2 = new Vector<>();
        v2.addElement(3);
        v2.addElement(3);
        vv.addElement(v1);
        vv.addElement(v2);

        var instanceOfClass_m1 = matrix.getDeclaredConstructor(Vector.class).newInstance(vv);

        Vector<Vector<Integer>> vv1 = new Vector<>();
        Vector<Integer> v3 = new Vector<>();
        v3.addElement(2);
        v3.addElement(2);
        Vector<Integer> v4 = new Vector<>();
        v4.addElement(3);
        v4.addElement(3);
        vv1.addElement(v3);
        vv1.addElement(v4);

        var instanceOfClass_m2 = matrix.getDeclaredConstructor(Vector.class).newInstance(vv1);

        var mul = matrix.getDeclaredMethod("mul", List.class);
        var result = mul.invoke(instanceOfClass_m1, instanceOfClass_m2);
        System.out.println(instanceOfClass_m1.toString() + " * " + instanceOfClass_m2.toString() + " = " + result.toString());

        Vector<Vector<Integer>> res = new Vector<>();
        Vector<Integer> v5 = new Vector<>();
        v5.addElement(10);
        v5.addElement(10);
        Vector<Integer> v6 = new Vector<>();
        v6.addElement(15);
        v6.addElement(15);
        res.addElement(v5);
        res.addElement(v6);

        var instanceOfClass_m3 = matrix.getDeclaredConstructor(Vector.class).newInstance(res);
        assertEquals(result, instanceOfClass_m3);
    }

    @Test
    public void scalarTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Scalar.jav");
        var scalar = classFiles.get("Scalar");

        Vector<Vector<Integer>> vv = new Vector<>();
        Vector<Integer> v1 = new Vector<>();
        v1.addElement(2);
        v1.addElement(2);

        var instanceOfClass_s1 = scalar.getDeclaredConstructor(Vector.class).newInstance(v1);

        Vector<Integer> v2 = new Vector<>();
        v2.addElement(2);
        v2.addElement(2);

        var instanceOfClass_s2 = scalar.getDeclaredConstructor(Vector.class).newInstance(v2);

        var mul = scalar.getDeclaredMethod("mul", Vector.class);
        var result = mul.invoke(instanceOfClass_s1, instanceOfClass_s2);
        System.out.println(instanceOfClass_s1.toString() + " * " + instanceOfClass_s2.toString() + " = " + result.toString());

        assertEquals(8, result);
    }

    @Test
    public void mergeTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Merge.jav");
        var instance = classFiles.get("Merge").getDeclaredConstructor().newInstance();
    }

    @Test
    public void overloadingSortingTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Sorting.jav");
        var instance = classFiles.get("Sorting").getDeclaredConstructor().newInstance();
    }

    @Test
    public void overloadingTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Overloading.jav");
        var overloading = classFiles.get("Overloading");
        var overloading2 = classFiles.get("Overloading2");
        var instance1 = overloading.getDeclaredConstructor().newInstance();
        var instance2 = overloading2.getDeclaredConstructor().newInstance();

        var m1 = overloading.getDeclaredMethod("test", overloading);
        assertEquals("Overloading", m1.invoke(instance1, instance1));
        var m2 = overloading.getDeclaredMethod("test", overloading2);
        assertEquals("Overloading2", m2.invoke(instance1, instance2));
    }

    @Test
    public void plusTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Plus.jav");
        var plus = classFiles.get("Plus");
        var instance = plus.getDeclaredConstructor().newInstance();

        var addInt = plus.getDeclaredMethod("m", Integer.class, Integer.class);
        assertEquals(10, addInt.invoke(instance, 7, 3));

        var addString = plus.getDeclaredMethod("m", String.class, String.class);
        assertEquals("ByteCode", addString.invoke(instance, "Byte", "Code"));
    }

    @Test
    public void relOpsTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "RelOps.jav");
        var relOps = classFiles.get("RelOps");
        var instance = relOps.getDeclaredConstructor().newInstance();

        var m = relOps.getDeclaredMethod("m", Integer.class, Integer.class);
        assertFalse((Boolean) m.invoke(instance, 7, 3));
    }

    @Test
    public void simpleCyclesTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SimpleCycle.jav");
        var instance = classFiles.get("SimpleCycle").getDeclaredConstructor().newInstance();
    }

    @Test
    public void subMatTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SubMatrix.jav");
        var instance = classFiles.get("SubMatrix").getDeclaredConstructor().newInstance();
    }

    @Test
    public void tphTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Tph.jav");
        var tph = classFiles.get("Tph");
        var instance = tph.getDeclaredConstructor().newInstance();
    }

    @Test
    public void tph2Test() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Tph2.jav");
        var tph2 = classFiles.get("Tph2");
        var instance = tph2.getDeclaredConstructor().newInstance();

        assertEquals(1, tph2.getTypeParameters().length);
        // public class Tph2<DZG>
        var DZG = tph2.getTypeParameters()[0];

        var id = tph2.getDeclaredField("id");
        // public Fun1$$<DZG, DZG> id
        var idParams = ((ParameterizedType) id.getGenericType()).getActualTypeArguments();
        assertEquals(2, idParams.length);
        assertEquals(DZG, idParams[0]);
        assertEquals(DZG, idParams[1]);

        var id3 = tph2.getDeclaredMethod("id3", Object.class);
        // public <U extends DZG> DZG id3(U var1)
        var paraTypes = id3.getGenericParameterTypes();
        var typeParaTypes = id3.getTypeParameters();

        var U = Arrays.stream(typeParaTypes).filter(t -> t.equals(paraTypes[0])).findFirst().get();
        assertEquals(DZG, U.getBounds()[0]);
        assertEquals(DZG, id3.getGenericReturnType());
    }

    @Test
    public void tph3Test() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Tph3.jav");
        var tph3 = classFiles.get("Tph3");
        var instance = tph3.getDeclaredConstructor().newInstance();
        var m1 = tph3.getDeclaredMethod("m1", Object.class, Object.class);

        // public <DXBE extends DXBD, DXBD> void m1(DXBD var1, DXBE var2)
        var paraTypes = m1.getGenericParameterTypes();
        var typeParaTypes = m1.getTypeParameters();

        var DXBD = Arrays.stream(typeParaTypes).filter(t -> t.equals(paraTypes[0])).findFirst().get();
        assertEquals(Object.class, DXBD.getBounds()[0]);
        var DXBE = Arrays.stream(typeParaTypes).filter(t -> t.equals(paraTypes[1])).findFirst().get();
        assertEquals(DXBD, DXBE.getBounds()[0]);

        var m2 = tph3.getDeclaredMethod("m2", Object.class);

        // public <V> void m2(V var1)
        var paraTypes2 = m2.getGenericParameterTypes();
        var typeParaTypes2 = m2.getTypeParameters();

        var V = Arrays.stream(typeParaTypes2).filter(t -> t.equals(paraTypes2[0])).findFirst().get();
        assertEquals(Object.class, V.getBounds()[0]);
    }

    @Test
    public void tph4Test() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Tph4.jav");
        var tph4 = classFiles.get("Tph4");
        var instance = tph4.getDeclaredConstructor().newInstance();
        var m = tph4.getDeclaredMethod("m", Object.class, Object.class);

        // public <N, O> N m(O var1, N var2) {
        var paraTypes = m.getGenericParameterTypes();
        var typeParaTypes = m.getTypeParameters();

        var N = Arrays.stream(typeParaTypes).filter(t -> t.equals(m.getGenericReturnType())).findFirst().get();
        assertEquals(Object.class, N.getBounds()[0]);

        var O = Arrays.stream(typeParaTypes).filter(t -> t.equals(paraTypes[0])).findFirst().get();
        var N2 = Arrays.stream(typeParaTypes).filter(t -> t.equals(paraTypes[1])).findFirst().get();
        assertEquals(N, N2);
        assertEquals(Object.class, N.getBounds()[0]);
        assertEquals(Object.class, O.getBounds()[0]);

        var m2 = tph4.getDeclaredMethod("m2", Object.class);

        // public <X> X m2(X var1)
        var paraTypes2 = m2.getGenericParameterTypes();
        var typeParaTypes2 = m2.getTypeParameters();

        var X = Arrays.stream(typeParaTypes2).filter(t -> t.equals(paraTypes2[0])).findFirst().get();
        assertEquals(Object.class, X.getBounds()[0]);
        assertEquals(X, m2.getGenericReturnType());
    }

    @Test
    public void tph5Test() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Tph5.jav");
        var tph5 = classFiles.get("Tph5");
        var instance = tph5.getDeclaredConstructor().newInstance();
        var m = tph5.getDeclaredMethod("m", Object.class, Object.class);

        // public <N, O extends N> void m(N var1, O var2)
        var paraTypes = m.getGenericParameterTypes();
        var typeParaTypes = m.getTypeParameters();

        var N = Arrays.stream(typeParaTypes).filter(t -> t.equals(paraTypes[0])).findFirst().get();
        var O = Arrays.stream(typeParaTypes).filter(t -> t.equals(paraTypes[1])).findFirst().get();

        assertEquals(O.getBounds()[0], N);
        assertEquals(Object.class, N.getBounds()[0]);

        var m2 = tph5.getDeclaredMethod("m2", Object.class);

        // public <R> R m2(R var1)
        var paraTypes2 = m2.getGenericParameterTypes();
        var typeParaTypes2 = m2.getTypeParameters();

        var R = Arrays.stream(typeParaTypes2).filter(t -> t.equals(paraTypes2[0])).findFirst().get();
        assertEquals(Object.class, R.getBounds()[0]);
        assertEquals(R, m2.getGenericReturnType());
    }

    @Test
    public void tph6Test() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Tph6.jav");
        var tph5 = classFiles.get("Tph6");
        var instance = tph5.getDeclaredConstructor().newInstance();
        var m = tph5.getDeclaredMethod("m", Object.class, Object.class);

        // public <P, O> void m(O var1, P var2)
        var paraTypes = m.getGenericParameterTypes();
        var typeParaTypes = m.getTypeParameters();

        var P = Arrays.stream(typeParaTypes).filter(t -> t.equals(paraTypes[0])).findFirst().get();
        var O = Arrays.stream(typeParaTypes).filter(t -> t.equals(paraTypes[1])).findFirst().get();

        assertEquals(Object.class, P.getBounds()[0]);
        assertEquals(Object.class, O.getBounds()[0]);

        var m2 = tph5.getDeclaredMethod("m2", Object.class);

        // public <W> W m2(W var1)
        var paraTypes2 = m2.getGenericParameterTypes();
        var typeParaTypes2 = m2.getTypeParameters();

        var W = Arrays.stream(typeParaTypes2).filter(t -> t.equals(paraTypes2[0])).findFirst().get();
        assertEquals(Object.class, W.getBounds()[0]);
        assertEquals(W, m2.getGenericReturnType());
    }

    @Test
    public void Tph7Test() throws Exception {
        var classFiles = TestCodegen.generateClassFiles(createClassLoader(), "Tph7.jav");
        var classToTest = classFiles.get("Tph7");
        var instanceOfClass = classToTest.getDeclaredConstructor().newInstance();

        // public <N, O> N m(O var1, N var2)
        Method m = classToTest.getDeclaredMethod("m", Object.class, Object.class);

        // System.out.println(m.toString());

        // Argumenttypes of the method m
        var paraTypes = m.getGenericParameterTypes();

        // Typeparameters of the method m
        var typeParaTypes = m.getTypeParameters();

        // Typeparameters are extracted from the argumenttypes
        // Conditions for the extracted typeparameters are set

        // paraTypes[0] = O
        var boundFstArg = Arrays.stream(typeParaTypes).filter(x -> x.equals(paraTypes[0])).findFirst().get().getBounds();

        // Bound of O has to be Object
        assertEquals(Object.class, Arrays.stream(boundFstArg).findFirst().get());

        // paraTypes[1] = N
        var N = Arrays.stream(typeParaTypes).filter(x -> x.equals(paraTypes[1])).findFirst().get();
        var boundSndArg = N.getBounds();

        // Bound of H has to be Object
        assertEquals(Object.class, Arrays.stream(boundSndArg).findFirst().get());

        // N has to be the return type of m
        assertEquals(N, m.getGenericReturnType());

        // public <DZU> DZU m2(DZU);
        Method m2 = classToTest.getDeclaredMethod("m2", Object.class);

        // Argumenttypes of the method m2
        var paraTypesm2 = m2.getGenericParameterTypes();

        // Typeparameters of the method m2
        var typeParaTypesm2 = m2.getTypeParameters();

        // Typeparameters are extracted from the argumenttypes
        // Conditions for the extracted typeparameters are set

        // paraTypes[0] = DZU
        var fstArgm2 = Arrays.stream(typeParaTypesm2).filter(x -> x.equals(paraTypesm2[0])).findFirst().get();

        // Bound of DZU has to be Object
        assertEquals(Object.class, Arrays.stream(fstArgm2.getBounds()).findFirst().get());

        // DZU has to be the return type of m
        assertEquals(fstArgm2, m2.getGenericReturnType());
    }

    @Test
    public void testTXGenerics() throws Exception {
        var classLoader = createClassLoader();
        generateClassFiles(classLoader, "Cycle.jav");
        var classFiles = generateClassFiles(classLoader, "TXGenerics.jav");
        var instance = classFiles.get("TXGenerics").getDeclaredConstructor().newInstance();
    }

    @Test
    public void typedIdTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "TypedID.jav");
        var instance = classFiles.get("TypedID").getDeclaredConstructor().newInstance();
    }

    @Test
    public void vectorAddTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "VectorAdd.jav");
        var instance = classFiles.get("VectorAdd").getDeclaredConstructor().newInstance();
    }

    @Test
    public void vectorSuperTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "VectorSuper.jav");
        var instance = classFiles.get("VectorSuper").getDeclaredConstructor().newInstance();
    }

    @Test
    public void yTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Y.jav");

        var fac1 = classFiles.get("Fac1");
        var instance = fac1.getDeclaredConstructor().newInstance();
        var fac = fac1.getDeclaredMethod("fac", Integer.class);
        assertEquals(3628800, fac.invoke(instance, 10));
    }

    @Test
    //@Disabled("This one isn't working")
    public void boxTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Box.jav");
        var instance = classFiles.get("Box").getDeclaredConstructor().newInstance();
    }

    @Test
    public void cycleTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Cycle.jav");
        var instance = classFiles.get("Cycle").getDeclaredConstructor().newInstance();
    }

    @Test
    public void olFunTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "OLFun.jav");
        var instance = classFiles.get("OLFun").getDeclaredConstructor().newInstance();
    }

    @Test
    public void pairTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Pair.jav");
        var instance = classFiles.get("Pair").getDeclaredConstructor().newInstance();
    }

    @Test
    public void olTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "OL.jav");
        var instance = classFiles.get("OL").getDeclaredConstructor().newInstance();
    }

    @Test
    public void recordTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "RecordTest.jav");
        var clazz = classFiles.get("RecordTest");
        var instance = clazz.getDeclaredConstructor().newInstance();
        assertTrue((Boolean) clazz.getDeclaredMethod("doesEqual").invoke(instance));
        assertFalse((Boolean) clazz.getDeclaredMethod("doesNotEqual").invoke(instance));
        System.out.println(clazz.getDeclaredMethod("hashCode").invoke(instance));
        System.out.println(clazz.getDeclaredMethod("toString").invoke(instance));
    }

    @Test
    public void genericRecordTest() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "GenericRecord.jav");
        var clazz = classFiles.get("GenericRecord");
        var instance = clazz.getDeclaredConstructor(Object.class).newInstance((Object) null);
    }

    @Test
    public void testSwitch() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Switch.jav");
        var clazz = classFiles.get("Switch");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var swtch = clazz.getDeclaredMethod("main", Object.class);

        var record = classFiles.get("Rec");
        var ctor = record.getDeclaredConstructor(Integer.class, Object.class);
        var r1 = ctor.newInstance(10, 20);
        var r2 = ctor.newInstance(10, 20f);
        var r3 = ctor.newInstance(10, r1);

        assertEquals(30, swtch.invoke(instance, r1));
        assertEquals(20, swtch.invoke(instance, r2));
        assertEquals(40, swtch.invoke(instance, r3));
        assertEquals(50, swtch.invoke(instance, 50));
        try {
            assertEquals(0, swtch.invoke(instance, "Some string"));
            fail("No assertion thrown!");
        } catch (InvocationTargetException exception) {
            assertTrue(exception.getCause() instanceof IllegalArgumentException);
            assertEquals("Unhandled case value", exception.getCause().getMessage());
        }
    }

    @Test
    public void testSwitchInfered() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SwitchInfered.jav");
        var clazz = classFiles.get("SwitchInfered");

        var instance = clazz.getDeclaredConstructor().newInstance();
        var swtch = clazz.getDeclaredMethod("main", Object.class);

        var record = classFiles.get("Rec");
        var ctor = record.getDeclaredConstructor(Object.class, Object.class);
        var r1 = ctor.newInstance("asd", "asb");


        assertEquals("asd", swtch.invoke(instance, r1));
    }

    @Test
    public void testSwitchAppend() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SwitchAppend.jav");
        var clazz = classFiles.get("SwitchAppend");

        var listClass = classFiles.get("List");
        var elemClass = classFiles.get("Elem");
        var linkedElemClass = classFiles.get("LinkedElem");

        var elemCtor = elemClass.getDeclaredConstructor(Object.class);
        var listCtor = linkedElemClass.getDeclaredConstructor(Object.class, listClass);

        var list1 = listCtor.newInstance("A", listCtor.newInstance("B", elemCtor.newInstance("C")));
        var list2 = listCtor.newInstance("D", listCtor.newInstance("E", elemCtor.newInstance("F")));

        var instance = clazz.getDeclaredConstructor().newInstance();
        var appendMethod = clazz.getDeclaredMethod("append", linkedElemClass, listClass);

        var res = appendMethod.invoke(instance, list2, list1);
        var expected = listCtor.newInstance("D", listCtor.newInstance("E", listCtor.newInstance("F", listCtor.newInstance("A", listCtor.newInstance("B", elemCtor.newInstance("C"))))));

        assertEquals(res, expected);
    }
    @Test
    public void testSwitchRecordLiteral() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "PatternMatchingLiteralStyle.jav");
        var clazz = classFiles.get("SwitchOverload");
        var record = classFiles.get("R");
        var rctor = record.getDeclaredConstructor(String.class);

        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("m", record);
        assertEquals(10, m.invoke(instance, rctor.newInstance("test")));
        assertEquals(20, m.invoke(instance, rctor.newInstance("foo")));
        assertEquals(0, m.invoke(instance, rctor.newInstance("???")));

    }

    @Test
    public void testSwitchCaseHeritageDetection() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SwitchCaseHeritageDetection.jav");
        var clazz = classFiles.get("SwitchCaseHeritageDetection");
        var clazzAbst = classFiles.get("NumberOrText");
        var clazzStringer = classFiles.get("Text");
        var clazzNumber = classFiles.get("Number");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var swtch = clazz.getDeclaredMethod("main", Object.class);

        var record = classFiles.get("Cons");



        var ctor = record.getDeclaredConstructor(Integer.class, clazzAbst.getConstructor().newInstance().getClass());
        var r1 = ctor.newInstance(1, clazzStringer.getConstructor().newInstance());
        var r2 = ctor.newInstance(1, clazzNumber.getConstructor().newInstance());


        assertEquals("Second Element is a Text", swtch.invoke(instance, r1));
        assertEquals("Second Element is a Number", swtch.invoke(instance, r2));
    }

    @Disabled("Infinite loop in type inference")
    @Test
    public void testSwitchListRecord() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SwitchRecordList.jav");

        var clazz = classFiles.get("SwitchCaseHeritageDetection");

        var instance = clazz.getDeclaredConstructor().newInstance();
        var swtch = clazz.getDeclaredMethod("main", Object.class);

        var linkedElem = classFiles.get("LinkedElem");
        var listInterface = classFiles.get("List");
        var elem = classFiles.get("Elem");


        var constructorLinkedElem = linkedElem.getDeclaredConstructor(Integer.class, listInterface);
        var constructorElem = elem.getDeclaredConstructor(Integer.class);



        var listWithMoreThanOneElement = constructorLinkedElem.newInstance(5, constructorLinkedElem.newInstance(1, constructorElem.newInstance(5)));
        assertEquals(11, swtch.invoke(instance, listWithMoreThanOneElement));

        var listWithOneElement = constructorLinkedElem.newInstance(5, constructorElem.newInstance(5));
        assertEquals(10, swtch.invoke(instance, listWithOneElement));
    }

    @Test
    public void testSwitchNestedValue() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SwitchNestedValue.jav");
        var clazz = classFiles.get("SwitchNestedValue");
        var rec = classFiles.get("R");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("main", rec);

        var ctor = rec.getDeclaredConstructor(Integer.class);
        var r1 = ctor.newInstance(10);
        var r2 = ctor.newInstance(20);
        var r3 = ctor.newInstance(30);

        assertEquals(1, m.invoke(instance, r1));
        assertEquals(2, m.invoke(instance, r2));
        assertEquals(3, m.invoke(instance, r3));
    }

    @Test
    public void testGenericRecordSwitchCase() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "GenericRecordSwitchCase.jav");

        var clazz = classFiles.get("GenericRecordSwitchCase");

        var instance = clazz.getDeclaredConstructor().newInstance();

        var linkedElem = classFiles.get("LinkedElem");
        var listInterface = classFiles.get("List");
        var elem = classFiles.get("Elem");
        var swtch = clazz.getDeclaredMethod("main", linkedElem);


        var constructorLinkedElem = linkedElem.getConstructor(Object.class, listInterface);
        var constructorElem = elem.getConstructor(Object.class);



        var listWithMoreThanOneElement = constructorLinkedElem.newInstance(5, constructorLinkedElem.newInstance(1, constructorElem.newInstance(5)));
        assertEquals(5, swtch.invoke(instance, listWithMoreThanOneElement));

        var listWithOneElement = constructorLinkedElem.newInstance(5, constructorElem.newInstance(5));
        assertEquals(5, swtch.invoke(instance, listWithOneElement));
    }


    @Disabled("Not implemented")
    @Test
    public void testSwitch2() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Switch2.jav");
        var clazz = classFiles.get("Switch2");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Disabled("Not implemented")
    @Test
    public void testStringSwitch() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SwitchString.jav");
        var clazz = classFiles.get("SwitchString");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var main = clazz.getDeclaredMethod("main", String.class);

        assertEquals(1, main.invoke(instance, "AaAaAa"));
        assertEquals(2, main.invoke(instance, "AaAaBB"));
        assertEquals(3, main.invoke(instance, "test"));
        assertEquals(3, main.invoke(instance, "TEST"));
        assertEquals(4, main.invoke(instance, "awawa"));
    }

    @Test
    public void testInstanceOfPattern() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "InstanceOf.jav");
        var clazz = classFiles.get("InstanceOf");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Disabled("Not implemented")
    @Test
    public void testOverloadPattern() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "OverloadPattern.jav");
        var clazz = classFiles.get("OverloadPattern");
        var rec = classFiles.get("Point");

        var instance = clazz.getDeclaredConstructor().newInstance();
        var m1 = clazz.getDeclaredMethod("m", rec, rec);
        var m2 = clazz.getDeclaredMethod("m", rec);
        var m3 = clazz.getDeclaredMethod("m", Integer.class);

        var ptInt = rec.getDeclaredConstructor(Number.class, Number.class).newInstance(1, 2);
        var ptFlt = rec.getDeclaredConstructor(Number.class, Number.class).newInstance(1f, 2f);

        assertEquals(1, m1.invoke(instance, ptInt, ptFlt));
        assertEquals(2, m1.invoke(instance, ptInt, ptInt));
        assertEquals(3, m1.invoke(instance, ptFlt, ptInt));
        assertEquals(4, m1.invoke(instance, ptFlt, ptFlt));

        assertEquals(5, m2.invoke(instance, ptInt));
        assertEquals(6, m2.invoke(instance, ptFlt));

        assertEquals(10, m3.invoke(instance, 10));
    }

    @Disabled("Not implemented")
    @Test
    public void testOverloadNestedPattern() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "OverloadNestedPattern.jav");
        var clazz = classFiles.get("OverloadNestedPattern");
        var rec = classFiles.get("R");

        var instance = clazz.getDeclaredConstructor().newInstance();
        var ctor = rec.getDeclaredConstructor(Object.class);
        var r1 = ctor.newInstance(1);
        var r2 = ctor.newInstance(r1);

        var m = clazz.getDeclaredMethod("m", rec, rec);
        assertEquals(1, m.invoke(instance, r2, r1));

        var r3 = ctor.newInstance(2f);
        var r4 = ctor.newInstance(r3);
        assertEquals(2, m.invoke(instance, r4, r3));

        assertEquals(3, m.invoke(instance, r1, r1));
    }

    @Disabled("Not implemented")
    @Test
    public void testPatternMatchingHaskellStyle() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "PatternMatchingHaskellStyle.jav");
        var clazz = classFiles.get("PatternMatchingHaskellStyle");
        var R2 = classFiles.get("Elem");
        var R = classFiles.get("LinkedElem");
        var I = classFiles.get("List");

        var rctor = R.getDeclaredConstructor(Object.class, I);
        var r2ctor = R2.getDeclaredConstructor(Object.class);

        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("append$LLinkedElem$_$LTPH$_$LLinkedElem$_$LTPH$_$", R, Object.class);

        var x = rctor.newInstance(rctor.newInstance(0, rctor.newInstance(0, r2ctor.newInstance(0))), r2ctor.newInstance(0));
        var y = rctor.newInstance(r2ctor.newInstance(0), r2ctor.newInstance(0));

        assertNull(m.invoke(instance, x, y));
        assertNull(m.invoke(instance, y, y));

    }

    //@Disabled("Not implemented")
    @Test
    public void testPatternMatchingListAppend() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "PatternMatchingListAppend.jav");
        var clazz = classFiles.get("PatternMatchingListAppend");
        var instance = clazz.getDeclaredConstructor().newInstance();

        var Cons = classFiles.get("Cons");
        var Empty = classFiles.get("Empty");
        var List = classFiles.get("List");

        var ConsCtor = Cons.getDeclaredConstructor(Object.class, List);
        var EmptyCtor = Empty.getDeclaredConstructor();

        var list1 = ConsCtor.newInstance(1, ConsCtor.newInstance(2, ConsCtor.newInstance(3, EmptyCtor.newInstance())));
        var list2 = ConsCtor.newInstance(4, ConsCtor.newInstance(5, ConsCtor.newInstance(6, EmptyCtor.newInstance())));

        var append = clazz.getDeclaredMethod("append", List, List);
        System.out.println(append.invoke(instance, list1, list2));
    }

    @Test
    public void testPatternMatchingSpecificity() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "PatternMatchingSpecificity.jav");

        var clazz = classFiles.get("PatternMatchingSpecificity");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var R = classFiles.get("R");

        Integer i = 20;
        var r = R.getDeclaredConstructor(Number.class).newInstance(i);

        var m = clazz.getDeclaredMethod("m", R);
        assertEquals(1, m.invoke(instance, r));
    }

    @Test
    public void testPatternMatchingCaptures() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "PatternMatchingCaptures.jav");

        var clazz = classFiles.get("PatternMatchingCaptures");
        var R = classFiles.get("R");
        var Base = classFiles.get("Base");
        var r = R.getDeclaredConstructor(Integer.class).newInstance(10);

        var instance = clazz.getDeclaredConstructor().newInstance();
        var test1 = clazz.getDeclaredMethod("test1", Base);
        assertEquals(10, test1.invoke(instance, r));

        var test2 = clazz.getDeclaredMethod("test2", Base);
        assertEquals(10, test1.invoke(instance, r));

        var test3 = clazz.getDeclaredMethod("test3", R);
        assertEquals(10, test1.invoke(instance, r));

        var test4 = clazz.getDeclaredMethod("test4", R);
        assertEquals(10, test1.invoke(instance, r));

        var test5 = clazz.getDeclaredMethod("test5", R);
        assertEquals(10, test1.invoke(instance, r));

        var test6 = clazz.getDeclaredMethod("test6", R);
        assertEquals(10, test1.invoke(instance, r));
    }

    @Test
    public void testPatternMatchingZip() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "PatternMatching.jav");
        var clazz = classFiles.get("PatternMatching");
        var instance = clazz.getDeclaredConstructor().newInstance();

        var Cons = classFiles.get("Cons");
        var Empty = classFiles.get("Empty");
        var List = classFiles.get("List");

        var ConsCtor = Cons.getDeclaredConstructor(Object.class, List);
        var EmptyCtor = Empty.getDeclaredConstructor();

        var list1 = ConsCtor.newInstance(1, ConsCtor.newInstance(2, ConsCtor.newInstance(3, EmptyCtor.newInstance())));
        var list2 = ConsCtor.newInstance(4, ConsCtor.newInstance(5, ConsCtor.newInstance(6, EmptyCtor.newInstance())));

        var zip = clazz.getDeclaredMethod("zip", List, List);
        System.out.println(zip.invoke(instance, list1, list2));
    }

    @Disabled("Invalid file, check this later")
    @Test
    public void testPatternMatchingZipJava() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), false, "PatternMatchingJava.jav", "PatternMatchingJava2.jav");
        var clazz = classFiles.get("PatternMatchingJava");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testOverloadSwitch() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SwitchOverload.jav");
        var clazz = classFiles.get("SwitchOverload");

        var R = classFiles.get("R");
        var rctor = R.getDeclaredConstructor(Number.class);

        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("m", R, Integer.class);

        var x = rctor.newInstance(10);
        var d = rctor.newInstance(20.0);

        assertEquals(50, m.invoke(instance, x, 0));
        assertEquals(40.0, m.invoke(instance, d, 0));
    }

    @Test
    public void testInterfaces() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Interfaces.jav");
        var clazz = classFiles.get("Interfaces");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testStatic() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Static.jav");
        var clazz = classFiles.get("Static");
        var m = clazz.getDeclaredMethod("m");
        assertEquals(50, m.invoke(null));
    }

    @Test
    public void testFor() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "For.jav");
        var clazz = classFiles.get("For");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("m", Integer.class);
        assertEquals(60, m.invoke(instance, 10));
    }

    @Test
    public void testForEach() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "ForEach.jav");
        var clazz = classFiles.get("ForEach");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("m");
        assertEquals(6, m.invoke(instance));
    }

    @Test
    public void testLambdaRunnable() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "LambdaRunnable.jav");
        var clazz = classFiles.get("LambdaRunnable");
        var instance = clazz.getDeclaredConstructor().newInstance();
        //var m = clazz.getDeclaredMethod("m", Integer.class);
        //assertEquals(m.invoke(instance, 10), 60);
    }

    @Test
    public void testFunctionalInterface() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "FunctionalInterface.jav");
        var clazz = classFiles.get("FunctionalInterface");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("m");
        assertEquals(200, m.invoke(instance));
    }

    @Test
    public void testChain() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Chain.jav");
        var clazz = classFiles.get("Chain");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("m");
        assertEquals(5, m.invoke(instance));
    }

    @Test
    public void testHelloWorld() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "HelloWorld.jav");
        var clazz = classFiles.get("HelloWorld");
        var hello = clazz.getDeclaredMethod("hello");
        hello.invoke(null);
    }

    @Test
    public void testSuperCall() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "SuperCall.jav");
        var clazz = classFiles.get("SuperCall");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testInstanceOf() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "InstanceOf.jav");
        var clazz = classFiles.get("InstanceOf");
        var instance = clazz.getDeclaredConstructor().newInstance();

        assertTrue((Boolean) clazz.getDeclaredMethod("test1").invoke(instance));
        assertTrue((Boolean) clazz.getDeclaredMethod("test2").invoke(instance));
        assertFalse((Boolean) clazz.getDeclaredMethod("test3").invoke(instance));
    }

    @Test
    public void testExceptions() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Exceptions.jav");
        var clazz = classFiles.get("Exceptions");
        var instance = clazz.getDeclaredConstructor().newInstance();

        try {
            clazz.getDeclaredMethod("m").invoke(instance);
            fail("No exception thrown!");
        } catch (InvocationTargetException exception) {
            var exc = exception.getTargetException();
            if (!(exc instanceof RuntimeException rexp) || !rexp.getMessage().equals("Some Exception")) {
                fail("Wrong exception thrown!");
            }
        }
    }

    @Test
    public void testLiteral() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Literal.jav");
        var clazz = classFiles.get("Literal");
        var instance = clazz.getDeclaredConstructor().newInstance();

        assertNull(clazz.getDeclaredMethod("m").invoke(instance));
        assertEquals('C', clazz.getDeclaredMethod("m2").invoke(instance));
        assertEquals(10L, clazz.getDeclaredMethod("m3").invoke(instance));
        assertEquals(10.5F, clazz.getDeclaredMethod("m4").invoke(instance));
    }

    @Test
    public void testOLConstructor() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "OLConstructor.jav");
        var clazz = classFiles.get("Child");
        var instance = clazz.getDeclaredConstructor().newInstance();
        assertEquals(3, clazz.getSuperclass().getDeclaredField("x").get(instance));
    }

    @Test
    public void testOperators() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Op1.jav");
        var clazz = classFiles.get("Op1");
        var instance = clazz.getDeclaredConstructor().newInstance();
        assertEquals(true, clazz.getDeclaredMethod("not").invoke(instance));
        assertEquals(10 | 20, clazz.getDeclaredMethod("or").invoke(instance));
        assertEquals(10 & 20, clazz.getDeclaredMethod("and").invoke(instance));
        assertEquals(10 ^ 20, clazz.getDeclaredMethod("xor").invoke(instance));
        assertEquals(10 % 2, clazz.getDeclaredMethod("mod").invoke(instance));
    }

    @Disabled("Not implemented")
    @Test
    public void testStringConcat() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Op2.jav");
        var clazz = classFiles.get("Op2");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testAccess() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Access.jav");
        var clazzPublic = classFiles.get("Access");
        var clazzDefault = classFiles.get("AccessDefault");

        assertEquals(Modifier.PUBLIC, clazzPublic.getModifiers());
        assertEquals(0, clazzDefault.getModifiers());

        assertEquals(Modifier.PUBLIC, clazzPublic.getDeclaredMethod("mPublic").getModifiers());
        assertEquals(Modifier.PROTECTED, clazzPublic.getDeclaredMethod("mProtected").getModifiers());
        assertEquals(0, clazzPublic.getDeclaredMethod("mDefault").getModifiers());
        assertEquals(Modifier.PRIVATE, clazzPublic.getDeclaredMethod("mPrivate").getModifiers());

        assertEquals(Modifier.PUBLIC, clazzPublic.getDeclaredField("fPublic").getModifiers());
        assertEquals(Modifier.PROTECTED, clazzPublic.getDeclaredField("fProtected").getModifiers());
        assertEquals(0, clazzPublic.getDeclaredField("fDefault").getModifiers());
        assertEquals(Modifier.PRIVATE, clazzPublic.getDeclaredField("fPrivate").getModifiers());
    }

    @Test
    public void testTypeCast() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "TypeCast.jav");
        var clazz = classFiles.get("TypeCast");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testAnnotation() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Annotation.jav");
        var clazz = classFiles.get("Annotation");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testWilcards() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Wildcards.jav");
        var clazz = classFiles.get("Wildcards");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testOverrideEquals() throws Exception {
        var loader = createClassLoader();
        loader.loadClass(TestCodegen.path.resolve("OverrideRoot.class"));
        var classFiles = generateClassFiles(loader, "OverrideEquals.jav");
        var clazz = classFiles.get("OverrideEquals");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testTernary() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Ternary.jav");
        var clazz = classFiles.get("Ternary");
        var instance = clazz.getDeclaredConstructor().newInstance();
        assertEquals("small", clazz.getDeclaredMethod("main", Integer.class).invoke(instance, 5));
    }

    @Test
    public void testWhile() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "While.jav");
        var clazz = classFiles.get("While");
        var instance = clazz.getDeclaredConstructor().newInstance();
        assertEquals(5, clazz.getDeclaredMethod("m", Integer.class).invoke(instance, 5));
        assertEquals(10, clazz.getDeclaredMethod("m2").invoke(instance));
    }

    @Test
    public void testImportWildcard() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "ImportWildcard.jav");
        var clazz = classFiles.get("ImportWildcard");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testAssign() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Assign.jav");
        var clazz = classFiles.get("Assign");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("m");
        assertEquals(20, m.invoke(instance));
    }

    @Test
    public void testMain() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Main.jav");
        var clazz = classFiles.get("Main");
        var m = clazz.getDeclaredMethod("main", List.class);
        m.setAccessible(true);
        m.invoke(null, List.of("foo", "bar", "baz"));
    }

    @Test
    public void testBug122() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug122.jav");
        var clazz = classFiles.get("Bug122");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug123() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug123.jav");
        var clazz = classFiles.get("Bug123");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug125() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug125.jav");
        var clazz = classFiles.get("Bug125");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug112() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug112.jav");
        var clazz = classFiles.get("Bug112");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug285() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug285.jav");
        var clazz = classFiles.get("Bug285");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    @Disabled("Functionality for loading jav files was removed, this is no longer supposed to work")
    public void testBug290() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug290A.jav");
        var clazz = classFiles.get("Bug290A");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug293() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug293.jav");
        var clazz = classFiles.get("Bug293");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug295() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug295.jav");
        var clazz = classFiles.get("Bug295");
        var instance = clazz.getDeclaredConstructor(Integer.class, Integer.class, Integer.class).newInstance(1, 2, 3);
        assertEquals(1, clazz.getDeclaredField("a").get(instance));
        assertEquals(2, clazz.getDeclaredField("b").get(instance));
        assertEquals(3, clazz.getDeclaredField("c").get(instance));
    }

    @Test
    public void testBug296() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug296.jav");
        var clazz = classFiles.get("Bug296");
        clazz.getDeclaredMethod("m1").invoke(null);
    }

    @Test
    public void testBug297() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug297.jav");
        var clazz = classFiles.get("Bug297");
        var instance = clazz.getDeclaredConstructor().newInstance();
        clazz.getDeclaredMethod("exec").invoke(instance);
    }

    @Test
    public void testBug298() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug298.jav");
        var clazz = classFiles.get("Bug298");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug300() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug300.jav");
        var clazz = classFiles.get("Bug300");
        var instance = clazz.getDeclaredConstructor().newInstance();
        assertEquals("Base", clazz.getDeclaredMethod("m").invoke(instance));
    }

    @Test
    public void testBug301() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug301.jav");
        var clazz = classFiles.get("Bug301");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug302() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug302.jav");
        var clazz = classFiles.get("Bug302");
        clazz.getDeclaredMethod("m").invoke(null);
    }

    @Test
    public void testBug306() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug306.jav");
        var clazz = classFiles.get("Bug306");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug307() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug307.jav");
        var clazz = classFiles.get("Bug307");
        var instance = clazz.getDeclaredConstructor().newInstance();
        clazz.getDeclaredMethod("main").invoke(instance);
    }

    @Test
    public void testBug309() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug309.jav");
        var clazz = classFiles.get("Bug309");
        var instance = clazz.getDeclaredConstructor().newInstance();
        clazz.getDeclaredMethod("main").invoke(instance);
    }

    @Test
    public void testBug310() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug310.jav");
        var clazz = classFiles.get("Bug310");
        var instance = clazz.getDeclaredConstructor().newInstance();
        assertEquals("3", clazz.getDeclaredMethod("toString").invoke(instance));
    }

    @Test
    public void testBug311() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug311.jav");
        var clazz = classFiles.get("Bug311");
        var instance = clazz.getDeclaredConstructor().newInstance();
        clazz.getDeclaredMethod("toString").invoke(instance);
    }

    @Test
    public void testBug312() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug312.jav");
        var clazz = classFiles.get("Bug312");
        var instance = clazz.getDeclaredConstructor().newInstance();
        clazz.getDeclaredMethod("main").invoke(instance);
    }

    @Test
    public void testBug314() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug314.jav");
        var clazz = classFiles.get("Bug314");
        var instance = clazz.getDeclaredConstructor().newInstance();

        var list = List.of(3, 4, 6, 7, 8);
        var res = clazz.getDeclaredMethod("convert", List.class).invoke(instance, list);
        assertEquals(res, List.of(6, 7, 8));
    }

    @Test
    @Disabled("too slow")
    public void testBug325() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug325.jav");
        var clazz = classFiles.get("Bug325");
        var instance = clazz.getDeclaredConstructor().newInstance();
        clazz.getDeclaredMethod("main").invoke(instance);
    }

    @Test
    public void testBug326() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug326.jav");
        var clazz = classFiles.get("Bug326");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug328() throws Exception {
        var loader = createClassLoader();
        loader.loadClass(TestCodegen.path.resolve("Bug328B.class"));
        var classFiles = generateClassFiles(loader, "Bug328.jav");
        var clazz = classFiles.get("Bug328");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug332() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug332.jav");
        var clazz = classFiles.get("Bug332");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug333() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug333.jav");
        var clazz = classFiles.get("Bug333");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug337() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug337.jav");
        var clazz = classFiles.get("Bug337");
        var instance = clazz.getDeclaredConstructor().newInstance();
        clazz.getDeclaredMethod("main").invoke(instance);
    }

    @Test
    public void testBug338() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug338.jav");
        var clazz = classFiles.get("Bug338");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug343() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug343.jav");
        var clazz = classFiles.get("Bug343");
        var instance = clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug363() throws Exception{
        var classFiles = generateClassFiles(createClassLoader(), "Bug363.jav");
        var clazz = classFiles.get("Bug363");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var test = clazz.getDeclaredMethod("test");
        assertEquals("ABC", test.invoke(instance));
    }

    @Test
    public void testBug364() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug364.jav");
        var clazz = classFiles.get("Bug364");
        var instance = clazz.getDeclaredConstructor().newInstance();
        assertEquals("ABC", clazz.getDeclaredMethod("main").invoke(instance));
    }

    @Test
    public void testBug365() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug365.jav");
        var clazz = classFiles.get("Bug365");
        var instance = clazz.getDeclaredConstructor().newInstance();
        var m = clazz.getDeclaredMethod("ex1");
        assertEquals("ABC", m.invoke(instance));
        var ex2 = clazz.getDeclaredMethod("ex2");
        assertEquals("BAC", ex2.invoke(instance));
    }
    @Test
    public void testBug366() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug366.jav");
        var clazz = classFiles.get("Bug366");
        var m = clazz.getDeclaredMethod("test");
        assertEquals(30, m.invoke(null));
    }

    @Test
    public void testBug371() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug371.jav");
        var clazz = classFiles.get("Bug371");
        var m = clazz.getDeclaredMethod("test");
        assertEquals(true, m.invoke(null));
    }

    @Test
    public void testBug373() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug373.jav");
        var clazz = classFiles.get("Bug373");
        var m = clazz.getDeclaredMethod("main");
        m.invoke(null);
    }

    @Disabled("Doesn't work yet")
    @Test
    public void testBug378() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug378Main.jav");
        var clazz = classFiles.get("Bug378Main");
        var main = clazz.getDeclaredMethod("main", List.class);
        main.invoke(null, List.of());
    }

    @Test
    public void testBug379() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug379.jav");
        var clazz = classFiles.get("Bug379");
        clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug380() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug380.jav");
        var clazz = classFiles.get("Bug380");
        clazz.getDeclaredConstructor().newInstance();
    }

    @Test
    public void testBug382() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug382.jav");
        var clazz = classFiles.get("Bug382");
        clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
    }

    @Test
    public void testBug383() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug383.jav");
        var clazz = classFiles.get("Bug383");
        clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
        // TODO This logs output that we should validate
    }

    @Test
    public void testBug389() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), false, "Bug389.jav", "Bug389Main.jav");
        var clazz = classFiles.get("Bug389Main");
        clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
    }

    @Test
    public void testBug390() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug390.jav");
        var clazz = classFiles.get("Bug390");
        clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
    }

    @Test
    public void testBug392() throws Exception {
        var classFiles = generateClassFiles(createClassLoader(), "Bug392.jav");
        var clazz = classFiles.get("Bug392");
        clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
    }
}
