//! Statement parsing for the language front-end.
//!
//! Implements a simple recursive-descent parser for
//! *Fermented UnrealScript statements*.

use crate::ast::{Statement, StatementRef};
use crate::lexer::{Keyword, Token, TokenSpan};
use crate::parser::{ParseErrorKind, ResultRecoveryExt};

impl<'src, 'arena> crate::parser::Parser<'src, 'arena> {
    /// Parses a single statement.
    ///
    /// Does not consume a trailing `;` except for [`Statement::Empty`].
    /// The caller handles semicolons (WRONG NOW - WE MUST HANDLE THEM). Returns [`Some`] if a statement is
    /// recognized; otherwise [`None`].
    /// ALSO WE SPECIFICALLY DONT HANDLE EXPRESSION TYPE STATEMENTS - on them also return NONE!!!!
    /// Basically only generates errors if statement is recognized, but got fucked up.
    #[must_use]
    pub(crate) fn parse_statement(&mut self) -> Option<StatementRef<'src, 'arena>> {
        let Some((token, lexeme, position)) = self.peek_token_lexeme_and_position() else {
            return None;
        };
        match token {
            // Empty statement
            Token::Semicolon => {
                self.advance(); // `;`
                Some(
                    self.arena
                        .alloc_node(Statement::Empty, TokenSpan::new(position)),
                )
            }
            // UnrealScript `local` declaration
            Token::Keyword(Keyword::Local) => {
                self.advance(); // `local`
                let start = position;
                let Some(type_spec) = self
                    .parse_type_specifier()
                    .sync_error_at(self, crate::parser::SyncLevel::StatementTerminator)
                    .ok_or_report(self)
                else {
                    return Some(crate::arena::ArenaNode::new_in(
                        Statement::Error,
                        crate::lexer::TokenSpan::new(position),
                        self.arena,
                    ));
                };
                let declarators = self.parse_variable_declarators(position, *type_spec.span());
                let mut span = TokenSpan::range(start, self.last_consumed_position_or_start());
                let mut error = if self.peek_token().is_some() {
                    self
                        .expect(
                            Token::Semicolon,
                            ParseErrorKind::LocalVariableDeclarationMissingSemicolon,
                        )
                        .widen_error_span_from(position)
                        .related_token("local_keyword", position)
                } else {
                    Ok(self.peek_position_or_eof())
                };
                if let Some(last_declarator) = declarators.last() {
                    error = error.related("last_declarator", *last_declarator.span())
                }
                let semicolon_position = error.ok_or_report(self);
                if let Some(semicolon_position) = semicolon_position {
                    span.extend_to(semicolon_position);
                }
                Some(self.arena.alloc_node(
                    Statement::LocalVariableDeclaration {
                        type_spec,
                        declarators,
                    },
                    span,
                ))
            }
            Token::Identifier | Token::Keyword(Keyword::Begin | Keyword::End)
                if matches!(self.peek_token_at(1), Some(Token::Colon)) =>
            {
                self.advance(); // ident/begin/end
                self.advance(); // :
                Some(self.arena.alloc_node(
                    Statement::Label(self.arena.string(lexeme)),
                    TokenSpan::range(position, self.last_consumed_position_or_start()),
                ))
            }
            // Nested function/event/operator inside blocks
            _ if is_function_starter(token) => {
                let callable_definition = self.parse_callable_definition();
                let span = *callable_definition.span();
                Some(
                    self.arena
                        .alloc_node(Statement::Function(callable_definition), span),
                )
            }
            // Not a statement
            _ => None,
        }
    }
}

fn is_function_starter(token: Token) -> bool {
    token.is_valid_function_modifier()
        || token == Token::Keyword(Keyword::Function)
        || token == Token::Keyword(Keyword::Event)
}
