package de.dhbwstuttgart.parser.SyntaxTreeGenerator;

import de.dhbwstuttgart.exceptions.TypeinferenceException;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.antlr.Java8Parser;
import de.dhbwstuttgart.syntaxtree.GTVDeclarationContext;
import de.dhbwstuttgart.syntaxtree.GenericDeclarationList;
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
import de.dhbwstuttgart.syntaxtree.type.GenericRefType;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.typecheck.GenericTypeName;
import de.dhbwstuttgart.typecheck.JavaClassName;
import de.dhbwstuttgart.typecheck.JavaClassRegistry;
import org.antlr.v4.runtime.Token;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

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

public class TypeGenerator {

    public static RefTypeOrTPHOrWildcardOrGeneric convert(Java8Parser.UnannClassOrInterfaceTypeContext unannClassOrInterfaceTypeContext, JavaClassRegistry reg, GenericsRegistry generics) {
        String name = unannClassOrInterfaceTypeContext.getText();
        return convertTypeName(name,unannClassOrInterfaceTypeContext.getStart(), reg, generics);
    }

    public static RefTypeOrTPHOrWildcardOrGeneric convert(Java8Parser.UnannTypeContext unannTypeContext, JavaClassRegistry reg, GenericsRegistry genericsRegistry) {
        if(unannTypeContext.unannPrimitiveType()!=null){
            throw new NotImplementedException();
        }else
        if(unannTypeContext.unannReferenceType().unannArrayType()!=null){
            throw new NotImplementedException();
        }else
        if(unannTypeContext.unannReferenceType().unannTypeVariable()!=null){
            JavaClassName name = reg.getName(unannTypeContext.unannReferenceType().unannTypeVariable().Identifier().toString());
            return new RefType(name, unannTypeContext.getStart());
        }
        return TypeGenerator.convert(unannTypeContext.unannReferenceType().unannClassOrInterfaceType(), reg, genericsRegistry);
    }

    public static GenericDeclarationList convert(Java8Parser.TypeParametersContext typeParametersContext, JavaClassName parentClass, String parentMethod, JavaClassRegistry reg, GenericsRegistry generics) {
        if(typeParametersContext == null){
            return new GenericDeclarationList(new ArrayList<>(), new NullToken());
        }
        Token endOffset = typeParametersContext.getStop();
        List<GenericTypeVar> typeVars = new ArrayList<>();
        for(Java8Parser.TypeParameterContext typeParameter : typeParametersContext.typeParameterList().typeParameter()){
            typeVars.add(convert(typeParameter, parentClass, parentMethod, reg, generics));
            endOffset = typeParameter.getStop();
        }
        return new GenericDeclarationList(typeVars, endOffset);
    }

    public static GenericTypeVar convert(Java8Parser.TypeParameterContext typeParameter, JavaClassName parentClass, String parentMethod, JavaClassRegistry reg, GenericsRegistry generics) {
        String name = typeParameter.Identifier().getText();
        List<RefTypeOrTPHOrWildcardOrGeneric> bounds = TypeGenerator.convert(typeParameter.typeBound(),reg, generics);

        GenericTypeVar ret = new GenericTypeVar(new GenericTypeName(new GenericContext(parentClass, parentMethod), name), bounds, typeParameter.getStart(), typeParameter.getStop());
        generics.put(name, new GenericContext(parentClass, parentMethod));
        return ret;
    }

    public static List<RefTypeOrTPHOrWildcardOrGeneric> convert(Java8Parser.TypeBoundContext typeBoundContext, JavaClassRegistry reg, GenericsRegistry generics) {
        if(typeBoundContext == null){
            List<RefTypeOrTPHOrWildcardOrGeneric> ret = new ArrayList<>();
            ret.add(new ASTFactory(reg).createObjectClass().getType());
            return ret;
        }

        throw new de.dhbwstuttgart.exceptions.NotImplementedException();
    }

    public static RefTypeOrTPHOrWildcardOrGeneric convert(Java8Parser.ReferenceTypeContext referenceTypeContext, JavaClassRegistry reg, GenericsRegistry generics) {
        return convertTypeName(referenceTypeContext.getText(), referenceTypeContext.getStart(), reg, generics);
    }

    public static RefTypeOrTPHOrWildcardOrGeneric convertTypeName(String name, Token offset, JavaClassRegistry reg, GenericsRegistry generics){
        if(!reg.contains(name)){ //Dann könnte es ein Generische Type sein:
            if(generics.keySet().contains(name)){
                return new GenericRefType(new GenericTypeName(generics.get(name),name), offset);
            }else{
                throw new TypeinferenceException("Der Typ "+ name + " ist nicht vorhanden",offset);
            }
        }
        return new RefType(reg.getName(name), offset);
    }

}
