use symboscript_lexer::Lexer;
use symboscript_types::{
    lexer::{Token, TokenKind, TokenValue},
    parser::*,
};
use symboscript_utils::report_error;
#[macro_use]
mod macro_utils;
pub struct Parser<'a> {
    path: &'a str,
    source: &'a str,
    lexer: Lexer<'a>,
    cur_token: Token,
}
impl<'a> Parser<'a> {
    pub fn new(path: &'a str, source: &'a str) -> Self {
        Self {
            path,
            source,
            lexer: Lexer::new(path, source, false),
            cur_token: Token::default(),
        }
    }
    pub fn parse(&mut self) -> Ast {
        self.eat(TokenKind::Start);
        return Ast {
            program: self.program(),
        };
    }
    fn program(&mut self) -> Program {
        Program {
            node: Node {
                start: 0,
                end: self.source.len(),
            },
            body: self.body(),
        }
    }
    fn body(&mut self) -> Vec<Statement> {
        let mut body = vec![];
        loop {
            match self.cur_kind() {
                TokenKind::Eof | TokenKind::RBrace => break,
                _ => {
                    body.push(self.statement());
                }
            }
        }
        body
    }
    fn statement(&mut self) -> Statement {
        match self.cur_kind() {
            TokenKind::Let => self.var_decl(false),
            TokenKind::Function | TokenKind::Async => self.fn_decl(),
            TokenKind::Scope => self.scope_decl(),
            TokenKind::If => self.if_stmt(),
            TokenKind::For => self.for_stmt(),
            TokenKind::While => self.while_stmt(),
            TokenKind::Loop => self.loop_stmt(),
            TokenKind::Continue => self.continue_stmt(),
            TokenKind::Break => self.break_stmt(),
            TokenKind::Try => self.try_stmt(),
            TokenKind::Throw => self.throw_stmt(),
            TokenKind::Return => self.return_stmt(),
            TokenKind::Yield => self.yield_stmt(),
            TokenKind::Block => self.block_decl(),
            TokenKind::LBrace => Statement::BlockStatement(self.block_stmt()),
            _ => self.expr_stmt(),
        }
    }
    fn block_stmt(&mut self) -> Vec<Statement> {
        let mut body = vec![];
        if self.cur_kind() == TokenKind::LBrace {
            body = {
                let start = self.cur_token.start;
                self.eat(TokenKind::LBrace);
                let consequent = self.body();
                self.eat_with_start(TokenKind::RBrace, start);
                consequent
            }
        } else {
            body.push(self.statement());
        }
        return body;
    }
    fn scope_decl(&mut self) -> Statement {
        let start = self.cur_token.start;
        self.eat(TokenKind::Scope);
        let id = format!("{}", self.cur_token.clone().value);
        self.eat(TokenKind::Identifier);
        let body = self.block_stmt();
        Statement::ScopeDeclaration(uni_builder!(self, ScopeDeclarator, start, [id, body]))
    }
    fn try_stmt(&mut self) -> Statement {
        let start = self.cur_token.start;
        self.eat(TokenKind::Try);
        let body = self.block_stmt();
        let mut handler = vec![];
        if self.cur_kind() == TokenKind::Catch {
            self.eat(TokenKind::Catch);
            handler = self.block_stmt();
        }
        let mut finalizer = vec![];
        if self.cur_kind() == TokenKind::Finally {
            self.eat(TokenKind::Finally);
            finalizer = self.block_stmt();
        }
        Statement::TryStatement(uni_builder!(
            self,
            TryStatement,
            start,
            [body, handler, finalizer]
        ))
    }
    fn loop_stmt(&mut self) -> Statement {
        let start = self.cur_token.start;
        self.eat(TokenKind::Loop);
        let body = self.block_stmt();
        Statement::LoopStatement(uni_builder!(self, LoopStatement, start, [body]))
    }
    fn while_stmt(&mut self) -> Statement {
        let start = self.cur_token.start;
        self.eat(TokenKind::While);
        let test = {
            let start = self.cur_token.start;
            self.eat(TokenKind::LParen);
            let test = self.expr();
            self.eat_with_start(TokenKind::RParen, start);
            test
        };
        let body = self.block_stmt();
        Statement::WhileStatement(uni_builder!(self, WhileStatement, start, [test, body]))
    }
    fn for_stmt(&mut self) -> Statement {
        let start = self.cur_token.start;
        self.eat(TokenKind::For);
        self.eat(TokenKind::LParen);
        let init = self.var_decl(true);
        let test = {
            let start = self.cur_token.start;
            let test = self.expr();
            self.eat_with_start(TokenKind::Semicolon, start);
            test
        };
        let update = {
            let start = self.cur_token.start;
            let update = self.expr();
            self.eat_with_start(TokenKind::RParen, start);
            update
        };
        let body = self.block_stmt();
        Statement::ForStatement(Box::new(uni_builder!(
            self,
            ForStatement,
            start,
            [init, test, update, body]
        )))
    }
    fn if_stmt(&mut self) -> Statement {
        let start = self.cur_token.start;
        self.eat(TokenKind::If);
        let test = {
            let start = self.cur_token.start;
            self.eat(TokenKind::LParen);
            let test = self.expr();
            self.eat_with_start(TokenKind::RParen, start);
            test
        };
        let consequent = self.block_stmt();
        let mut alternate = vec![];
        if self.cur_kind() == TokenKind::Else {
            self.advance();
            alternate = self.block_stmt();
        }
        Statement::IfStatement(uni_builder!(
            self,
            IfStatement,
            start,
            [test, consequent, alternate]
        ))
    }
    fn return_stmt(&mut self) -> Statement {
        word_stmt!(self, TokenKind::Return, ReturnStatement)
    }
    fn yield_stmt(&mut self) -> Statement {
        word_stmt!(self, TokenKind::Yield, YieldStatement)
    }
    fn throw_stmt(&mut self) -> Statement {
        word_stmt!(self, TokenKind::Throw, ThrowStatement)
    }
    fn block_decl(&mut self) -> Statement {
        self.advance();
        Statement::BlockStatement(self.block_stmt())
    }
    fn continue_stmt(&mut self) -> Statement {
        let start = self.cur_token.start;
        self.eat(TokenKind::Continue);
        Statement::ContinueStatement(Node::new(start, self.cur_token.end))
    }
    fn break_stmt(&mut self) -> Statement {
        let start = self.cur_token.start;
        self.eat(TokenKind::Break);
        Statement::BreakStatement(Node::new(start, self.cur_token.end))
    }
    fn fn_decl(&mut self) -> Statement {
        let start = self.cur_token.start;
        let is_async = {
            if self.cur_kind() == TokenKind::Async {
                self.eat(TokenKind::Async);
                true
            } else {
                false
            }
        };
        self.advance();
        let id = format!("{}", self.cur_token.clone().value);
        self.eat(TokenKind::Identifier);
        let params = {
            let start = self.cur_token.start;
            self.eat(TokenKind::LBracket);
            let params = self
                .parse_params()
                .into_iter()
                .map(|p| format!("{}", p.value))
                .collect();
            self.eat_with_start(TokenKind::RBracket, start);
            params
        };
        let body = {
            let start = self.cur_token.start;
            self.eat(TokenKind::LBrace);
            let body = self.body();
            self.eat_with_start(TokenKind::RBrace, start);
            body
        };
        Statement::FunctionDeclaration(uni_builder!(
            self,
            FunctionDeclarator,
            start,
            [id, params, body, is_async]
        ))
    }
    fn parse_params(&mut self) -> Vec<Token> {
        let mut params = vec![];
        if self.cur_kind() == TokenKind::RBracket {
            self.advance();
            return params;
        }
        params.push(self.cur_token.clone());
        self.eat(TokenKind::Identifier);
        while self.cur_kind() == TokenKind::Comma {
            self.advance();
            params.push(self.cur_token.clone());
            self.eat(TokenKind::Identifier);
        }
        params
    }
    fn var_decl(&mut self, only_with_init: bool) -> Statement {
        let start = self.cur_token.start;
        self.advance();
        let id = format!("{}", self.cur_token.clone().value);
        self.eat(TokenKind::Identifier);
        let mut is_formula = false;
        let init = {
            let start = self.cur_token.start;
            match self.cur_kind() {
                TokenKind::Assign => {
                    self.advance();
                    self.expr()
                }
                TokenKind::FormulaAssign => {
                    is_formula = true;
                    self.advance();
                    self.expr()
                }
                _ if !only_with_init => Expression::None(None {
                    node: Node::new(start, self.cur_token.end),
                }),
                _ => {
                    self.report_expected(start, "Assign or FormulaAssign", self.cur_kind());
                    unreachable!("Report ends proccess");
                }
            }
        };
        self.eat(TokenKind::Semicolon);
        Statement::VariableDeclaration(uni_builder!(
            self,
            VariableDeclarator,
            start,
            [id, init, is_formula]
        ))
    }
    fn expr_stmt(&mut self) -> Statement {
        let expression = self.expr();
        self.eat(TokenKind::Semicolon);
        Statement::ExpressionStatement(expression)
    }
    fn expr(&mut self) -> Expression {
        self.comma(false)
    }
    fn comma(&mut self, only_sequence: bool) -> Expression {
        let start = self.cur_token.start;
        let mut nodes = vec![];
        nodes.push(self.assign());
        while self.cur_kind() == TokenKind::Comma {
            self.advance();
            nodes.push(self.assign());
        }
        if !only_sequence && nodes.len() == 1 {
            return nodes[0].clone();
        }
        self.sequence_expression(start, nodes)
    }
    fn assign(&mut self) -> Expression {
        binary_right_associative!(
            self,
            ternary,
            [
                TokenKind::Assign,
                TokenKind::PlusAssign,
                TokenKind::MinusAssign,
                TokenKind::MultiplyAssign,
                TokenKind::DivideAssign,
                TokenKind::PowerAssign,
                TokenKind::ModuloAssign
            ]
        )
    }
    fn ternary(&mut self) -> Expression {
        let start = self.cur_token.start;
        let mut node = self.range();
        while self.cur_kind() == TokenKind::Question {
            self.advance();
            let consequent = self.range();
            self.eat(TokenKind::Colon);
            let alternate = self.expr();
            node = self.conditional_expression(start, node, consequent, alternate);
        }
        node
    }
    fn range(&mut self) -> Expression {
        binary_left_associative!(self, [TokenKind::Range], logical_or)
    }
    fn logical_or(&mut self) -> Expression {
        binary_left_associative!(self, [TokenKind::Or], logical_and)
    }
    fn logical_and(&mut self) -> Expression {
        binary_left_associative!(self, [TokenKind::And], cmp)
    }
    fn cmp(&mut self) -> Expression {
        binary_left_associative!(
            self,
            [
                TokenKind::Less,
                TokenKind::LessEqual,
                TokenKind::Greater,
                TokenKind::GreaterEqual,
                TokenKind::Equal,
                TokenKind::NotEqual,
            ],
            bit_or
        )
    }
    fn bit_or(&mut self) -> Expression {
        binary_left_associative!(self, [TokenKind::BitOr], bit_xor)
    }
    fn bit_xor(&mut self) -> Expression {
        binary_left_associative!(self, [TokenKind::BitXor], bit_and)
    }
    fn bit_and(&mut self) -> Expression {
        binary_left_associative!(self, [TokenKind::BitAnd], shift)
    }
    fn shift(&mut self) -> Expression {
        binary_left_associative!(
            self,
            [TokenKind::BitRightShift, TokenKind::BitLeftShift],
            add_sub
        )
    }
    fn add_sub(&mut self) -> Expression {
        binary_left_associative!(self, [TokenKind::Plus, TokenKind::Minus], term)
    }
    fn term(&mut self) -> Expression {
        let start = self.cur_token.start;
        let mut expr = self.power();
        while [TokenKind::Identifier, TokenKind::LParen, TokenKind::Number]
            .contains(&self.cur_kind())
        {
            let right = self.power();
            expr = self.binary_expression(start, expr, right, TokenKind::Multiply);
        }
        while [TokenKind::Multiply, TokenKind::Divide, TokenKind::Modulo].contains(&self.cur_kind())
        {
            let operator = self.cur_kind();
            self.advance();
            let right = self.power();
            expr = self.binary_expression(start, expr, right, operator);
        }
        expr
    }
    fn power(&mut self) -> Expression {
        binary_left_associative!(self, [TokenKind::Power], factor)
    }
    fn factor(&mut self) -> Expression {
        let token = self.cur_token.clone();
        match token.kind {
            TokenKind::Number | TokenKind::Str => {
                self.advance();
                return Expression::Literal(Literal {
                    node: Node::new(token.start, token.end),
                    value: token.value,
                });
            }
            TokenKind::True => {
                self.advance();
                return Expression::Literal(Literal {
                    node: Node::new(token.start, token.end),
                    value: TokenValue::Bool(true),
                });
            }
            TokenKind::False => {
                self.advance();
                return Expression::Literal(Literal {
                    node: Node::new(token.start, token.end),
                    value: TokenValue::Bool(false),
                });
            }
            TokenKind::LParen => {
                self.advance();
                let node = self.expr();
                self.eat_with_start(TokenKind::RParen, token.start);
                return node;
            }
            TokenKind::LBracket => self.read_seq_expr(token),
            TokenKind::Not
            | TokenKind::PlusPlus
            | TokenKind::MinusMinus
            | TokenKind::BitNot
            | TokenKind::Minus
            | TokenKind::Plus => {
                self.advance();
                let right = self.factor();
                return self.unary_expression(token.start, token.kind, right);
            }
            _ => return self.await_expr(),
        }
    }
    fn read_seq_expr(&mut self, token: Token) -> Expression {
        self.advance();
        match self.cur_kind() {
            TokenKind::RBracket => {
                self.advance();
                return self.sequence_expression(token.start, vec![]);
            }
            _ => {}
        }
        let mut node = self.comma(true);
        self.eat_with_start(TokenKind::RBracket, token.start);
        match node {
            Expression::SequenceExpression(seq_exp) => {
                node = self.sequence_expression(token.start, seq_exp.expressions);
            }
            _ => node = self.sequence_expression(token.start, vec![node]),
        }
        return node;
    }
    fn await_expr(&mut self) -> Expression {
        word_right_associative_expr!(self, TokenKind::Await, delete_expr, await_expr)
    }
    fn delete_expr(&mut self) -> Expression {
        word_right_associative_expr!(self, TokenKind::Delete, new_expr, delete_expr)
    }
    fn new_expr(&mut self) -> Expression {
        word_right_associative_expr!(self, TokenKind::New, dot, new_expr)
    }
    fn dot(&mut self) -> Expression {
        member_left_associative!(self, [TokenKind::Dot], call)
    }
    fn call(&mut self) -> (Expression, bool) {
        let token = self.cur_token.clone();
        match self.cur_kind() {
            TokenKind::Identifier => {
                self.advance();
                match self.cur_kind() {
                    TokenKind::LBracket => {
                        let sequence_start = self.cur_token.start;
                        self.advance();
                        match self.cur_kind() {
                            TokenKind::RBracket => {
                                self.advance();
                                let node = self.sequence_expression(sequence_start, vec![]);
                                return (
                                    self.call_expression(
                                        sequence_start,
                                        format!("{}", token.value),
                                        node,
                                    ),
                                    true,
                                );
                            }
                            _ => {}
                        }
                        let mut node = self.expr();
                        self.eat_with_start(TokenKind::RBracket, token.start);
                        match node {
                            Expression::SequenceExpression(seq_exp) => {
                                node =
                                    self.sequence_expression(sequence_start, seq_exp.expressions);
                            }
                            _ => node = self.sequence_expression(sequence_start, vec![node]),
                        }
                        return (
                            self.call_expression(token.start, format!("{}", token.value), node),
                            true,
                        );
                    }
                    _ => {
                        return (
                            Expression::Identifier(Identifier {
                                node: Node::new(token.start, token.end),
                                name: format!("{}", token.value),
                            }),
                            false,
                        );
                    }
                }
            }
            TokenKind::LBracket => {
                self.advance();
                let node = self.expr();
                self.eat_with_start(TokenKind::RBracket, token.start);
                return (node, true);
            }
            got => {
                self.report_expected(token.start, "Identifier or [", got);
                unreachable!("Report ends proccess");
            }
        }
    }
    fn call_expression(
        &mut self,
        start: usize,
        callee: String,
        arguments: Expression,
    ) -> Expression {
        Expression::CallExpression(Box::new(CallExpression {
            node: Node::new(start, self.cur_token.end),
            callee,
            arguments,
        }))
    }
    fn member_expression(
        &mut self,
        start: usize,
        object: Expression,
        property: Expression,
        is_expr: bool,
    ) -> Expression {
        Expression::MemberExpression(Box::new(MemberExpression {
            node: Node::new(start, self.cur_token.end),
            object,
            property,
            is_expr,
        }))
    }
    fn sequence_expression(&mut self, start: usize, expressions: Vec<Expression>) -> Expression {
        Expression::SequenceExpression(Box::new(SequenceExpression {
            node: Node::new(start, self.cur_token.end),
            expressions,
        }))
    }
    fn conditional_expression(
        &mut self,
        start: usize,
        test: Expression,
        consequent: Expression,
        alternate: Expression,
    ) -> Expression {
        Expression::ConditionalExpression(Box::new(ConditionalExpression {
            node: Node::new(start, self.cur_token.end),
            test,
            consequent,
            alternate,
        }))
    }
    fn binary_expression(
        &mut self,
        start: usize,
        left: Expression,
        right: Expression,
        operator: TokenKind,
    ) -> Expression {
        Expression::BinaryExpression(Box::new(BinaryExpression {
            node: Node::new(start, self.cur_token.end),
            left,
            operator: self.kind_to_bin_op(operator),
            right,
        }))
    }
    fn unary_expression(
        &mut self,
        start: usize,
        operator: TokenKind,
        right: Expression,
    ) -> Expression {
        Expression::UnaryExpression(Box::new(UnaryExpression {
            node: Node::new(start, self.cur_token.end),
            operator: self.kind_to_un_op(operator),
            right,
        }))
    }
    fn kind_to_word_op(&mut self, kind: TokenKind) -> WordOperator {
        match kind {
            TokenKind::Await => WordOperator::Await,
            TokenKind::New => WordOperator::New,
            TokenKind::Delete => WordOperator::Delete,
            got => unreachable!("This function can't be called for other tokens: ({})", got),
        }
    }
    fn kind_to_un_op(&mut self, kind: TokenKind) -> UnaryOperator {
        match kind {
            TokenKind::MinusMinus => UnaryOperator::MinusMinus,
            TokenKind::BitNot => UnaryOperator::BitNot,
            TokenKind::Not => UnaryOperator::Not,
            TokenKind::PlusPlus => UnaryOperator::PlusPlus,
            TokenKind::Plus => UnaryOperator::Plus,
            TokenKind::Minus => UnaryOperator::Minus,
            got => unreachable!("This function can't be called for other tokens: ({})", got),
        }
    }
    fn kind_to_bin_op(&mut self, kind: TokenKind) -> BinaryOperator {
        match kind {
            TokenKind::Plus => BinaryOperator::Plus,
            TokenKind::Minus => BinaryOperator::Minus,
            TokenKind::Multiply => BinaryOperator::Multiply,
            TokenKind::Divide => BinaryOperator::Divide,
            TokenKind::Power => BinaryOperator::Power,
            TokenKind::Range => BinaryOperator::Range,
            TokenKind::Modulo => BinaryOperator::Modulo,
            TokenKind::And => BinaryOperator::And,
            TokenKind::Or => BinaryOperator::Or,
            TokenKind::Xor => BinaryOperator::Xor,
            TokenKind::BitAnd => BinaryOperator::BitAnd,
            TokenKind::BitOr => BinaryOperator::BitOr,
            TokenKind::BitXor => BinaryOperator::BitXor,
            TokenKind::BitLeftShift => BinaryOperator::BitLeftShift,
            TokenKind::BitRightShift => BinaryOperator::BitRightShift,
            TokenKind::Assign => BinaryOperator::Assign,
            TokenKind::PlusAssign => BinaryOperator::PlusAssign,
            TokenKind::MinusAssign => BinaryOperator::MinusAssign,
            TokenKind::MultiplyAssign => BinaryOperator::MultiplyAssign,
            TokenKind::DivideAssign => BinaryOperator::DivideAssign,
            TokenKind::PowerAssign => BinaryOperator::PowerAssign,
            TokenKind::ModuloAssign => BinaryOperator::ModuloAssign,
            TokenKind::Equal => BinaryOperator::Equal,
            TokenKind::NotEqual => BinaryOperator::NotEqual,
            TokenKind::Less => BinaryOperator::Less,
            TokenKind::LessEqual => BinaryOperator::LessEqual,
            TokenKind::Greater => BinaryOperator::Greater,
            TokenKind::GreaterEqual => BinaryOperator::GreaterEqual,
            _ => unreachable!("This function can't be called for other tokens: ({kind})"),
        }
    }
    fn eat(&mut self, kind: TokenKind) {
        self.eat_with_start(kind, self.cur_token.start);
    }
    fn eat_with_start(&mut self, kind: TokenKind, start: usize) -> bool {
        if self.at(kind) {
            self.advance();
            return true;
        }
        let val = self.cur_token.value.to_string();
        self.report_expected(
            start,
            kind,
            format!(
                "{} {}",
                self.cur_kind(),
                if self.cur_token.value == TokenValue::None {
                    ""
                } else {
                    &val
                }
            ),
        );
        unreachable!("Report ends proccess");
    }
    fn report_expected<T: std::fmt::Display, U: std::fmt::Display>(
        &self,
        start: usize,
        expected: T,
        got: U,
    ) {
        report_error(
            self.path,
            self.source,
            &format!("Expected {expected} but got {got}"),
            start,
            self.cur_token.end,
        );
    }
    fn advance(&mut self) {
        let token = self.lexer.next_token();
        self.cur_token = token;
    }
    fn cur_kind(&self) -> TokenKind {
        self.cur_token.kind
    }
    fn at(&self, kind: TokenKind) -> bool {
        self.cur_kind() == kind
    }
}