package de.dhbwstuttgart.parser.SyntaxTreeGenerator;

import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.antlr.Java8Parser;
import de.dhbwstuttgart.parser.scope.GenericsRegistry;
import de.dhbwstuttgart.parser.scope.JavaClassRegistry;
import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.statement.UnaryExpr.Operation;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.syntaxtree.type.Void;

import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.*;

public class StatementGenerator {

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

    public StatementGenerator(JavaClassRegistry reg, GenericsRegistry generics, Map<String, RefTypeOrTPHOrWildcardOrGeneric> fields, Map<String, RefTypeOrTPHOrWildcardOrGeneric> localVars){
        this.reg = reg;
        this.generics = generics;
        this.fields = fields;
        this.localVars = localVars;
    }

    public ParameterList convert(Java8Parser.FormalParameterListContext formalParameterListContext) {
        List<FormalParameter> ret = new ArrayList<>();
        List<Java8Parser.FormalParameterContext> fps = new ArrayList<>();
        if(formalParameterListContext == null || formalParameterListContext.lastFormalParameter() == null)
            return new ParameterList(ret, new NullToken()); //Dann ist die Parameterliste leer

        if(formalParameterListContext.lastFormalParameter().formalParameter() == null)throw new NotImplementedException();

        if(formalParameterListContext != null && formalParameterListContext.formalParameters() != null
                && formalParameterListContext.formalParameters().formalParameter() != null){
            fps = new ArrayList<>(formalParameterListContext.formalParameters().formalParameter());
        }
        fps.add(formalParameterListContext.lastFormalParameter().formalParameter());

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

  /*
   * StatementGeneration:
   */

    private Statement convert(Java8Parser.StatementContext stmt) {
        if (stmt.statementWithoutTrailingSubstatement() != null) {
            return convert(stmt.statementWithoutTrailingSubstatement());
        } else if (stmt.whileStatement() != null) {
            return convert(stmt.whileStatement());
        } else if (stmt.forStatement() != null) {
            return convert(stmt.forStatement());
        } else if (stmt.ifThenElseStatement() != null) {
            return convert(stmt.ifThenElseStatement());
        } else if (stmt.ifThenStatement() != null) {
            return convert(stmt.ifThenStatement());
        } else if (stmt.labeledStatement() != null) {
            return convert(stmt.labeledStatement() );
        }else throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.StatementNoShortIfContext stmt){
        if(stmt.statementWithoutTrailingSubstatement() != null){
            return convert(stmt.statementWithoutTrailingSubstatement());
        }else if(stmt.labeledStatementNoShortIf() != null){
            return convert(stmt.labeledStatementNoShortIf());
        }else if(stmt.ifThenElseStatementNoShortIf() != null){
            return convert(stmt.ifThenElseStatementNoShortIf());
        }else if(stmt.whileStatementNoShortIf() != null){
            return convert(stmt.whileStatementNoShortIf());
        }else if(stmt.forStatementNoShortIf() != null){
            return convert(stmt.forStatementNoShortIf());
        }else throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.StatementWithoutTrailingSubstatementContext stmt) {
        if(stmt.block() != null){
            return convert(stmt.block(), false);
        }else if(stmt.emptyStatement() != null){
            return new EmptyStmt(stmt.getStart());
        }else if(stmt.expressionStatement() != null){
            return convert(stmt.expressionStatement());
        }else if(stmt.assertStatement() != null){
            return convert(stmt.assertStatement());
        }else if(stmt.switchStatement() != null){
            return convert(stmt.switchStatement());
        }else if(stmt.doStatement() != null){
            return convert(stmt.doStatement());
        }else if(stmt.breakStatement() != null){
            return convert(stmt.breakStatement());
        }else if(stmt.continueStatement() != null){
            return convert(stmt.continueStatement());
        }else if(stmt.returnStatement() != null){
            return convert(stmt.returnStatement());
        }else if(stmt.synchronizedStatement() != null){
            return convert(stmt.synchronizedStatement());
        }else if(stmt.throwStatement() != null){
            return convert(stmt.throwStatement());
        }else if(stmt.tryStatement() != null){
            return convert(stmt.tryStatement());
        }else throw new NotImplementedException();
    }

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

    private List<Statement> convert(Java8Parser.BlockStatementContext statementContext) {
        if(statementContext.localVariableDeclarationStatement() != null){
            return convert(statementContext.localVariableDeclarationStatement());
        }else if(statementContext.classDeclaration() != null){
            throw new NotImplementedException();
        }else{
            return Arrays.asList(convert(statementContext.statement()));
        }
    }

    private List<Statement> convert(Java8Parser.LocalVariableDeclarationStatementContext stmt) {
        Java8Parser.LocalVariableDeclarationContext declaration = stmt.localVariableDeclaration();
        return convert(declaration);
    }

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

    private Statement convert(Java8Parser.LabeledStatementNoShortIfContext stmt){
        throw new NotImplementedException();
        //return convert(stmt.statementNoShortIf());
    }

    private Statement convert(Java8Parser.ExpressionStatementContext stmt){
        return convert(stmt.statementExpression());
    }

    private Statement convert(Java8Parser.StatementExpressionContext stmt) {
        if(stmt.assignment() != null){
            return convert(stmt.assignment());
        }else if(stmt.preIncrementExpression() != null){
            return convert(stmt.preIncrementExpression());
        }else if(stmt.preDecrementExpression() != null){
            return convert(stmt.preDecrementExpression());
        }else if(stmt.postIncrementExpression() != null){
            return convert(stmt.postIncrementExpression());
        }else if(stmt.postDecrementExpression() != null){
            return convert(stmt.postDecrementExpression());
        }else if(stmt.methodInvocation() != null){
            return convert(stmt.methodInvocation());
        }else if(stmt.classInstanceCreationExpression() != null){
            return convert(stmt.classInstanceCreationExpression());
        }else throw new NotImplementedException();
    }

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

    private Statement convert(Java8Parser.MethodInvocationContext methodInvocationContext) {
        String name;
        if(methodInvocationContext.methodName()!=null){
            name = methodInvocationContext.methodName().Identifier().getText();
        }else{// if(methodInvocationContext.Identifier() != null){
            name = methodInvocationContext.Identifier().getText();
        }
        Expression receiver;
        if(methodInvocationContext.typeName() != null){
            receiver = generateLocalOrFieldVarOrClassName(methodInvocationContext.typeName().getText(), methodInvocationContext.typeName().getStart());
        }else if(methodInvocationContext.expressionName()!=null){
            receiver = convert(methodInvocationContext.expressionName());
        }else if(methodInvocationContext.primary() != null){
            receiver = convert(methodInvocationContext.primary());
        }else if(methodInvocationContext.toString().startsWith("super")){
            receiver = new Super(methodInvocationContext.getStart());
        }else if(methodInvocationContext.methodName() != null){
            receiver = new This(methodInvocationContext.getStart());
        }else throw new NotImplementedException();

        ArgumentList argumentList = convert(methodInvocationContext.argumentList());
        MethodCall ret = new MethodCall(TypePlaceholder.fresh(methodInvocationContext.getStart()), getReceiver(receiver), name, argumentList, methodInvocationContext.getStart());
        return ret;
    }

    private ArgumentList convert(Java8Parser.ArgumentListContext argumentListContext) {
        if(argumentListContext == null)return new ArgumentList(new ArrayList<>(), new NullToken());
        List<Expression> args = new ArrayList<>();

        Token offset = new NullToken();
        for(Java8Parser.ExpressionContext expr : argumentListContext.expression()){
            args.add(convert(expr));
        }
        if(args.size()>0)offset = args.get(0).getOffset();

        return new ArgumentList(args, 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){
        String[] parts = expression.split("\\.");
        if(parts.length < 2){
            //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), offset);
                
            } else {
            	//kann eigentlich nicht vorkommen
                //Dann Muss es ein Feld sein!
                return new FieldVar(new This(offset), expression, TypePlaceholder.fresh(offset), offset);
            }}
        }
        return generateFieldVarOrClassname(expression, offset);
    }

    private Expression generateFieldVarOrClassname(String expression, Token offset){
        String[] parts = expression.split("\\.");
        String whole = "";
        Expression receiver = null;
        for(String part : parts){
            whole+=part;
            //Check for Classname:
            if(reg.contains(whole)){
                receiver = new StaticClassName(reg.getName(whole), offset);
            }
            whole+=".";
        }

        if(parts.length < 2){
            receiver = new This(offset);
        }else if(receiver == null){ //Handelt es sich um keinen Statischen Klassennamen:
            String part = expression.substring(0,expression.length() - (1 + parts[parts.length-1].length()));
            receiver = generateLocalOrFieldVarOrClassName(part, offset);
        }
        return new FieldVar(receiver, parts[parts.length-1], TypePlaceholder.fresh(offset) ,offset);
    }

    private Expression convert(Java8Parser.ExpressionNameContext expressionNameContext) {
        return generateLocalOrFieldVarOrClassName(expressionNameContext.getText(), expressionNameContext.getStart());
    }


    private Statement convert(Java8Parser.ClassInstanceCreationExpressionContext stmt) {
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.PreIncrementExpressionContext stmt) {
    		Expression argument = convert(stmt.unaryExpression());
    		Token offset = stmt.getStart();
    		return new UnaryExpr(UnaryExpr.Operation.PREINCREMENT, argument, TypePlaceholder.fresh(offset), offset);
    }

	private Statement convert(Java8Parser.PreDecrementExpressionContext stmt) {
		return new UnaryExpr(UnaryExpr.Operation.PREDECREMENT, convert(stmt.unaryExpression()),
                TypePlaceholder.fresh(stmt.getStart()), stmt.getStart());
    }

    private Statement convert(Java8Parser.PostIncrementExpressionContext stmt) {
        return new UnaryExpr(UnaryExpr.Operation.POSTINCREMENT, convert(stmt.postfixExpression()),
                TypePlaceholder.fresh(stmt.getStart()), stmt.getStart());
    }

    private Statement convert(Java8Parser.PostDecrementExpressionContext stmt) {
    		return new UnaryExpr(UnaryExpr.Operation.POSTDECREMENT, convert(stmt.postfixExpression()),
                TypePlaceholder.fresh(stmt.getStart()), stmt.getStart());
    }

    private Statement convert(Java8Parser.AssignmentContext stmt) {
        AssignLeftSide leftHandSide = convert(stmt.leftHandSide());
        return new Assign(leftHandSide, convert(stmt.expression()), stmt.getStart());
    }

    private AssignLeftSide convert(Java8Parser.LeftHandSideContext leftHandSide) {
        Expression leftSide = generateLocalOrFieldVarOrClassName(leftHandSide.getText(), leftHandSide.getStart());
        if(leftSide instanceof FieldVar)return new AssignToField((FieldVar) leftSide);
        else if (leftSide instanceof LocalVar)return new AssignToLocal((LocalVar) leftSide);
        else throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.IfThenStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.IfThenElseStatementContext stmt){
        Expression expr = convert(stmt.expression());
        Statement thenBlock = convert(stmt.statementNoShortIf());
        Statement elseBlock = convert(stmt.statement());
        return new IfStmt(TypePlaceholder.fresh(stmt.getStart()), expr, thenBlock, elseBlock, stmt.getStart());
    }

    private Statement convert(Java8Parser.IfThenElseStatementNoShortIfContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.AssertStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.SwitchStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.SwitchBlockContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.SwitchBlockStatementGroupContext stmt){
        //TODO
        throw new NotImplementedException();
    }

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

    private Statement convert(Java8Parser.WhileStatementNoShortIfContext stmt){
        //TODO
        throw new NotImplementedException();
    }

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

    private Statement convert(Java8Parser.ForStatementContext stmt){
        if(stmt.basicForStatement() != null){
            return convert(stmt.basicForStatement());
        }else if(stmt.enhancedForStatement() != null){
            return convert(stmt.enhancedForStatement());
        }else throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.ForStatementNoShortIfContext stmt){
        if(stmt.basicForStatementNoShortIf() != null){
            return convert(stmt.basicForStatementNoShortIf());
        }else if(stmt.enhancedForStatementNoShortIf() != null){
            return convert(stmt.enhancedForStatementNoShortIf());
        }else throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.BasicForStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.BasicForStatementNoShortIfContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private List<Statement> convert(Java8Parser.ForInitContext stmt){
        if(stmt.statementExpressionList() != null){
            return Arrays.asList(convert(stmt.statementExpressionList()));
        }else if(stmt.localVariableDeclaration() != null){
            return convert(stmt.localVariableDeclaration());
        }else throw new NotImplementedException();
    }

    private List<Statement> convert(Java8Parser.LocalVariableDeclarationContext declaration) {
        List<Statement> ret = new ArrayList<>();
        if(declaration.variableModifier() != null && declaration.variableModifier().size() > 0){
            //TODO
            throw new NotImplementedException();
        }
        RefTypeOrTPHOrWildcardOrGeneric type;
        if(declaration.unannTypeOrAuto().unannType() == null){
            type = TypePlaceholder.fresh(declaration.getStart());
        }else{
            type = TypeGenerator.convert(declaration.unannTypeOrAuto().unannType(), reg, generics);
        }
        ret.addAll(generateLocalVariableAssignments(declaration.variableDeclaratorList().variableDeclarator(), type));
        return ret;
    }

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

            ret.add(new LocalVarDecl(name.getText(), type, name.getSymbol()));
            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.getSymbol()))
                        , initValue, name.getSymbol()));
            }
        }
        return ret;
    }

    public Statement generateFieldAssignment(Java8Parser.VariableDeclaratorContext varDecl, RefTypeOrTPHOrWildcardOrGeneric type){
        TerminalNode name = varDecl.variableDeclaratorId().Identifier();
        Expression initValue;
        if(varDecl.variableInitializer().arrayInitializer() != null){
            throw new NotImplementedException();
        }else{
            initValue = convert(varDecl.variableInitializer().expression());
        }
        return (new Assign(new AssignToField(
                new FieldVar(new This(varDecl.getStart()), name.getText(),
                        type, varDecl.getStart())),
                initValue, name.getSymbol()));
    }

    private Statement convert(Java8Parser.ForUpdateContext stmt){
        return convert(stmt.statementExpressionList());
    }

    private Statement convert(Java8Parser.StatementExpressionListContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.EnhancedForStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.EnhancedForStatementNoShortIfContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.BreakStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.ContinueStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

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

    private Statement convert(Java8Parser.ThrowStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.SynchronizedStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.TryStatementContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.CatchesContext stmt){
        //TODO
        throw new NotImplementedException();
    }

    private Statement convert(Java8Parser.CatchClauseContext stmt){
        //TODO
        throw new NotImplementedException();
    }

/*
***************+ Expression Conversions:
 */

    private Expression convert(Java8Parser.ExpressionContext expression) {
        if(expression.lambdaExpression()!=null){
            return convert(expression.lambdaExpression());
        }else{
            return convert(expression.assignmentExpression());
        }
    }

    private Expression convert(Java8Parser.AssignmentExpressionContext expression) {
        if(expression.conditionalExpression() != null){
            return convert(expression.conditionalExpression());
        }else{
            return convert(expression.assignment());
        }
    }

    private Expression convert(Java8Parser.ConditionalExpressionContext expression) {
        if(expression.conditionalOrExpression() != null){
            return convert(expression.conditionalOrExpression());
        }else{
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.ConditionalOrExpressionContext expression) {
        if(expression.conditionalOrExpression() == null){
            return convert(expression.conditionalAndExpression());
        }else{
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.ConditionalAndExpressionContext expression) {
        if(expression.conditionalAndExpression() == null){
            return convert(expression.inclusiveOrExpression());
        }else{
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.InclusiveOrExpressionContext expression) {
        if(expression.inclusiveOrExpression() == null){
            return convert(expression.exclusiveOrExpression());
        }else{
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.ExclusiveOrExpressionContext expression) {
        if(expression.exclusiveOrExpression() == null){
            return convert(expression.andExpression());
        }else{
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.AndExpressionContext expression) {
        if(expression.andExpression() == null){
            return convert(expression.equalityExpression());
        }else{
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.EqualityExpressionContext expression) {
        if(expression.equalityExpression() == null){
            return convert(expression.relationalExpression());
        }else{
            String operator = expression.getChild(1).getText();
            Expression leftSide = convert(expression.equalityExpression());
            Expression rightSide = convert(expression.relationalExpression());
            return new BinaryExpr(convertBinaryOperator(operator), TypePlaceholder.fresh(expression.getStart()), leftSide, rightSide, expression.getStart());
        }
    }

    private Expression convert(Java8Parser.RelationalExpressionContext expression) {
        if(expression.relationalExpression() == null) {
            return convert(expression.shiftExpression());
        }else {
            String operator = expression.getChild(1).getText();
            return new BinaryExpr(convertBinaryOperator(operator), TypePlaceholder.fresh(expression.getStart()),
                    convert(expression.relationalExpression()),
                    convert(expression.shiftExpression()), expression.getStart());
        }
            //}else 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 {
    			throw new NotImplementedException();
    		}
//    		throw new NotImplementedException();
    }

    private Expression convert(Java8Parser.ShiftExpressionContext expression) {
        if(expression.shiftExpression() == null){
            return convert(expression.additiveExpression());
        }else{
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.AdditiveExpressionContext expression) {

        if(expression.additiveExpression() == null){
            return convert(expression.multiplicativeExpression());
        }else {
            Expression leftSide = convert(expression.additiveExpression());
            Expression rightSide = convert(expression.multiplicativeExpression());
            BinaryExpr.Operator op = convertBinaryOperator(expression.getChild(1).getText());
            Token offset = expression.getStart();
            return new BinaryExpr(op, TypePlaceholder.fresh(expression.getStart()), leftSide, rightSide, offset);
        }
    }

    private Expression convert(Java8Parser.MultiplicativeExpressionContext expression) {
        if(expression.multiplicativeExpression() == null){
            return convert(expression.unaryExpression());
        }else{
        		Expression leftSide = convert(expression.multiplicativeExpression());
        		Expression rightSide = convert(expression.unaryExpression());
        		BinaryExpr.Operator op = convertBinaryOperator(expression.getChild(1).getText());
        		Token offset = expression.getStart();
        		return new BinaryExpr(op, TypePlaceholder.fresh(offset), leftSide, rightSide, offset);
        }
    }

    private Expression convert(Java8Parser.UnaryExpressionContext expression) {
        if(expression.preIncrementExpression() != null){
            return convert(expression.preIncrementExpression());
        }else if(expression.preDecrementExpression() != null){
            return convert(expression.preDecrementExpression());
        }else if(expression.unaryExpressionNotPlusMinus() != null){
            return convert(expression.unaryExpressionNotPlusMinus());
        }else if(expression.getText().startsWith("+")){
            return new UnaryExpr(UnaryExpr.Operation.PLUS, convert(expression.unaryExpression()),
                    TypePlaceholder.fresh(expression.getStart()), expression.getStart());
        }else if(expression.getText().startsWith("-")){
            return new UnaryExpr(UnaryExpr.Operation.MINUS, convert(expression.unaryExpression()),
                    TypePlaceholder.fresh(expression.getStart()), expression.getStart());
        }else{
            //Diese Exceptions sollte nie geworfen werden.
            //Der Code wurde nur noch nicht getestet. Sollte zur Sicherheit drin bleiben.
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.UnaryExpressionNotPlusMinusContext expression) {
        if(expression.postfixExpression() != null){
            return convert(expression.postfixExpression());
        }else if(expression.castExpression() != null){
            return convert(expression.castExpression());
        }else{
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.CastExpressionContext expressionContext) {
        Expression expr = null;
        if(expressionContext.unaryExpression() != null){
            throw new NotImplementedException();
        }else if(expressionContext.unaryExpressionNotPlusMinus() != null){
            expr = convert(expressionContext.unaryExpressionNotPlusMinus());
        }else if(expressionContext.lambdaExpression() != null){
            expr = convert(expressionContext.lambdaExpression());
        }
        return new CastExpr(TypeGenerator.convert(expressionContext.referenceType(), reg, generics),expr, expressionContext.getStart());
    }

    private Expression convert(Java8Parser.PostfixExpressionContext expression) {
        Expression expr;
        if(expression.primary() != null){
            expr = convert(expression.primary());
        }else{
            expr = convert(expression.expressionName());
        }
        if(expression.postDecrementExpression_lf_postfixExpression() == null &&
                expression.postIncrementExpression_lf_postfixExpression() == null){
            return expr;
        }

        for(Java8Parser.PostIncrementExpression_lf_postfixExpressionContext inc : expression.postIncrementExpression_lf_postfixExpression()){
            expr = new UnaryExpr(UnaryExpr.Operation.POSTINCREMENT, expr, TypePlaceholder.fresh(inc.getStart()), inc.getStart());
        }
        for(Java8Parser.PostDecrementExpression_lf_postfixExpressionContext dec : expression.postDecrementExpression_lf_postfixExpression()){
            expr = new UnaryExpr(UnaryExpr.Operation.POSTDECREMENT, expr, TypePlaceholder.fresh(dec.getStart()), dec.getStart());
        }

        return expr;
    }

    private Expression convert(Java8Parser.PrimaryContext primary) {
        Expression expr;
        if(primary.primaryNoNewArray_lfno_primary()!=null){
            expr = convert(primary.primaryNoNewArray_lfno_primary());
        }else{
            expr = convert(primary.arrayCreationExpression());
        }

        if(primary.primaryNoNewArray_lf_primary() != null && primary.primaryNoNewArray_lf_primary().size()>0){
            for(Java8Parser.PrimaryNoNewArray_lf_primaryContext e : primary.primaryNoNewArray_lf_primary()){
                expr = convert(expr, e);
            }
        }
        return expr;
    }

    private Expression convert(Expression expr, Java8Parser.PrimaryNoNewArray_lf_primaryContext e) {
        if(e.classInstanceCreationExpression_lf_primary() != null){
            throw new NotImplementedException();
        }else if(e.fieldAccess_lf_primary() != null){
            
            throw new NotImplementedException();
        }else if(e.arrayAccess_lf_primary() != null){
            throw new NotImplementedException();
        }else if(e.methodReference_lf_primary() != null){
            throw new NotImplementedException();
        }else {
            Java8Parser.MethodInvocation_lf_primaryContext ctxt = e.methodInvocation_lf_primary();
            String methodName = ctxt.Identifier().toString();
            return new MethodCall(TypePlaceholder.fresh(e.getStart()), getReceiver(expr), methodName, convert(ctxt.argumentList()), e.getStart());
        }
    }

    private Expression convert(Java8Parser.ArrayCreationExpressionContext expression) {
        throw new NotImplementedException();
    }

    private Expression convert(Java8Parser.PrimaryNoNewArray_lfno_primaryContext expression) {
        if(expression.literal() != null){
            return convert(expression.literal());
        }else if(expression.expression()!=null){
            return convert(expression.expression());
        }else if(expression.methodInvocation_lfno_primary() != null){
            return convert(expression.methodInvocation_lfno_primary());
        }else if(expression.classInstanceCreationExpression_lfno_primary() != null) {
            return convert(expression.classInstanceCreationExpression_lfno_primary());
        }else  if(expression.getText().equals("this")) {
            return new This(expression.getStart());
        }else if(expression.fieldAccess_lfno_primary() != null){
            return convert(expression.fieldAccess_lfno_primary());
        }else if(expression.methodReference_lfno_primary() != null){
            throw new NotImplementedException();
        }else if(expression.typeName() != null){
            throw new NotImplementedException();
        }else if(expression.unannPrimitiveType() != null){
            throw new NotImplementedException();
        }else if(expression.arrayAccess_lfno_primary() != null){
            throw new NotImplementedException();
        }else if(expression.fieldAccess_lfno_primary() != null){
            throw new NotImplementedException();
        }else{
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.FieldAccess_lfno_primaryContext fieldAccess_lfno_primaryContext) {
        throw new NotImplementedException();
    }

    private Expression convert(Java8Parser.ClassInstanceCreationExpression_lfno_primaryContext newExpression) {
        Java8Parser.TypeArgumentsContext genericArgs = null;
        if(newExpression.expressionName()!= null)throw new NotImplementedException();
        if(newExpression.typeArgumentsOrDiamond()!= null){
            if(newExpression.typeArgumentsOrDiamond().typeArguments()!=null){
                genericArgs = newExpression.typeArgumentsOrDiamond().typeArguments();
            }
        }
        if(newExpression.typeArguments()!= null)throw new NotImplementedException();

        TerminalNode identifier = newExpression.Identifier(0);
        RefType newClass = (RefType) TypeGenerator.convertTypeName(identifier.getText(),genericArgs,identifier.getSymbol(),reg,generics);

        ArgumentList args = convert(newExpression.argumentList());
        return new NewClass(newClass, args, newExpression.getStart());
    }

    private Expression convert(Java8Parser.LiteralContext literal) {
        if(literal.IntegerLiteral() != null){
            Number value = Integer.parseInt(literal.IntegerLiteral().getText());
            return new Literal(TypePlaceholder.fresh(literal.getStart()),
                    value, literal.getStart());
        } else if(literal.FloatingPointLiteral()!= null){
            Number value = Double.parseDouble(literal.FloatingPointLiteral().getText());
            return new Literal(TypePlaceholder.fresh(literal.getStart()),
                    value, literal.getStart());
        }else if(literal.BooleanLiteral() != null){
            RefType type = new RefType(reg.getName("java.lang.Boolean"),literal.getStart());
            return new Literal(type,
                    Boolean.parseBoolean(literal.BooleanLiteral().getText()),
                    literal.getStart());
        }else if(literal.CharacterLiteral() != null){
            RefType type = new RefType(reg.getName("java.lang.Character"),literal.getStart());
            return new Literal(type,
            			// das gibt immer ' zurück, der Char befindet sich in Position 1 
        				//literal.CharacterLiteral().getText().charAt(0),
                    literal.CharacterLiteral().getText().charAt(1),
                    literal.getStart());
        }else if(literal.StringLiteral()!=null){
            RefType type = new RefType(reg.getName("java.lang.String"),literal.getStart());
            return new Literal(type,
                    literal.StringLiteral().getText().substring(1,  literal.StringLiteral().getText().length()-1),
                    literal.getStart());
        }else  if(literal.NullLiteral() != null){
            return new Literal(TypePlaceholder.fresh(literal.getStart()), null,
                    literal.getStart());
        }else {
            throw new NotImplementedException();
        }
    }

    private Expression convert(Java8Parser.MethodInvocation_lfno_primaryContext methodInvocationContext) {
        String name;
        if(methodInvocationContext.methodName()!=null){
            name = methodInvocationContext.methodName().Identifier().getText();
        }else{// if(methodInvocationContext.Identifier() != null){
            name = methodInvocationContext.Identifier().getText();
        }
        Expression receiver;
        if(methodInvocationContext.typeName() != null){
            receiver = generateLocalOrFieldVarOrClassName(methodInvocationContext.typeName().getText(), methodInvocationContext.typeName().getStart());
        }else if(methodInvocationContext.expressionName()!=null){
            receiver = convert(methodInvocationContext.expressionName());
        }else if(methodInvocationContext.toString().startsWith("super")) {
            receiver = new Super(methodInvocationContext.getStart());
        }else if(methodInvocationContext.methodName() != null){
            receiver = new This(methodInvocationContext.methodName().getStart());
        }else {
            throw new NotImplementedException();
        }

        ArgumentList argumentList = convert(methodInvocationContext.argumentList());
        MethodCall ret = new MethodCall(TypePlaceholder.fresh(methodInvocationContext.getStart()), getReceiver(receiver), name, argumentList, methodInvocationContext.getStart());
        return ret;
    }

    private Expression convert(Java8Parser.LambdaExpressionContext expression) {
        Java8Parser.LambdaParametersContext lambdaParams = expression.lambdaParameters();
        ParameterList params;
        if(lambdaParams.Identifier() != null){
            List<FormalParameter> parameterList = new ArrayList<>();
            parameterList.add(new FormalParameter(lambdaParams.Identifier().getText(),
                    TypePlaceholder.fresh(lambdaParams.getStart()), lambdaParams.getStart()));
            params = new ParameterList(parameterList, lambdaParams.getStart());
        }else if(lambdaParams.formalParameterList() != null){
            params = convert(lambdaParams.formalParameterList());
        //}else if( lambdaParams.inferredFormalParameterList != null){
        }else {
            params = new ParameterList(new ArrayList<>(), expression.getStart());
        }

        HashMap<String,RefTypeOrTPHOrWildcardOrGeneric> lambdaLocals = new HashMap<>();
        lambdaLocals.putAll(localVars);
        for(FormalParameter param : params.getFormalparalist()){
            lambdaLocals.put(param.getName(), param.getType());
        }
        StatementGenerator lambdaGenerator = new StatementGenerator(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()));//ret-Type
        params.getFormalparalist().forEach(formalParameter -> //Für jeden Parameter einen TPH anfügen:
                funNParams.add(TypePlaceholder.fresh(expression.getStart())));
        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());
    }
}
