import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.GenericContext;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.GenericDeclarationList;
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import org.junit.jupiter.api.Test;

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

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

public class GenericsParserTest {

    @Test
    public void testMethodNoGenerics() {
        var signature = "()V";
        var generics = ASTFactory.createGenerics(signature);
        assertEquals(generics, new GenericDeclarationList(List.of(), new NullToken()));
    }

    @Test
    public void testMethodSimpleGenerics() {
        var signature = "<T:Ljava/lang/Object;>()V";
        var generics = ASTFactory.createGenerics(signature);
        assertEquals(generics, new GenericDeclarationList(
                List.of(new GenericTypeVar("T", List.of(ASTToTargetAST.OBJECT), new NullToken(), new NullToken())), new NullToken())
        );
    }

    @Test
    public void testMethodExtends() {
        var signature = "<T:Ljava/lang/Class<TT;>;>()V";
        var generics = ASTFactory.createGenerics(signature);
        assertEquals(generics, new GenericDeclarationList(
                List.of(new GenericTypeVar("T", List.of(
                            new RefType(new JavaClassName("java.lang.Class"), List.of(new GenericRefType("T", new NullToken())), new NullToken())),
                        new NullToken(), new NullToken())), new NullToken()
                )
        );
    }

    @Test
    public void testMethodVariance() {
        var signature = "<T:Ljava/lang/Class<+TT;>;>()V";
        var generics = ASTFactory.createGenerics(signature);

        assertEquals(generics, new GenericDeclarationList(
                List.of(new GenericTypeVar("T", List.of(
                        new RefType(new JavaClassName("java.lang.Class"), List.of(
                                new ExtendsWildcardType(new GenericRefType("T", new NullToken()), new NullToken())
                        ), new NullToken())),
                        new NullToken(), new NullToken())),
                new NullToken())
        );
    }

    @Test
    public void testMethodTypeVars() {
        var signature = "<A:TB;B:LClassA;C:LClassB<TA;>;D:TB;>()V";
        var generics = ASTFactory.createGenerics(signature);
        assertEquals(generics, new GenericDeclarationList(
                List.of(
                        new GenericTypeVar("A", List.of(new GenericRefType("B", new NullToken())), new NullToken(), new NullToken()),
                        new GenericTypeVar("B", List.of(new RefType(new JavaClassName("ClassA"), new NullToken())), new NullToken(), new NullToken()),
                        new GenericTypeVar("C", List.of(new RefType(new JavaClassName("ClassB"),
                                List.of(new GenericRefType("A", new NullToken())),
                                new NullToken())), new NullToken(), new NullToken()
                        ),
                        new GenericTypeVar("D", List.of(new GenericRefType("B", new NullToken())), new NullToken(), new NullToken())
                ),
                new NullToken()
        ));
    }

    @Test
    public void testMethodComplex() {
        var signature = "<T:LClassA<+TT;LClassB<+LClassA;-LClassC<LClassA;>;>;>;U:LClassC<LClassC;>;>()V";
        var generics = ASTFactory.createGenerics(signature);

        assertEquals(generics, new GenericDeclarationList(
                List.of(new GenericTypeVar("T", List.of(
                        new RefType(new JavaClassName("ClassA"), List.of(
                                new ExtendsWildcardType(new GenericRefType("T", new NullToken()), new NullToken()),
                                new RefType(new JavaClassName("ClassB"), List.of(
                                        new ExtendsWildcardType(new RefType(new JavaClassName("ClassA"), new NullToken()), new NullToken()),
                                        new SuperWildcardType(
                                                new RefType(new JavaClassName("ClassC"), List.of(
                                                        new RefType(new JavaClassName("ClassA"), new NullToken())
                                                ), new NullToken()), new NullToken())
                                ), new NullToken())
                        ), new NullToken())
                ), new NullToken(), new NullToken()),
                        new GenericTypeVar("U", List.of(
                                new RefType(new JavaClassName("ClassC"), List.of(
                                        new RefType(new JavaClassName("ClassC"), new NullToken())
                                ), new NullToken())
                        ), new NullToken(), new NullToken())),
                new NullToken()
        ));
    }
}
