use crate::{
    ast::err::ParseError,
    ast::types::{
        ArrayDimension, CompileUnit, Expr, FuncDeclStmt, GlobalDeclStmt, Param, StorageClass,
        VarDeclStmt, VarDeclStmtValue,
    },
    diagnostic::span::Span,
    lexer::types::{TokenValue, TypeIdent},
};

use super::{ParseProcessError, ParseType, Parser};

struct DeclSpecifiers {
    storage_class: Option<StorageClass>,
    type_specifier: TypeIdent,
    type_span: Span,
}

struct Declarator {
    name: String,
    name_span: Span,
    suffixes: Vec<DeclaratorSuffix>,
}

enum DeclaratorSuffix {
    Array(ArrayDimension),
    Function {
        params: Vec<Param>,
        span: Span,
    },
}

struct InitDeclarator {
    declarator: Declarator,
    initializer: Option<Expr>,
}

impl Parser {
    pub(super) fn parse_compile_unit(&mut self) -> CompileUnit {
        let mut global_decls = vec![];
        while !self.is_eof() {
            if let Some(decl) = self.parse_external_decl_stmt() {
                global_decls.push(decl);
            } else {
                self.until_next_token(&[TokenValue::Semicolon, TokenValue::RBrace]);
            }
        }
        CompileUnit { global_decls }
    }

    fn parse_external_decl_stmt(&mut self) -> Option<GlobalDeclStmt> {
        let specifiers = match self.parse_decl_specifiers(ParseType::MustParse) {
            Ok(specifiers) => specifiers,
            Err(ParseProcessError::ErrorInMatch) => return None,
            Err(ParseProcessError::TryNext) => unreachable!(),
        };
        let declarator = match self.parse_declarator() {
            Ok(declarator) => declarator,
            Err(ParseProcessError::ErrorInMatch) => return None,
            Err(ParseProcessError::TryNext) => unreachable!(),
        };

        if self.peek().value == TokenValue::LBrace {
            match self.finish_func_def_stmt(specifiers, declarator) {
                Ok(func_decl) => return Some(GlobalDeclStmt::FuncDecl(func_decl)),
                Err(ParseProcessError::ErrorInMatch) => return None,
                Err(ParseProcessError::TryNext) => unreachable!(),
            }
        }

        match self.finish_var_decl_stmt(specifiers, declarator, true, true) {
            Ok(var_decl) => Some(GlobalDeclStmt::VarDecl(var_decl)),
            Err(ParseProcessError::ErrorInMatch) => None,
            Err(ParseProcessError::TryNext) => unreachable!(),
        }
    }

    fn parse_decl_specifiers(
        &mut self,
        parse_type: ParseType,
    ) -> Result<DeclSpecifiers, ParseProcessError> {
        let storage_class = if self.peek().value == TokenValue::Static {
            self.advance(1);
            Some(StorageClass::Static)
        } else {
            None
        };

        let type_token = self.peek().clone();
        let type_specifier = match type_token.value.as_type_ident() {
            Some(type_specifier) => {
                self.advance(1);
                type_specifier
            }
            None if parse_type == ParseType::TryParse && storage_class.is_none() => {
                return Err(ParseProcessError::TryNext);
            }
            None => {
                let token = self.next();
                self.diagnostics.add_from_frontend_error(
                    ParseError::ExpectedBefore(token.value, "type specifier"),
                    token.span,
                );
                return Err(ParseProcessError::ErrorInMatch);
            }
        };

        Ok(DeclSpecifiers {
            storage_class,
            type_specifier,
            type_span: type_token.span,
        })
    }

    fn parse_declarator(&mut self) -> Result<Declarator, ParseProcessError> {
        self.parse_direct_declarator()
    }

    fn parse_direct_declarator(&mut self) -> Result<Declarator, ParseProcessError> {
        let mut declarator = match self.peek().value.clone() {
            TokenValue::Ident(name) => {
                let name_span = self.peek().span;
                self.advance(1);
                Declarator {
                    name,
                    name_span,
                    suffixes: vec![],
                }
            }
            TokenValue::LParen => {
                self.advance(1);
                let declarator = self.parse_declarator()?;
                self.must_match_token(&TokenValue::RParen, "`)`")?;
                declarator
            }
            _ => {
                let token = self.next();
                self.diagnostics.add_from_frontend_error(
                    ParseError::ExpectedBefore(token.value, "declarator"),
                    token.span,
                );
                return Err(ParseProcessError::ErrorInMatch);
            }
        };

        loop {
            match self.peek().value {
                TokenValue::LBracket => {
                    let dimension = self.parse_array_declarator_suffix()?;
                    declarator.suffixes.push(DeclaratorSuffix::Array(dimension));
                }
                TokenValue::LParen => {
                    let start_span = self.next().span;
                    let params = self.parse_parameter_list_after_lparen()?;
                    let end_span = self.last().span;
                    declarator.suffixes.push(DeclaratorSuffix::Function {
                        params,
                        span: Span::from_two(start_span, end_span),
                    });
                }
                _ => break,
            }
        }

        Ok(declarator)
    }

    fn parse_array_declarator_suffix(&mut self) -> Result<ArrayDimension, ParseProcessError> {
        let start_span = self.next().span;
        let value = if self.peek().value == TokenValue::RBracket {
            None
        } else {
            Some(self.parse_expr()?)
        };
        let end_span = match &self.peek().value {
            TokenValue::RBracket => {
                let span = self.peek().span;
                self.advance(1);
                span
            }
            // TokenValue::Eof => {
            //     self.diagnostics.add_from_frontend_error(
            //         ParseError::ExpectedBefore(TokenValue::Eof, "`]`"),
            //         start_span,
            //     );
            //     return Err(ParseProcessError::ErrorInMatch);
            // }
            _ => {
                let token = self.next();
                self.diagnostics.add_from_frontend_error(
                    ParseError::ExpectedBefore(token.value, "`]`"),
                    token.span,
                );
                return Err(ParseProcessError::ErrorInMatch);
            }
        };
        Ok(ArrayDimension {
            value,
            span: Span::from_two(start_span, end_span),
        })
    }

    fn parse_parameter_list_after_lparen(&mut self) -> Result<Vec<Param>, ParseProcessError> {
        if self.peek().value == TokenValue::RParen {
            self.advance(1);
            return Ok(vec![]);
        }

        let mut params = vec![];
        loop {
            match self.parse_param() {
                Ok(param) => params.push(param),
                Err(error) => {
                    self.until_next_token(&[TokenValue::Comma, TokenValue::RParen]);
                    if self.last().value == TokenValue::RParen {
                        return Err(error);
                    }
                    continue;
                }
            }

            match self.peek().value {
                TokenValue::Comma => {
                    self.advance(1);
                }
                TokenValue::RParen => {
                    self.advance(1);
                    break;
                }
                _ => {
                    self.must_match_token(&TokenValue::RParen, "`,` or `)`")?;
                    break;
                }
            }
        }

        Ok(params)
    }

    fn parse_param(&mut self) -> Result<Param, ParseProcessError> {
        let specifiers = self.parse_decl_specifiers(ParseType::MustParse)?;
        let declarator = self.parse_declarator()?;
        self.declarator_to_param(specifiers, declarator)
    }

    pub(super) fn parse_var_decl_stmt(
        &mut self,
        parse_type: ParseType,
    ) -> Result<VarDeclStmt, ParseProcessError> {
        self.parse_var_decl_stmt_inner(parse_type, true)
    }

    pub(super) fn parse_var_decl_stmt_inner(
        &mut self,
        parse_type: ParseType,
        consume_semicolon: bool,
    ) -> Result<VarDeclStmt, ParseProcessError> {
        let specifiers = self.parse_decl_specifiers(parse_type)?;
        let declarator = self.parse_declarator()?;
        self.finish_var_decl_stmt(specifiers, declarator, consume_semicolon, false)
    }

    fn finish_func_def_stmt(
        &mut self,
        specifiers: DeclSpecifiers,
        declarator: Declarator,
    ) -> Result<FuncDeclStmt, ParseProcessError> {
        let Declarator {
            name,
            name_span,
            suffixes,
        } = declarator;
        let params = self.take_function_params(name_span, suffixes)?;
        let body = self.parse_block_stmt(ParseType::MustParse)?;
        Ok(FuncDeclStmt {
            return_type: specifiers.type_specifier.into(),
            storage_class: specifiers.storage_class,
            name,
            params,
            body,
            ret_type_span: specifiers.type_span,
            name_span,
        })
    }

    fn finish_var_decl_stmt(
        &mut self,
        specifiers: DeclSpecifiers,
        first_declarator: Declarator,
        consume_semicolon: bool,
        _allow_empty_declarator_list: bool,
    ) -> Result<VarDeclStmt, ParseProcessError> {
        let mut values = vec![];

        let initializer = self.parse_initializer()?;
        values.push(self.init_declarator_to_var_value(InitDeclarator {
            declarator: first_declarator,
            initializer,
        })?);

        while self.peek().value == TokenValue::Comma {
            self.advance(1);
            let declarator = self.parse_declarator()?;
            let initializer = self.parse_initializer()?;
            values.push(self.init_declarator_to_var_value(InitDeclarator {
                declarator,
                initializer,
            })?);
        }

        if consume_semicolon {
            self.must_match_token(&TokenValue::Semicolon, "`;`")?;
        }

        Ok(VarDeclStmt {
            storage_class: specifiers.storage_class,
            values,
            type_span: specifiers.type_span,
            data_type: specifiers.type_specifier.into(),
        })
    }

    fn parse_initializer(&mut self) -> Result<Option<Expr>, ParseProcessError> {
        if self.peek().value != TokenValue::Equal {
            return Ok(None);
        }
        self.advance(1);
        Ok(Some(self.parse_expr()?))
    }

    fn init_declarator_to_var_value(
        &mut self,
        init_declarator: InitDeclarator,
    ) -> Result<VarDeclStmtValue, ParseProcessError> {
        let Declarator {
            name,
            name_span,
            suffixes,
        } = init_declarator.declarator;
        let dimensions = self.take_array_dimensions(suffixes)?;
        Ok(VarDeclStmtValue {
            name,
            name_span,
            dimensions,
            value: init_declarator.initializer,
        })
    }

    fn declarator_to_param(
        &mut self,
        specifiers: DeclSpecifiers,
        declarator: Declarator,
    ) -> Result<Param, ParseProcessError> {
        let Declarator {
            name,
            name_span,
            suffixes,
        } = declarator;
        let dimensions = self.take_array_dimensions(suffixes)?;
        Ok(Param {
            param_type: specifiers.type_specifier.into(),
            name,
            dimensions,
            name_span,
            type_span: specifiers.type_span,
        })
    }

    fn take_array_dimensions(
        &mut self,
        suffixes: Vec<DeclaratorSuffix>,
    ) -> Result<Vec<ArrayDimension>, ParseProcessError> {
        let mut dimensions = vec![];
        for suffix in suffixes {
            match suffix {
                DeclaratorSuffix::Array(dimension) => dimensions.push(dimension),
                DeclaratorSuffix::Function { span, .. } => {
                    self.diagnostics.add_from_frontend_error(
                        ParseError::ExpectedBefore(TokenValue::LParen, "variable declarator"),
                        span,
                    );
                    return Err(ParseProcessError::ErrorInMatch);
                }
            }
        }
        Ok(dimensions)
    }

    fn take_function_params(
        &mut self,
        name_span: Span,
        suffixes: Vec<DeclaratorSuffix>,
    ) -> Result<Vec<Param>, ParseProcessError> {
        let mut params = None;
        for suffix in suffixes {
            match suffix {
                DeclaratorSuffix::Function {
                    params: suffix_params,
                    ..
                } if params.is_none() => params = Some(suffix_params),
                DeclaratorSuffix::Function { span, .. } => {
                    self.diagnostics.add_from_frontend_error(
                        ParseError::ExpectedBefore(TokenValue::LParen, "function body"),
                        span,
                    );
                    return Err(ParseProcessError::ErrorInMatch);
                }
                DeclaratorSuffix::Array(dimension) => {
                    self.diagnostics.add_from_frontend_error(
                        ParseError::ExpectedBefore(TokenValue::LBracket, "function declarator"),
                        dimension.span,
                    );
                    return Err(ParseProcessError::ErrorInMatch);
                }
            }
        }

        match params {
            Some(params) => Ok(params),
            None => {
                self.diagnostics.add_from_frontend_error(
                    ParseError::ExpectedBefore(TokenValue::LBrace, "function declarator"),
                    name_span,
                );
                Err(ParseProcessError::ErrorInMatch)
            }
        }
    }
}
