package de.dhbwstuttgart.parser.SyntaxTreeGenerator;

import de.dhbwstuttgart.parser.JavaTXParser;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.type.Void;
import de.dhbwstuttgart.target.tree.expression.TargetUnaryOp;
import de.dhbwstuttgart.target.generate.StatementToTargetExpression;
import org.antlr.v4.runtime.Token;

import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.antlr.Java17Parser;
import de.dhbwstuttgart.parser.antlr.Java17Parser.AndexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.ArrayaccessexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.AssertstmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.AssignexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwiseandexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwiseorexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwisexorexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.BlockstmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.BoolLiteralContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.BreakstmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.CastexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.CharLiteralContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.ConditionalassignexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.ConditionalstmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.ContinuestmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.DottedexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.DowhileloopContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.EqualityexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.ExpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.FltLiteralContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.ForloopContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.GPatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.GuardedPatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.IdentifierContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.InstanceofexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.IntLiteralContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.LabeledRuleDefaultContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.LabeledRuleExprListContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.LabeledRulePatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.LabeledstmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.LambdaLVTIParameterContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.LambdaExpression2Context;
import de.dhbwstuttgart.parser.antlr.Java17Parser.MathaddsubexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.MathmuldivmodexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.MethodCallContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.MethodcallexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.NewinstanceexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.NullLiteralContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.OrexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PPatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PostfixexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PrefixexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PrimaryClassrefContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PrimaryExpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PrimaryIdentifierContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PrimaryLiteralContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PrimaryPatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PrimarySuperContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PrimaryThisContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.PrimaryExpression2Context;
import de.dhbwstuttgart.parser.antlr.Java17Parser.RPatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.RecordPatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.RelationalexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.ReturnstmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SemistmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.ShiftexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.StmtexpressionContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.StringLiteralContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SwitchBlockStatementGroupContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SwitchLabelConstContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SwitchLabelContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SwitchLabelDefaultContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SwitchLabelPatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SwitchLabeledRuleContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SwitchRuleOutcomeContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SwitchExpression2Context;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SwitchstmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SynchronizedstmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.TPatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.ThrowstmtContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.TrycatchblockContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.TrycatchresourceContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.TypePatternContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.WhileloopContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.YieldstmtContext;
import de.dhbwstuttgart.parser.scope.GenericsRegistry;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.parser.scope.JavaClassRegistry;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;

public class StatementGenerator {

    private final JavaClassRegistry reg;

    private final Map<String, FieldEntry> fields; // PL 2018-11-01 fields eingefuegt, damit die fields
                                                                 // immer die gleiche TPH bekommen
    private final Map<String, RefTypeOrTPHOrWildcardOrGeneric> localVars;
    private final GenericsRegistry generics;
    private final JavaTXCompiler compiler;

    private final RefType superClass;

    // TODO How about instead of passing all of these types we just pass an instance of the SyntaxTreeGenerator?
    public StatementGenerator(RefType superType, JavaTXCompiler compiler, JavaClassRegistry reg, GenericsRegistry generics, Map<String, FieldEntry> fields, Map<String, RefTypeOrTPHOrWildcardOrGeneric> localVars) {
        this.reg = reg;
        this.generics = generics;
        this.fields = fields;
        this.localVars = localVars;
        this.compiler = compiler;
        this.superClass = superType;
    }

    public ParameterList convert(Java17Parser.FormalParameterListContext formalParameterListContext, boolean methodparameters) {
        List<Pattern> ret = new ArrayList<>();
        List<Java17Parser.FormalParameterContext> fps = new ArrayList<>();
        if (Objects.isNull(formalParameterListContext))
            return new ParameterList(ret, new NullToken()); // Dann ist die Parameterliste leer
        if (!Objects.isNull(formalParameterListContext.lastFormalParameter())) {
            /*
             * Der '...' Operator wird noch nicht unterstützt, da dafür benötigte Typen (List oder Array) nicht vom Typinferenzalgo. verarbeitet werden können
             */
            throw new NotImplementedException("Formale Parameter variabler Länge nicht unterstützt.");
        }

        fps = formalParameterListContext.formalParameter();
        for (Java17Parser.FormalParameterContext fp : fps) {
            if (fp.pattern() != null) {
                ret.add(convert(fp.pattern()));
            } else {
                String paramName = SyntaxTreeGenerator.convert(fp.variableDeclaratorId());
                RefTypeOrTPHOrWildcardOrGeneric type;
                if (fp.typeType() != null) {
                    type = TypeGenerator.convert(fp.typeType(), reg, generics);
                } else {
                	type = TypePlaceholder.fresh(fp.getStart(), 1, false);
                }
                ret.add(new FormalParameter(paramName, type, fp.getStart()));
                localVars.put(paramName, type); 
            }
        }
        return new ParameterList(ret, ret.get(0).getOffset());
    }

    /*
     * StatementGeneration:
     */

    private Statement convert(Java17Parser.StatementContext stmt) {
        switch (stmt) {
        case BlockstmtContext blockstmt:
            return convert(blockstmt.block(), false);
        case ConditionalstmtContext condition:
            return convert(condition);
        case WhileloopContext whileloop:
            return convert(whileloop);
        case DowhileloopContext dowhileloop:
            return convert(dowhileloop);
        case SwitchstmtContext switchstmt:
            return convert(switchstmt);
        case ReturnstmtContext returnstmt:
            return convert(returnstmt);
        case YieldstmtContext yieldstmt:
            return convert(yieldstmt);
        case StmtexpressionContext stmtexpression:
            return convert(stmtexpression);
        case AssertstmtContext assertstmt:
            return convert(assertstmt);
        case ForloopContext forloop:
            return convert(forloop);
        case TrycatchblockContext trycatchblock:
            return convert(trycatchblock);
        case TrycatchresourceContext trycatchresource:
            return convert(trycatchresource);
        case SynchronizedstmtContext synchronizedstmt:
            return convert(synchronizedstmt);
        case ThrowstmtContext throwstmt:
            return convert(throwstmt);
        case BreakstmtContext breakstmt:
            return convert(breakstmt);
        case ContinuestmtContext continuestmt:
            return convert(continuestmt);
        case SemistmtContext semistmt:
            return convert(semistmt);
        case LabeledstmtContext labeledstmt:
            return convert(labeledstmt);
        default:
            throw new NotImplementedException();
        }
    }

    public Block convert(Java17Parser.BlockContext block, boolean addTrailingReturn) {
        List<Statement> statements = new ArrayList<>();
        if (block.blockStatement().size() > 0)
            for (Java17Parser.BlockStatementContext statementContext : block.blockStatement()) {
                statements.addAll(convert(statementContext));
            }
        if (addTrailingReturn)
            statements = SyntacticSugar.addTrailingReturn(statements);
        return new Block(statements, block.getStart());
    }

    private List<Statement> convert(Java17Parser.BlockStatementContext statementContext) {
        if (!Objects.isNull(statementContext.localVariableDeclaration())) {
            return convert(statementContext.localVariableDeclaration());
        } else if (!Objects.isNull(statementContext.localTypeDeclaration())) {
            throw new NotImplementedException();
        } else {
            return Arrays.asList(convert(statementContext.statement()));
        }
    }

    private Statement convert(Java17Parser.LabeledstmtContext labeledStatementContext) {
        throw new NotImplementedException();
        // return convert(labeledStatementContext.statement());
    }

    private Statement convert(Java17Parser.StmtexpressionContext stmt) {
        ExpressionContext expr = stmt.statementExpression;
        Token offset = stmt.getStart();
        switch (expr) {
        case DottedexpressionContext dotted:
            return convertToStatement(dotted, offset);
        case MethodcallexpressionContext methodc:
            return convert(methodc.methodCall(), offset);
        case NewinstanceexpressionContext newinstance:
            return convert(newinstance.creator());
        case AssignexpressionContext assignment:
            Statement ret = convert(assignment);
            ret.setStatement();
            return ret;
        case PostfixexpressionContext postfix:
            ret = convert(postfix);
            ret.setStatement();
            return ret;
        case PrefixexpressionContext prefix:
            ret = convert(prefix);
            ret.setStatement();
            return ret;
        case SwitchExpression2Context switchexpr:
            ret = convert(switchexpr);
            ret.setStatement();
            return ret;
        default:
            JavaTXParser.logger.info(stmt.getClass());
            throw new NotImplementedException();
        }
    }

    private Statement convertToStatement(DottedexpressionContext expr, Token offset) {
        if (!Objects.isNull(expr.methodCall())) {
            return convert(expr.methodCall(), expr.expression(), offset);
        } else if (!Objects.isNull(expr.NEW())) {
            return convert(expr.innerCreator());
        } else {
            throw new NotImplementedException();
        }
    }

    private Statement convert(Java17Parser.CreatorContext creator) {
        if (creator.nonWildcardTypeArguments() != null)
            throw new NotImplementedException();

        RefType newclass = convert(creator.createdName());

        if (!Objects.isNull(creator.classCreatorRest())) {
            ArgumentList args = convertArguments(creator.classCreatorRest().arguments().expressionList());
            ArrayList<TypePlaceholder> argTypes = args.getArguments().stream().map(x -> TypePlaceholder.fresh(creator.getStart())).collect(Collectors.toCollection(ArrayList::new));
            argTypes.add(TypePlaceholder.fresh(creator.getStart())); // return type
            Statement ret = new NewClass(newclass, args, null, argTypes, creator.getStart());
            ret.setStatement();
            return ret;
        } else {
            return convert(creator.arrayCreatorRest());
        }
    }

    private Statement convert(Java17Parser.ArrayCreatorRestContext expression) {
        throw new NotImplementedException();
    }

    private RefType convert(Java17Parser.CreatedNameContext createdname) {
        Java17Parser.TypeArgumentsContext genericArgs = null;
        genericArgs = null;
        if (createdname.typeArgumentsOrDiamond() != null)
            genericArgs = createdname.typeArgumentsOrDiamond().typeArguments();
        IdentifierContext identifier = createdname.identifier();
        return (RefType) TypeGenerator.convertTypeName(identifier.getText(), genericArgs, identifier.getStart(), reg, generics);
    }

    private Statement convert(Java17Parser.InnerCreatorContext innercreator) {
        Java17Parser.NonWildcardTypeArgumentsContext genericArgs = null;

        if (!Objects.isNull(innercreator.nonWildcardTypeArgumentsOrDiamond())) {
            genericArgs = innercreator.nonWildcardTypeArgumentsOrDiamond().nonWildcardTypeArguments();
        }
        IdentifierContext identifier = innercreator.identifier();
        RefType newclass = (RefType) TypeGenerator.convertTypeName(identifier.getText(), genericArgs, identifier.getStart(), reg, generics);
        ArgumentList args = convertArguments(innercreator.classCreatorRest().arguments().expressionList());
        ArrayList<TypePlaceholder> argTypes = args.getArguments().stream().map(x -> TypePlaceholder.fresh(innercreator.getStart())).collect(Collectors.toCollection(ArrayList::new));
        Statement ret = new NewClass(newclass, args, null, argTypes, innercreator.getStart());
        ret.setStatement();
        return ret;

    }

    private Statement convert(Java17Parser.ConditionalstmtContext stmt) {
        Expression expr = convert(stmt.parExpression().expression());
        Statement thenBlock = convert(stmt.statement(0));
        Statement elseBlock = (stmt.statement().size() >= 2) ? convert(stmt.statement(1)) : null;
        return new IfStmt(TypePlaceholder.fresh(stmt.getStart()), expr, thenBlock, elseBlock, stmt.getStart());
    }

    private Statement convert(Java17Parser.AssertstmtContext stmt) {
        // TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java17Parser.SwitchstmtContext stmt) {
        Expression switched = convert(stmt.parExpression().expression());
        List<SwitchBlock> switchBlocks = new ArrayList<>();
        for (SwitchBlockStatementGroupContext blockstmt : stmt.switchBlockStatementGroup()) {
            switchBlocks.add(convert(blockstmt));
        }
        return new Switch(switched, switchBlocks, TypePlaceholder.fresh(switched.getOffset()), true, stmt.getStart());
    }

    // Um switchExpressions als Statement zu behandeln
    private Statement convert(Java17Parser.SwitchExpression2Context switchexpression) {
        Expression switchExpr = convert(switchexpression.switchExpression());
        if (switchExpr instanceof Switch s) {
            s.setStatement();
            return s;
        } else {
            // sollte nie vorkommen, da convert(Java17Parser.SwitchExpressionContext switchExpression) eine Instanz von Switch zurückgibt
            throw new RuntimeException();
        }
    }

    private Expression convert(Java17Parser.SwitchExpressionContext switchExpression) {
        Expression switched = convert(switchExpression.parExpression().expression());
        List<SwitchBlock> switchBlocks = new ArrayList<>();
        Token offset = switchExpression.getStart();
        for (SwitchLabeledRuleContext labeledRule : switchExpression.switchLabeledRule()) {
            switchBlocks.add(convert(labeledRule));
        }
        return new Switch(switched, switchBlocks, TypePlaceholder.fresh(offset), false, offset);
    }

    private SwitchBlock convert(SwitchLabeledRuleContext labeledRule) {
        Boolean isDefault = false;
        List<SwitchLabel> labels = switch (labeledRule.switchLabelCase()) {
        case LabeledRuleExprListContext exprList -> {
            List<SwitchLabel> labelList = exprList.expressionList().expression().stream().map((exprctx) -> {
                Pattern expr = new ExpressionPattern(convert(exprctx), exprList.getStart());
                return new SwitchLabel(expr, expr.getType(), exprList.getStart());
            }).toList();
            yield labelList;
        }
        case LabeledRulePatternContext pattern -> {
            Pattern p = convert(pattern.pattern());
            yield Arrays.asList(new SwitchLabel(p, p.getType(), pattern.getStart()));
        }
        case LabeledRuleDefaultContext def -> {
            isDefault = true;
            yield Arrays.asList(new SwitchLabel(new Void(def.getStart()), def.getStart()));
        }
        default -> throw new NotImplementedException();
        };

        var isSingleExpression = false;
        Token offset = labeledRule.getStart();
        SwitchRuleOutcomeContext outcome = labeledRule.switchRuleOutcome();
        Block block;
        if (Objects.isNull(outcome.block())) {
            List<Statement> stmts = new ArrayList<>();
            stmts.add(new Yield(convert(outcome.expression()), outcome.expression().start));
            isSingleExpression = true;
            block = new Block(stmts, outcome.expression().getStart());
        } else {
            block = convert(outcome.block(), false);
        }
        return new SwitchBlock(labels, block, isDefault, isSingleExpression, offset);
    }

    private Statement convert(Java17Parser.YieldstmtContext yieldstmt) {
        return new Yield(convert(yieldstmt.expression()), yieldstmt.getStart());
    }

    private SwitchBlock convert(Java17Parser.SwitchBlockStatementGroupContext stmt) {
        List<SwitchLabel> labels = new ArrayList<>();
        stmt.switchLabel().forEach((label) -> {
            labels.add(convert(label));
        });
        List<Statement> block = new ArrayList<>();
        stmt.blockStatement().stream().forEach((blockStmt) -> {
            block.addAll(convert(blockStmt));
        });
        return new SwitchBlock(labels, new Block(block, stmt.blockStatement(0).getStart()), false, stmt.getStart());
    }

    private SwitchLabel convert(SwitchLabelContext switchLabel) {
        RefTypeOrTPHOrWildcardOrGeneric type = null;
        Pattern caseExpression = switch (switchLabel) {
        case SwitchLabelConstContext cons -> {
            yield new ExpressionPattern(convert(cons.constantExpression), cons.getStart());
        }
        case SwitchLabelPatternContext pattern -> {
            yield convert(pattern.pattern());
        }
        case SwitchLabelDefaultContext def -> {
            type = new Void(switchLabel.getStart());
            yield null;
        }
        default -> throw new NotImplementedException();
        };
        Token offset = switchLabel.getStart();
        if (Objects.isNull(caseExpression)) {
            if (type == null)
                type = TypePlaceholder.fresh(offset);
            return new SwitchLabel(type, offset);
        } else {
            return new SwitchLabel(caseExpression, caseExpression.getType(), offset);
        }
    }

    private Pattern convert(PatternContext pattern) {

        return switch (pattern) {
        case PPatternContext pPattern -> {
            yield convert(pPattern.primaryPattern());
        }
        case GPatternContext gPattern -> {
            GuardedPatternContext guarded = gPattern.guardedPattern();
            Expression condition = convert(guarded.expression());
            yield new GuardedPattern(condition, convert(guarded.primaryPattern()), guarded.getStart());
        }
        default -> throw new NotImplementedException();
        };
    }

    private FormalParameter convert(PrimaryPatternContext pPattern) {

        switch (pPattern) {
        case TPatternContext tPattern:
            TypePatternContext typePattern = tPattern.typePattern();
            var text = typePattern.identifier().getText();
            var type = typePattern.typeType() == null ? TypePlaceholder.fresh(pPattern.getStart()) :  TypeGenerator.convert(typePattern.typeType(), reg, generics);
            localVars.put(text, type);
            return new FormalParameter(text, type, typePattern.getStart());
        case RPatternContext rPattern:
            RecordPatternContext recordPattern = rPattern.recordPattern();
            return convert(recordPattern);
            case Java17Parser.LPatternContext patternContext: return new LiteralPattern(TypePlaceholder.fresh(patternContext.start), convert(patternContext.literal().get(0)), patternContext.start);

            default:
                throw new NotImplementedException();

        }
    }

    private RecordPattern convert(RecordPatternContext recordPatternCtx) {
        var cpl = recordPatternCtx.recordStructurePattern().recordComponentPatternList();
        List<PatternContext> subPatternCtx = cpl == null ? List.of() : cpl.pattern();
        List<Pattern> subPattern = subPatternCtx.stream().map(this::convert).collect(Collectors.toList());
        IdentifierContext identifierCtx = recordPatternCtx.identifier();
        var text = (identifierCtx != null) ? identifierCtx.getText() : null;
        //Hier evtl. Typ anpassen -> wenn kein Typ bekannt ist push neuen Typ auf Hashtable
        var type = recordPatternCtx.type == null ? TypePlaceholder.fresh(recordPatternCtx.getStart()) : TypeGenerator.convert(recordPatternCtx.type, reg, generics);
        var ctor = TypeGenerator.convert(recordPatternCtx.ctor, reg, generics);
        if (text != null) localVars.put(text, type);
        var ret = new RecordPattern(subPattern, text, type, (RefType)ctor, recordPatternCtx.getStart());
        return ret;
    }

    private Statement convert(Java17Parser.WhileloopContext stmt) {
        Expression expr = convert(stmt.parExpression().expression());
        Statement block = convert(stmt.statement());
        return new WhileStmt(expr, block, stmt.getStart());
    }

    private Statement convert(Java17Parser.DowhileloopContext stmt) {
        Statement block = convert(stmt.statement());
        Expression expr = convert(stmt.parExpression().expression());
        return new DoStmt(expr, block, stmt.getStart());
    }

    private Statement convert(Java17Parser.ForloopContext stmt) {
        var control = stmt.forControl();
        if (control.enhancedForControl() != null) {
            var forCtrl = control.enhancedForControl();
            var name = forCtrl.variableDeclaratorId().identifier();

            RefTypeOrTPHOrWildcardOrGeneric type;
            if (Objects.isNull(forCtrl.typeType()) || !Objects.isNull(forCtrl.VAR())) {
                type = TypePlaceholder.fresh(forCtrl.getStart());
            } else {
                type = TypeGenerator.convert(forCtrl.typeType(), reg, generics);
            }

            var vardecl = new LocalVarDecl(name.getText(), type, name.getStart());
            this.localVars.put(name.getText(), type);

            return new ForEachStmt(
                stmt.getStart(),
                vardecl,
                convert(forCtrl.expression()),
                convert(stmt.statement())
            );
        } else {
            return new ForStmt(
                stmt.getStart(),
                control.forInit() != null ? convert(control.forInit().localVariableDeclaration()) : List.of(),
                control.expression() != null ? convert(control.expression()) : null,
                control.forUpdate != null ? control.forUpdate.expression().stream().map(this::convert).toList() : List.of(),
                convert(stmt.statement())
            );
        }
    }

    private ArgumentList convertArguments(Java17Parser.ExpressionListContext arglist) {
        if (arglist == null)
            return new ArgumentList(new ArrayList<>(), new NullToken());
        Token offset = new NullToken();
        List<Expression> arguments = new ArrayList<Expression>();
        for (ExpressionContext arg : arglist.expression()) {
            arguments.add(convert(arg));
        }
        if (arguments.size() > 0)
            offset = arguments.get(0).getOffset();
        return new ArgumentList(arguments, offset);
    }

    private List<Statement> convert(Java17Parser.LocalVariableDeclarationContext declaration) {
        List<Statement> ret = new ArrayList<>();
        if (declaration.variableModifier() != null && declaration.variableModifier().size() > 0) {
            // TODO
            throw new NotImplementedException();
        }
        RefTypeOrTPHOrWildcardOrGeneric type;
        if (Objects.isNull(declaration.typeType()) || !Objects.isNull(declaration.VAR())) {
            type = TypePlaceholder.fresh(declaration.getStart());
        } else {
            type = TypeGenerator.convert(declaration.typeType(), reg, generics);
        }
        if (!Objects.isNull(declaration.variableDeclarators()))
            ret.addAll(generateLocalVariableAssignments(declaration.variableDeclarators().variableDeclarator(), type));
        return ret;
    }

    private List<Statement> generateLocalVariableAssignments(List<Java17Parser.VariableDeclaratorContext> varDeclarators, RefTypeOrTPHOrWildcardOrGeneric type) {
        List<Statement> ret = new ArrayList<>();
        for (Java17Parser.VariableDeclaratorContext varDecl : varDeclarators) {
            IdentifierContext name = varDecl.variableDeclaratorId().identifier();

            ret.add(new LocalVarDecl(name.getText(), type, name.getStart()));
            this.localVars.put(name.getText(), type);
            if (varDecl.variableInitializer() != null) {
                Expression initValue;
                if (varDecl.variableInitializer().arrayInitializer() != null) {
                    throw new NotImplementedException();
                } else {
                    initValue = convert(varDecl.variableInitializer().expression());
                }
                ret.add(new Assign(new AssignToLocal(new LocalVar(name.getText(), type, name.getStart())), initValue, name.getStart()));
            }
        }
        return ret;
    }

    public Statement generateFieldAssignment(Java17Parser.VariableDeclaratorContext varDecl, RefTypeOrTPHOrWildcardOrGeneric type) {
        IdentifierContext name = varDecl.variableDeclaratorId().identifier();
        Expression initValue;
        if (varDecl.variableInitializer().arrayInitializer() != null) {
            throw new NotImplementedException();
        } else {
            initValue = convert(varDecl.variableInitializer().expression());
        }
        var fieldEntry = fields.get(name.getText());
        return (new Assign(new AssignToField(new FieldVar(new This(varDecl.getStart()), name.getText(), type, varDecl.getStart())), initValue, name.getStart()));
    }

    private Statement convert(Java17Parser.BreakstmtContext stmt) {
        Token offset = stmt.getStart();
        if (!Objects.isNull(stmt.identifier())) {
            return new Break(localVars.get(stmt.identifier().getText()), offset);
        } else {
            return new Break(TypePlaceholder.fresh(offset), offset);
        }

    }

    private Statement convert(Java17Parser.ContinuestmtContext stmt) {
        Token offset = stmt.getStart();
        if (!Objects.isNull(stmt.identifier())) {
            return new Continue(localVars.get(stmt.identifier().getText()), offset);
        } else {
            return new Continue(TypePlaceholder.fresh(offset), offset);
        }
    }

    private Statement convert(Java17Parser.SemistmtContext stmt) {
        // TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java17Parser.ReturnstmtContext stmt) {
        if (stmt.expression() != null) {
            return new Return(convert(stmt.expression()), stmt.getStart());
        } else {
            return new ReturnVoid(stmt.getStart());
        }
    }

    private Statement convert(Java17Parser.ThrowstmtContext stmt) {
        return new Throw(convert(stmt.expression()), stmt.getStart());
    }

    private Statement convert(Java17Parser.SynchronizedstmtContext stmt) {
        // TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java17Parser.TrycatchblockContext stmt) {
        // TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java17Parser.TrycatchresourceContext stmt) {
        // TODO
        throw new NotImplementedException();
    }

    /*
     *************** + Expression Conversions:
     */
    private Expression convert(Java17Parser.ExpressionContext expression) {
        switch (expression) {
        case PrimaryExpression2Context primary:
            return convert(primary.primary());
        case DottedexpressionContext dotted:
            return convert(dotted, expression.getStart());
        case MethodcallexpressionContext methodcall:
            return convert(methodcall.methodCall(), methodcall.getStart());
        case NewinstanceexpressionContext newinstance:
            return convert(newinstance.creator());
        case CastexpressionContext castexpr:
            return convert(castexpr);
        case PostfixexpressionContext postfexpr:
            return convert(postfexpr);
        case PrefixexpressionContext prefexpr:
            return convert(prefexpr);
        case MathmuldivmodexpressionContext mathexpr:
            return convert(mathexpr);
        case MathaddsubexpressionContext mathexpr:
            return convert(mathexpr);
        case RelationalexpressionContext comparison:
            return convert(comparison);
        case InstanceofexpressionContext instanceOf:
            return convert(instanceOf);
        case SwitchExpression2Context switchexpression:
            return convert(switchexpression.switchExpression());
        case EqualityexpressionContext equal:
            return convert(equal);
        case AssignexpressionContext assignment:
            return convert(assignment);
        case LambdaExpression2Context lambdaexpr:
            return convert(lambdaexpr.lambdaExpression());
        case ArrayaccessexpressionContext arrayaccess:
            return convert(arrayaccess);
        case ShiftexpressionContext shiftexpr:
            return convert(shiftexpr);
        case BitwiseandexpressionContext bitwiseand:
            return convert(bitwiseand);
        case BitwisexorexpressionContext bitwisexor:
            return convert(bitwisexor);
        case BitwiseorexpressionContext bitwiseor:
            return convert(bitwiseor);
        case AndexpressionContext andexpr:
            return convert(andexpr);
        case OrexpressionContext orexpr:
            return convert(orexpr);
        case ConditionalassignexpressionContext condassign:
            return convert(condassign);
        default:
            throw new NotImplementedException(expression.getClass().toString());
        }
    }

    private Expression convert(DottedexpressionContext expr, Token offset) {
        if (!Objects.isNull(expr.methodCall())) {
            return convert(expr.methodCall(), expr.expression(), offset);
        } else if (!Objects.isNull(expr.identifier())) {
            return new FieldVar(convert(expr.expression()), expr.identifier().getText(), TypePlaceholder.fresh(expr.identifier().start), offset);
        } else {
            // Für alle anderen Optionen, wie Feldzugriff, Aufrufe von super oder explizite
            // generische Invokationen
            throw new NotImplementedException();
        }
    }

    private MethodCall convert(MethodCallContext expr, Token offset) {
        String name = "this";
        Expression receiver = new This(offset);
        if (expr.identifier() != null) {
            name = expr.identifier().getText();
        } else if (!Objects.isNull(expr.SUPER())) {
            // if(methodInvocationContext.Identifier() != null){
            name = expr.SUPER().getText();
        }

        ArgumentList argumentList = convertArguments(expr.expressionList());
        ArrayList<TypePlaceholder> signature = argumentList.getArguments().stream().map(x -> TypePlaceholder.fresh(offset)).collect(Collectors.toCollection(ArrayList::new));
        signature.add(TypePlaceholder.fresh(offset)); // return type

        MethodCall ret;
        if (expr.SUPER() != null) {
            ret = new SuperCall(argumentList, TypePlaceholder.fresh(offset), signature, offset);
        } else if(expr.THIS() != null) {
            ret = new ThisCall(argumentList, TypePlaceholder.fresh(offset), signature, offset);
        } else {
            ret = new MethodCall(TypePlaceholder.fresh(offset), getReceiver(receiver), name, argumentList, TypePlaceholder.fresh(offset), signature, offset);
        }

        ret.setStatement();
        return ret;
    }

    private MethodCall convert(MethodCallContext expr, ExpressionContext receiver, Token offset) {
        String name = "this";
        if (expr.identifier() != null) {
            name = expr.identifier().getText();
        } else if (!Objects.isNull(expr.SUPER())) {
            // if(methodInvocationContext.Identifier() != null){
            name = expr.SUPER().getText();
        }
        ArgumentList argumentList = convertArguments(expr.expressionList());
        ArrayList<TypePlaceholder> signature = argumentList.getArguments().stream().map(x -> TypePlaceholder.fresh(offset)).collect(Collectors.toCollection(ArrayList::new));
        signature.add(TypePlaceholder.fresh(offset)); // return type
        MethodCall ret = new MethodCall(TypePlaceholder.fresh(offset), getReceiver(receiver), name, argumentList, TypePlaceholder.fresh(offset), signature, offset);
        ret.setStatement();
        return ret;
    }

    public Receiver getReceiver(ExpressionContext expr) {
        Expression expression = convert(expr);
        /*
         * if (expr instanceof PrimaryExpression2Context pc) { expression = convert(pc.primary()); } else { expression = generateLocalOrFieldVarOrClassName(expr.getText(), expr.getStart()); }
         */
        return getReceiver(expression);
    }

    public Receiver getReceiver(Expression expr) {
        if (expr instanceof StaticClassName) {
            return (Receiver) expr;
        } else {
            return new ExpressionReceiver(expr);
        }
    }

    private StaticClassName generateStaticClassName(String name, Token offset) {
        var className = reg.getName(name);
        var numberOfGenerics = reg.getNumberOfGenerics(name);
        var refType = new RefType(className, Stream.generate(() -> (RefTypeOrTPHOrWildcardOrGeneric) TypePlaceholder.fresh(offset)).limit(numberOfGenerics).toList(), offset);
        return new StaticClassName(refType, offset);
    }

    /**
     * Der Parser kann nicht zwischen einer lokalen Variable, einem Feldzugriff und einer Klassenangabe unterscheiden.
     *
     * @param expression
     * @param offset
     * @return
     */
    private Expression generateLocalOrFieldVarOrClassName(String expression, Token offset) {
        // Check for localVar:
        if (localVars.get(expression) != null) {
            return new LocalVar(expression, localVars.get(expression), offset);
        } else if (fields.get(expression) != null) {// PL 2018-11-01 fields eingefuegt, damit die fields immer die
            // gleiche TPH bekommen
            return new FieldVar(new This(offset), expression, fields.get(expression).type(), offset);
        } else if (reg.contains(expression)) {
            return generateStaticClassName(expression, offset);
        } else {
            // lokale Variable wurde ohne "var"-Keyword deklariert und direkt mit Wert versehen
            localVars.put(expression, TypePlaceholder.fresh(offset));
            return new LocalVar(expression, localVars.get(expression), offset);
        }
    }

    private Expression convert(Java17Parser.ArrayaccessexpressionContext arrayaccess) {
        throw new NotImplementedException();
    }

    private Expression convert(Java17Parser.ConditionalassignexpressionContext expression) {
        return new Ternary(TypePlaceholder.fresh(
            expression.getStart()),
            convert(expression.expression(0)),
            convert(expression.expression(1)),
            convert(expression.expression(2)),
            expression.getStart()
        );
    }

    private Expression convert(Java17Parser.OrexpressionContext expression) {
        if (expression.expression().size() != 2) {
            throw new NotImplementedException();
        } else {
            return new BoolExpression(BoolExpression.Operator.OR, new RefType(new JavaClassName("java.lang.Boolean"), expression.getStart()), convert(expression.expression(0)), convert(expression.expression(1)), expression.getStart());
        }
    }

    private Expression convert(Java17Parser.AndexpressionContext expression) {
        if (expression.expression().size() != 2) {
            throw new NotImplementedException();
        } else {
            return new BoolExpression(BoolExpression.Operator.AND, new RefType(new JavaClassName("java.lang.Boolean"), expression.getStart()), convert(expression.expression(0)), convert(expression.expression(1)), expression.getStart());
        }
    }

    private Statement convert(AssignexpressionContext expr) {
        switch (expr.bop.getText()) {
        case "=":
            AssignLeftSide leftHandSide = convertAssignLHS(convert(expr.expression(0)));
            return new Assign(leftHandSide, convert(expr.expression(1)), expr.getStart());
        case "+=":
        case "-=":
        case "*=":
        case "/=":
        case "&=":
        case "|=":
        case "^=":
        case ">>=":
        case ">>>=":
        case "<<=":
        case "%=":
        default:
            throw new NotImplementedException();
        }
    }

    private AssignLeftSide convertAssignLHS(Expression expr) {
        Expression leftSide = expr;
        if (leftSide instanceof FieldVar)
            return new AssignToField((FieldVar) leftSide);
        else if (leftSide instanceof LocalVar)
            return new AssignToLocal((LocalVar) leftSide);
        else
            throw new NotImplementedException();
    }

    private Expression convert(Java17Parser.BitwiseorexpressionContext expression) {
        return new BinaryExpr(BinaryExpr.Operator.OR, TypePlaceholder.fresh(expression.getStart()), convert(expression.expression(0)), convert(expression.expression(1)), expression.getStart());
    }

    private Expression convert(Java17Parser.BitwisexorexpressionContext expression) {
        return new BinaryExpr(BinaryExpr.Operator.XOR, TypePlaceholder.fresh(expression.getStart()), convert(expression.expression(0)), convert(expression.expression(1)), expression.getStart());
    }

    private Expression convert(Java17Parser.BitwiseandexpressionContext expression) {
        return new BinaryExpr(BinaryExpr.Operator.AND, TypePlaceholder.fresh(expression.getStart()), convert(expression.expression(0)), convert(expression.expression(1)), expression.getStart());
    }

    private Expression convert(Java17Parser.EqualityexpressionContext expression) {
        String operator = expression.bop.getText();
        Expression leftSide = convert(expression.expression(0));
        Expression rightSide = convert(expression.expression(1));
        return new BinaryExpr(convertBinaryOperator(operator), TypePlaceholder.fresh(expression.getStart()), leftSide, rightSide, expression.getStart());
    }

    private Expression convert(Java17Parser.RelationalexpressionContext expression) {
        String operator = expression.bop.getText();
        return new BinaryExpr(convertBinaryOperator(operator), TypePlaceholder.fresh(expression.getStart()), convert(expression.expression(0)), convert(expression.expression(1)), expression.getStart());
    }

    private Expression convert(Java17Parser.InstanceofexpressionContext expression) {
        Expression left = convert(expression.expression());
        Token offset = expression.getStart();
        if (Objects.isNull(expression.pattern())) {;
            return new InstanceOf(left, new RefType(reg.getName("java.lang.Boolean"), expression.getStart()), TypeGenerator.convert(expression.typeType(), reg, generics), offset);
        } else {
            switch (expression.pattern()) {
            case PPatternContext primaryPattern:
                switch (primaryPattern.primaryPattern()) {
                case TPatternContext typePattern:
                    TypePatternContext typePatternCtx = typePattern.typePattern();
                    String localVarName = typePatternCtx.identifier().getText();
                    RefTypeOrTPHOrWildcardOrGeneric localVarType = TypeGenerator.convert(typePatternCtx.typeType(), reg, generics);
                    localVars.put(localVarName, localVarType);
                    return new InstanceOf(left, new RefType(reg.getName("java.lang.Boolean"), expression.getStart()), new FormalParameter(localVarName, localVarType, typePatternCtx.getStart()), offset);
                default:
                    throw new NotImplementedException();
                }
            default:
                throw new NotImplementedException();
            }
        }
    }

    private BinaryExpr.Operator convertBinaryOperator(String operator) {
        // return BinaryExpr.Operator.ADD;
        if (operator.equals("+")) {
            return BinaryExpr.Operator.ADD;
        } else if (operator.equals("-")) {
            return BinaryExpr.Operator.SUB;
        } else if (operator.equals("*")) {
            return BinaryExpr.Operator.MUL;
        } else if (operator.equals("&")) {
            return BinaryExpr.Operator.AND;
        } else if (operator.equals("|")) {
            return BinaryExpr.Operator.OR;
        } else if (operator.equals("/")) {
            return BinaryExpr.Operator.DIV;
        } else if (operator.equals("<")) {
            return BinaryExpr.Operator.LESSTHAN;
        } else if (operator.equals(">")) {
            return BinaryExpr.Operator.BIGGERTHAN;
        } else if (operator.equals(">=")) {
            return BinaryExpr.Operator.BIGGEREQUAL;
        } else if (operator.equals("<=")) {
            return BinaryExpr.Operator.LESSEQUAL;
        } else if (operator.equals("==")) {
            return BinaryExpr.Operator.EQUAL;
        } else if (operator.equals("!=")) {
            return BinaryExpr.Operator.NOTEQUAL;
        } else if (operator.equals("%")) {
            return BinaryExpr.Operator.MOD;
        } else {
            throw new NotImplementedException();
        }
        // throw new NotImplementedException();
    }

    private Expression convert(Java17Parser.ShiftexpressionContext expression) {
        throw new NotImplementedException();
    }

    private Expression convert(Java17Parser.MathaddsubexpressionContext expression) {
        Expression leftSide = convert(expression.expression(0));
        Expression rightSide = convert(expression.expression(1));
        BinaryExpr.Operator op = convertBinaryOperator(expression.bop.getText());
        Token offset = expression.getStart();
        return new BinaryExpr(op, TypePlaceholder.fresh(expression.getStart()), leftSide, rightSide, offset);
    }

    private Expression convert(Java17Parser.MathmuldivmodexpressionContext expression) {
        Expression leftSide = convert(expression.expression(0));
        Expression rightSide = convert(expression.expression(1));
        BinaryExpr.Operator op = convertBinaryOperator(expression.bop.getText());
        Token offset = expression.getStart();
        return new BinaryExpr(op, TypePlaceholder.fresh(offset), leftSide, rightSide, offset);
    }

    private Statement convert(PrefixexpressionContext prefixexpr) {
        Expression expr = convert(prefixexpr.expression());
        Token op = prefixexpr.prefix;
        Statement ret;
        if (op.getText().equals("++")) {
            ret = new UnaryExpr(UnaryExpr.Operation.PREINCREMENT, expr, TypePlaceholder.fresh(op), op);
            ret.setStatement();
            return ret;
        } else if (op.getText().equals("--")) {
            ret = new UnaryExpr(UnaryExpr.Operation.PREDECREMENT, expr, TypePlaceholder.fresh(op), op);
            ret.setStatement();
            return ret;
        } else if (op.getText().equals("!")) {
            ret = new UnaryExpr(UnaryExpr.Operation.NOT, expr, TypePlaceholder.fresh(op), op);
            return ret;
        } else {
            throw new NotImplementedException();
        }
    }

    private Statement convert(Java17Parser.PostfixexpressionContext postfixexpr) {
        Expression expr = convert(postfixexpr.expression());
        Token op = postfixexpr.postfix;
        switch (op.getText()) {
        case "++":
            return new UnaryExpr(UnaryExpr.Operation.POSTINCREMENT, expr, TypePlaceholder.fresh(op), op);
        case "--":
            return new UnaryExpr(UnaryExpr.Operation.POSTDECREMENT, expr, TypePlaceholder.fresh(op), op);
        default:
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java17Parser.CastexpressionContext castexpr) {
        ExpressionContext expr = castexpr.expression();
        if (expr instanceof PrefixexpressionContext pfe) {
            throw new NotImplementedException();
        }
        return new CastExpr(TypeGenerator.convert(castexpr.typeType(0), reg, generics), convert(expr), expr.getStart());
    }

    private Expression convert(Java17Parser.PrimaryContext primary) {
        switch (primary) {
        case PrimaryExpressionContext primexpression:
            return convert(primexpression.expression());
        case PrimaryThisContext primthis:
            return new This(primthis.getStart());
        case PrimarySuperContext primsuper:
            return new Super(primsuper.getStart());
        case PrimaryLiteralContext primliteral:
            return convert(primliteral.literal());
        case PrimaryIdentifierContext primidentifier:
            return generateLocalOrFieldVarOrClassName(primidentifier.getText(), primidentifier.getStart());
        case PrimaryClassrefContext primclassref:
            throw new NotImplementedException();
        default:
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java17Parser.LiteralContext literal) {
        switch (literal) {
        case IntLiteralContext intliteral:
            Number value;
            if (intliteral.getText().endsWith("l") || intliteral.getText().endsWith("L"))
                value = Long.parseLong(intliteral.getText().substring(0, intliteral.getText().length() - 1));
            else value = Integer.parseInt(intliteral.getText());
            return new Literal(TypePlaceholder.fresh(literal.getStart()), value, intliteral.getStart());
        case FltLiteralContext floatliteral:
            if (floatliteral.getText().endsWith("f") || floatliteral.getText().endsWith("F"))
                value = Float.parseFloat(floatliteral.getText().substring(0, floatliteral.getText().length() - 1));
            else if (floatliteral.getText().endsWith("d") || floatliteral.getText().endsWith("D"))
                value = Double.parseDouble(floatliteral.getText().substring(0, floatliteral.getText().length() - 1));
            else value = Double.parseDouble(floatliteral.getText());
            return new Literal(TypePlaceholder.fresh(literal.getStart()), value, floatliteral.getStart());
        case CharLiteralContext charliteral:
            RefType type = new RefType(reg.getName("java.lang.Character"), charliteral.getStart());
            return new Literal(type, charliteral.getText().charAt(1), charliteral.getStart());
        case StringLiteralContext stringliteral:
            type = new RefType(reg.getName("java.lang.String"), stringliteral.getStart());
            return new Literal(type, stringliteral.getText().substring(1, stringliteral.getText().length() - 1), stringliteral.getStart());
        case BoolLiteralContext boolliteral:
            type = new RefType(reg.getName("java.lang.Boolean"), boolliteral.getStart());
            return new Literal(type, Boolean.parseBoolean(boolliteral.getText()), boolliteral.getStart());
        case NullLiteralContext nullliteral:
            return new Literal(TypePlaceholder.fresh(nullliteral.getStart()), null, nullliteral.getStart());
        default:
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java17Parser.LambdaExpressionContext expression) {
        Java17Parser.LambdaParametersContext lambdaParams = expression.lambdaParameters();
        ParameterList params;
        if (lambdaParams.identifier().size() > 0) {
            List<Pattern> parameterList = new ArrayList<>();
            for (IdentifierContext identifier : lambdaParams.identifier()) {
                Token offset = identifier.getStart();
                parameterList.add(new FormalParameter(identifier.getText(), TypePlaceholder.fresh(offset, 1, false), offset));
            }
            params = new ParameterList(parameterList, lambdaParams.getStart());
        } else if (lambdaParams.formalParameterList() != null) {
            params = convert(lambdaParams.formalParameterList(), false);
            // }else if( lambdaParams.inferredFormalParameterList != null){
        } else if (!Objects.isNull(lambdaParams.lambdaLVTIList())) {
            List<Pattern> parameterList = new ArrayList<>();
            for (LambdaLVTIParameterContext param : lambdaParams.lambdaLVTIList().lambdaLVTIParameter()) {
                Token offset = param.getStart();
                parameterList.add(new FormalParameter(param.identifier().getText(), TypePlaceholder.fresh(offset, 1, false), offset));
            }
            params = new ParameterList(parameterList, lambdaParams.getStart());
        } else {
            params = new ParameterList(new ArrayList<>(), expression.getStart());
        }

        HashMap<String, RefTypeOrTPHOrWildcardOrGeneric> lambdaLocals = new HashMap<>();
        lambdaLocals.putAll(localVars);
        for (Pattern param : params.getFormalparalist()) {
            if (!(param instanceof FormalParameter fp)) throw new IllegalArgumentException();
            lambdaLocals.put(fp.getName(), fp.getType());
        }
        StatementGenerator lambdaGenerator = new StatementGenerator(superClass, compiler, reg, generics, fields, lambdaLocals);

        Block block;
        if (expression.lambdaBody().expression() != null) {
            List<Statement> statements = new ArrayList<>();
            statements.add(new Return(lambdaGenerator.convert(expression.lambdaBody().expression()), expression.lambdaBody().expression().getStart()));
            block = new Block(statements, expression.lambdaBody().getStart());
        } else {
            block = lambdaGenerator.convert(expression.lambdaBody().block(), true);
        }
        List<RefTypeOrTPHOrWildcardOrGeneric> funNParams = new ArrayList<>();
        funNParams.add(TypePlaceholder.fresh(expression.getStart(), -1, false));// ret-Type
        params.getFormalparalist().forEach(formalParameter -> // Für jeden Parameter einen TPH anfügen:
        	funNParams.add(TypePlaceholder.fresh(expression.getStart(), 1, false)));
        RefTypeOrTPHOrWildcardOrGeneric lambdaType = TypePlaceholder.fresh(expression.getStart());
        // RefType lambdaType = new
        // RefType(reg.getName("Fun"+params.getFormalparalist().size()),
        // funNParams, name.getStart());
        return new LambdaExpression(lambdaType, params, block, expression.getStart());
    }
}
