package Interpreter;

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

public class Parser {
    private final List<Token> tokens;
    private int currentToken = 0;

    Parser(List<Token> tokens){
        this.tokens=tokens;
    }

    List<Statement> parse(){
        List<Statement> statements =  new ArrayList<>();
        while (!checkEOF()){
            statements.add(declaration());
        }
        return statements;
        
    }

    private Statement declaration(){
        try{
            if (matchAndAdvance(TokenType.VAR)){
                if (matchOrError(TokenType.DEFINE, ":: Required for variable definition")){
                    if (matchOrError(TokenType.IDENTIFIER,"Expected variable name.")){
                        Token varName = getPreviousToken();
                        return new Statement.VariableDeclaration(varName);
                    } 
                }
            }
            return statement();
        } catch (Error e){
            currentToken++;
            return null;
        }
    }

    private Statement statement(){
        if (matchAndAdvance(TokenType.PRINT)){
            Expression expression = expression();
            return new Statement.PrintStatement(expression);
        }
        return expressionStatement();
    }



    private Statement expressionStatement(){
        Expression expression = assignment();
        return new Statement.ExpressionStatement(expression);
    }

    private Expression assignment(){
        Expression variable = expression();
        if (matchAndAdvance(TokenType.EQUALS)){
            Expression assignedvalue = expression();

            if (variable instanceof Expression.Variable){
                return new Expression.AssignmentExpression(((Expression.Variable)variable).name,assignedvalue);
            }
            throw error("Incorrect assignment operation");
        }
        return variable;
    }

    private Expression expression(){
        Expression createdExpression = equality();
        return createdExpression;
    }

    private Expression equality(){
        Expression createdExpression = comparison();
        while (matchAndAdvance(TokenType.EQUALITY)){
            Token op = getPreviousToken();
            Expression right = comparison();
            createdExpression = new Expression.Binary(createdExpression, op, right);
        }
        return createdExpression;
    }

    private Expression comparison(){
        Expression createdExpression = term();
        while (matchAndAdvance(TokenType.GREATER)||matchAndAdvance(TokenType.LESS)){
            Token op = getPreviousToken();
            Expression right = term();
            createdExpression = new Expression.Binary(createdExpression, op, right);
        }
        return createdExpression;
    }

    private Expression term(){
        Expression createdExpression = factor();
        while (matchAndAdvance(TokenType.PLUS)||matchAndAdvance(TokenType.MINUS)){
            Token op = getPreviousToken();
            Expression right = factor();
            createdExpression = new Expression.Binary(createdExpression, op, right);
        }
        return createdExpression;
    }

    private Expression factor(){
        Expression createdExpression = primary();
        while (matchAndAdvance(TokenType.STAR)||matchAndAdvance(TokenType.SLASH)){
            Token op = getPreviousToken();
            Expression right = primary();
            createdExpression = new Expression.Binary(createdExpression, op, right);
        }
        return createdExpression;
    }

    private Expression primary(){
        if (matchAndAdvance(TokenType.NUMBER)){
            return new Expression.Literal(getPreviousToken());
        }

        if (matchAndAdvance(TokenType.IDENTIFIER)) {

            return new Expression.Variable(getPreviousToken());
          }

        if (matchAndAdvance(TokenType.LEFT_PAREN)){
            Expression expr = expression();
            if (matchAndAdvance(TokenType.RIGHT_PAREN)){
                return new Expression.BracketedExpression(expr);
            }
            else{
                throw error("Expected ')");
            }
        }
        throw error("Expected Expression");
    }

    private void advanceToken(){
        if (!checkEOF()) {
            currentToken++;
        };
    }

    private boolean matchAndAdvance(TokenType type){
        if (checkToken(type)) {
            advanceToken();
            return true;
        }
        return false;
    }

    private boolean matchOrError(TokenType type,String errorMessage){
        if (matchAndAdvance(type)){
            return true;
        }
        throw error(errorMessage);
    }

    private boolean checkToken(TokenType type){
        if (checkEOF()) return false;
        return getCurrentToken().type == type; 
    }

    private boolean checkEOF(){
        return tokens.get(currentToken).type==TokenType.EOF;
    }

    private Token getCurrentToken(){
        return tokens.get(currentToken);
    }

    private Token getPreviousToken(){
        return tokens.get(currentToken - 1);
    }

    private Error error(String message){
        Language.displayError(message);
        return new Error();
    }


}