package Interpreter;

import java.util.List;

import Interpreter.Expression.*;
import Interpreter.Statement.ExpressionStatement;
import Interpreter.Statement.PrintStatement;
import Interpreter.Statement.VariableDeclaration;

public class Interpreter {

    private Environment environment = new Environment();

    void interpret(List<Statement> statements){
        try{
            for (Statement statement: statements){
                evaluateStatement(statement);
            }
        } catch (Error e){

        }
    }

    private Object evaluateStatement(Statement statement){
        switch(statement.getStatmentType()){
            case "exprStmt":
                return evalExpressionStatement((ExpressionStatement)statement);
            case "vardec":
                return evalVariableDeclaration((VariableDeclaration)statement);
            case "print":
                return evalPrintStatement((PrintStatement)statement);
            default:
                return null;
        }
    } 
    private Object evalExpressionStatement(ExpressionStatement stmt){
        return evaluateExpression(stmt.expr);
    }

    private Object evalVariableDeclaration(VariableDeclaration vardec){
        environment.defineVariable(vardec.name.text, null);
        return null;
    }

    private Object evalPrintStatement(PrintStatement print){
        System.out.println(evaluateExpression(print.expr));
        return null;
    }

    private Object evaluateExpression(Expression expression){
        switch(expression.getExpressionType()){
            case "binary":
                return evaluateBinaryExpression((Binary)expression);
            case "literal":
                return evaluateLiteralExpression((Literal)expression);
            case "bracket":
                return evaluateBracketedExpression((BracketedExpression)expression);
            case "assign":
                return evaluateAssignmentExpression((AssignmentExpression)expression);
            case "var":
                return evaluateVariableExpression((Variable)expression);
            default:
                return null;
        }
    }

    private Object evaluateBinaryExpression(Binary expr){
        Object leftEval = evaluateExpression(expr.left);
        Object rightEval = evaluateExpression(expr.right);
        switch (expr.op.type){
            case PLUS:
                if (checkOperandsNum(leftEval, leftEval)){
                    return (double)leftEval + (double)rightEval;
                } 
            case STAR:
                if (checkOperandsNum(leftEval, leftEval)){
                    return (double)leftEval * (double)rightEval;
                } 
            case MINUS:
                if (checkOperandsNum(leftEval, leftEval)){
                    return (double)leftEval - (double)rightEval;
                }
            case SLASH:
                if (checkOperandsNum(leftEval, leftEval)){
                    return (double)leftEval / (double)rightEval;
                }

            case GREATER:
                if (checkOperandsNum(leftEval, leftEval)){
                    return (double)leftEval > (double)rightEval;
                }
            case LESS:
                if (checkOperandsNum(leftEval, leftEval)){
                    return (double)leftEval < (double)rightEval;
                }
            
            case EQUALITY:
                return leftEval.equals(rightEval);
            default:
                break;
        }
        return null;
    }

    private Object evaluateLiteralExpression(Literal expr){
        return expr.value.value;
    }

    private Object evaluateBracketedExpression(BracketedExpression expr){
        return evaluateExpression(expr.expr);
    }

    private Object evaluateAssignmentExpression(AssignmentExpression expr){
        Object assignedValue = evaluateExpression(expr.value);
        environment.assignVariable(expr.name.text, assignedValue);
        return null;
    }

    private Object evaluateVariableExpression(Variable expr){
        return environment.getVariable(expr.name.text);
    }

    private boolean checkOperandsNum(Object left, Object right){
        if (left instanceof Double && right instanceof Double){
            return true;
        } else {
            Language.displayError("Operands must be numbers");
            throw new Error();
        }
    }
}