use crate::{
    ast::err::ParseError,
    ast::types::CompileUnit,
    diagnostic::{Diagnositics, span::Span},
    lexer::types::{Token, TokenValue},
};

mod decl;
mod expr;
mod stmt;

#[cfg(test)]
mod tests;

pub struct Parser {
    tokens: Vec<Token>,
    eof_token: Token,
    pub diagnostics: Diagnositics,
    pos: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ParseType {
   MustParse,
    TryParse,
}
enum ParseProcessError {
    TryNext,
    ErrorInMatch
}
impl Parser {
    pub fn new(tokens: Vec<Token>, diagnostics: Diagnositics) -> Self {
        let eof_pos = tokens.last().map_or(0, |token| token.span.end);
        Self {
            tokens,
            eof_token: Token {
                value: TokenValue::Eof,
                span: Span {
                    start: eof_pos,
                    end: eof_pos,
                },
            },
            diagnostics,
            pos: 0,
        }
    }
    pub fn parse(&mut self) -> CompileUnit {
        self.parse_compile_unit()
    }
    fn peek(&self) -> &Token {
        self.tokens.get(self.pos).unwrap_or(&self.eof_token)
    }
    fn peek_n(&self, n: usize) -> &Token {
        self.tokens.get(self.pos + n).unwrap_or(&self.eof_token)
    }
    fn next(&mut self) -> Token {
        let token = self.peek().clone();
        if !matches!(token.value, TokenValue::Eof) {
            self.pos += 1;
        }
        token
    }
    fn advance(&mut self, n: usize) {
        self.pos += n;
        assert!(self.pos <= self.tokens.len());
    }
    fn back(&mut self, n: usize) {
        assert!(self.pos >= n);
        self.pos -= n;
    }
    fn last(&self) -> &Token {
        if self.pos == 0 {
            &self.eof_token
        } else {
            self.tokens.get(self.pos - 1).unwrap_or(&self.eof_token)
        }
    }
    fn is_eof(&self) -> bool {
        matches!(self.peek().value, TokenValue::Eof)
    }
    fn must_match_token(&mut self, expected: &TokenValue, diagnostic_text: &'static str) -> Result<(), ParseProcessError> {
        if &self.peek().value == expected {
            self.advance(1);
            Ok(())
        } else {
            let token = self.next();
            self.diagnostics.add_from_frontend_error(
                ParseError::ExpectedBefore(token.value, diagnostic_text),
                token.span,
            );
            Err(ParseProcessError::ErrorInMatch)
        }
    }
    fn must_have_some(&mut self, diagnostic_text: &'static str) -> Result<(), ParseProcessError> {
        if self.is_eof() {
            let span = self.last().span;
            self.diagnostics
                .add_from_frontend_error(ParseError::ExpectedBefore(TokenValue::Eof, diagnostic_text), span);
            return Err(ParseProcessError::ErrorInMatch);
        }
        Ok(())

    }
    fn until_next_token(&mut self, expected: &[TokenValue]) {
        while !self.is_eof() {
            if expected.contains(&self.peek().value) {
                self.advance(1);
                break;
            }
            self.advance(1);
        }
    }
}
