// FILE: src/parser/core/precedence.rs

use crate::parser::ast::Expr;
use crate::parser::lexer::{Token, TokenKind};
use super::utils::Parser;
use super::primary::parse_primary;

fn pos(parser: &Parser) -> (usize, usize) {
    parser.current().map(|t| (t.line, t.col)).unwrap_or((0, 0))
}

pub fn parse_ternary(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_pipe(parser)?;
    while let Some(Token { kind: TokenKind::Question, .. }) = parser.current() {
        parser.advance();
        let if_true = parse_expression(parser)?;
        parser.expect(&TokenKind::Colon)?;
        let if_false = parse_expression(parser)?;
        expr = Expr::Ternary {
            cond: Box::new(expr),
            if_true: Box::new(if_true),
            if_false: Box::new(if_false),
            line,
            col,
        };
    }
    Ok(expr)
}

pub fn parse_pipe(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_xor(parser)?;
    loop {
        match parser.current() {
            Some(Token { kind: TokenKind::Pipe, .. }) => {
                parser.advance();
                let right = parse_xor(parser)?;
                expr = Expr::BitOr {
                    left: Box::new(expr),
                    right: Box::new(right),
                    line,
                    col,
                };
            }
            _ => break,
        }
    }
    Ok(expr)
}

pub fn parse_xor(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_tilde(parser)?;
    loop {
        match parser.current() {
            Some(Token { kind: TokenKind::Caret, .. }) => {
                parser.advance();
                let right = parse_tilde(parser)?;
                expr = Expr::BitXor {
                    left: Box::new(expr),
                    right: Box::new(right),
                    line,
                    col,
                };
            }
            _ => break,
        }
    }
    Ok(expr)
}

pub fn parse_tilde(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_or(parser)?;
    loop {
        match parser.current() {
            Some(Token { kind: TokenKind::Tilde, .. }) => {
                parser.advance();
                let right = parse_or(parser)?;
                expr = Expr::BitTilde {
                    left: Box::new(expr),
                    right: Box::new(right),
                    line,
                    col,
                };
            }
            _ => break,
        }
    }
    Ok(expr)
}

pub fn parse_or(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_and(parser)?;
    loop {
        match parser.current() {
            Some(Token { kind: TokenKind::OrOr, .. }) => {
                parser.advance();
                let right = parse_and(parser)?;
                expr = Expr::OrOr {
                    left: Box::new(expr),
                    right: Box::new(right),
                    line,
                    col,
                };
            }
            _ => break,
        }
    }
    Ok(expr)
}

pub fn parse_and(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_bitand(parser)?;
    loop {
        match parser.current() {
            Some(Token { kind: TokenKind::AndAnd, .. }) => {
                parser.advance();
                let right = parse_bitand(parser)?;
                expr = Expr::AndAnd {
                    left: Box::new(expr),
                    right: Box::new(right),
                    line,
                    col,
                };
            }
            _ => break,
        }
    }
    Ok(expr)
}

pub fn parse_bitand(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_shifts(parser)?;
    loop {
        match parser.current() {
            Some(Token { kind: TokenKind::Amp, .. }) => {
                parser.advance();
                let right = parse_shifts(parser)?;
                expr = Expr::BitAnd {
                    left: Box::new(expr),
                    right: Box::new(right),
                    line,
                    col,
                };
            }
            _ => break,
        }
    }
    Ok(expr)
}

pub fn parse_shifts(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_comparison(parser)?;
    loop {
        match parser.current() {
            Some(Token { kind: TokenKind::Shl, .. }) => {
                parser.advance();
                let right = parse_comparison(parser)?;
                expr = Expr::Shl {
                    left: Box::new(expr),
                    right: Box::new(right),
                    line,
                    col,
                };
            }
            Some(Token { kind: TokenKind::Shr, .. }) => {
                parser.advance();
                let right = parse_comparison(parser)?;
                expr = Expr::Shr {
                    left: Box::new(expr),
                    right: Box::new(right),
                    line,
                    col,
                };
            }
            _ => break,
        }
    }
    Ok(expr)
}

pub fn parse_comparison(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_term(parser)?;
    loop {
        let op = match parser.current() {
            Some(Token { kind: TokenKind::Gt, .. })   => { parser.advance(); ">"  }
            Some(Token { kind: TokenKind::Lt, .. })   => { parser.advance(); "<"  }
            Some(Token { kind: TokenKind::EqEq, .. }) => { parser.advance(); "==" }
            Some(Token { kind: TokenKind::Le, .. })   => { parser.advance(); "<=" }
            Some(Token { kind: TokenKind::Ge, .. })   => { parser.advance(); ">=" }
            Some(Token { kind: TokenKind::Ne, .. })   => { parser.advance(); "!=" }
            _ => break,
        };
        let right = parse_term(parser)?;
        expr = match op {
            ">"  => Expr::Gt { left: Box::new(expr), right: Box::new(right), line, col },
            "<"  => Expr::Lt { left: Box::new(expr), right: Box::new(right), line, col },
            "==" => Expr::EqEq { left: Box::new(expr), right: Box::new(right), line, col },
            "<=" => Expr::Le { left: Box::new(expr), right: Box::new(right), line, col },
            ">=" => Expr::Ge { left: Box::new(expr), right: Box::new(right), line, col },
            "!=" => Expr::Ne { left: Box::new(expr), right: Box::new(right), line, col },
            _    => unreachable!(),
        };
    }
    Ok(expr)
}

pub fn parse_term(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_factor(parser)?;
    loop {
        let op = match parser.current() {
            Some(Token { kind: TokenKind::Plus, .. })  => { parser.advance(); "+" }
            Some(Token { kind: TokenKind::Minus, .. }) => { parser.advance(); "-" }
            _ => break,
        };
        let right = parse_factor(parser)?;
        expr = match op {
            "+" => Expr::Add { left: Box::new(expr), right: Box::new(right), line, col },
            "-" => Expr::Sub { left: Box::new(expr), right: Box::new(right), line, col },
            _   => unreachable!(),
        };
    }
    Ok(expr)
}

pub fn parse_factor(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_postfix(parser)?;
    loop {
        let op = match parser.current() {
            Some(Token { kind: TokenKind::Star, .. })    => { parser.advance(); "*" }
            Some(Token { kind: TokenKind::Slash, .. })   => { parser.advance(); "/" }
            Some(Token { kind: TokenKind::Percent, .. }) => { parser.advance(); "%" }
            _ => break,
        };
        let right = parse_postfix(parser)?;
        expr = match op {
            "*" => Expr::Mul { left: Box::new(expr), right: Box::new(right), line, col },
            "/" => Expr::Div { left: Box::new(expr), right: Box::new(right), line, col },
            "%" => Expr::Mod { left: Box::new(expr), right: Box::new(right), line, col },
            _   => unreachable!(),
        };
    }
    Ok(expr)
}

pub fn parse_postfix(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    let (line, col) = pos(parser);
    let mut expr = parse_primary(parser)?;
    loop {
        if let Some(Token { kind: TokenKind::LParen, .. }) = parser.current() {
            parser.advance();
            let mut args = Vec::new();
            if let Some(Token { kind: TokenKind::RParen, .. }) = parser.current() {
                parser.advance();
            } else {
                args.push(parse_expression(parser)?);
                while let Some(Token { kind: TokenKind::Comma, .. }) = parser.current() {
                    parser.advance();
                    args.push(parse_expression(parser)?);
                }
                parser.expect(&TokenKind::RParen)?;
            }
            expr = Expr::FunctionCall {
                callee: Box::new(expr),
                args,
                line,
                col,
            };
        } else if let Some(Token { kind: TokenKind::LBracket, .. }) = parser.current() {
            parser.advance();
            let idx = parse_expression(parser)?;
            parser.expect(&TokenKind::RBracket)?;
            expr = Expr::Index {
                base: Box::new(expr),
                index: Box::new(idx),
                line,
                col,
            };
        } else {
            break;
        }
    }
    Ok(expr)
}

pub fn parse_expression(parser: &mut Parser) -> Result<Expr, super::driver::ParseError> {
    parse_ternary(parser)
}
