use crate::arena::ArenaVec;
use crate::ast::{Parameter, ParameterModifier, ParameterModifierKind, ParameterRef};
use crate::lexer::{Keyword, Token, TokenSpan};
use crate::parser::{ParseErrorKind, ResultRecoveryExt, SyncLevel};

impl<'src, 'arena> crate::parser::Parser<'src, 'arena> {
    // allowed to switch to result returning
    pub(crate) fn parse_parameter_list(&mut self) -> ArenaVec<'arena, ParameterRef<'src, 'arena>> {
        let mut params = self.arena.vec();
        if matches!(self.peek_token(), Some(Token::RightParenthesis)) {
            return params;
        }

        loop {
            let start_pos = self.last_consumed_position_or_start();

            let mut modifiers = self.arena.vec();

            while let Some((next_keyword, next_position)) = self.peek_keyword_and_position() {
                match next_keyword {
                    Keyword::Optional => {
                        modifiers.push(ParameterModifier {
                            kind: ParameterModifierKind::Optional,
                            position: next_position,
                        });
                        self.advance();
                    }
                    Keyword::Out => {
                        modifiers.push(ParameterModifier {
                            kind: ParameterModifierKind::Out,
                            position: next_position,
                        });
                        self.advance();
                    }
                    Keyword::Coerce => {
                        modifiers.push(ParameterModifier {
                            kind: ParameterModifierKind::Coerce,
                            position: next_position,
                        });
                        self.advance();
                    }
                    Keyword::Skip => {
                        modifiers.push(ParameterModifier {
                            kind: ParameterModifierKind::Skip,
                            position: next_position,
                        });
                        self.advance();
                    }
                    _ => break,
                }
            }

            let type_spec = match self.parse_type_specifier() {
                Ok(t) => t,
                Err(e) => {
                    self.report_error(e);
                    self.recover_until(SyncLevel::ListSeparator);
                    if self.eat(Token::Comma) {
                        continue;
                    }
                    break;
                }
            };

            let name = self
                .parse_identifier(ParseErrorKind::ParamMissingIdentifier)
                .unwrap_or_fallback(self);

            let array_len = match self.parse_array_len_expr() {
                Ok(v) => v,
                Err(e) => {
                    self.report_error(e);
                    self.recover_until(SyncLevel::CloseBracket);
                    let _ = self.eat(Token::RightBracket);
                    None
                }
            };

            let default_value = if self.eat(Token::Assign) {
                Some(self.parse_expression())
            } else {
                None
            };

            let span = TokenSpan::range(start_pos, self.last_consumed_position_or_start());
            params.push(self.arena.alloc_node(
                Parameter {
                    modifiers,
                    type_specifier: type_spec,
                    name,
                    array_size: array_len,
                    default_value,
                },
                span,
            ));

            if !self.eat(Token::Comma) || matches!(self.peek_token(), Some(Token::RightParenthesis))
            {
                break;
            }

            self.ensure_forward_progress(start_pos);
        }

        params
    }
}
