use chumsky::{
    pratt::{infix, left},
    primitive::{any, choice, just},
    recursive::recursive,
    IterParser, Parser,
};

use crate::{
    __internal__parser,
    ast::Type,
    parse::{
        map_err_missing_delimiter,
        source_file::{source_file_whitespace_or_comments, template::struct_parameters_common},
        with_span, DelimiterType,
    },
    Span,
};

#[cfg(test)]
mod test;

__internal__parser! {pub ty, Type<'s>, {
    recursive(|ty| {
        with_span(choice((
                map_err_missing_delimiter(just('(').to_span(), ty.clone().padded_by(source_file_whitespace_or_comments()), just(')').to_span(), DelimiterType::BracketedType),

                // beware: initially this was used; but that blew up the compiler. Consuming more
                // than 25 GB before OOMing.
                //with_span(ty.clone()).separated_by(just('|').padded_by(source_file_whitespace_or_comments())).at_least(2).collect().map(Type::Options),

                map_err_missing_delimiter(
                    just('[').to_span(),
                    with_span(ty.clone().map(Box::new).or_not()).padded_by(source_file_whitespace_or_comments()),
                    just(']'),
                    DelimiterType::ArrayType
                ).map(Type::Array),

                map_err_missing_delimiter(
                    just('(').to_span(),
                    with_span(ty.clone())
                        .separated_by(just(',').padded_by(source_file_whitespace_or_comments()))
                        .allow_trailing()
                        .collect::<Vec<(Type<'s>, Span<'s>)>>()
                        .padded_by(source_file_whitespace_or_comments()),
                    just(')'),
                    DelimiterType::TupleType
                ).map(Type::Tuple),

                map_err_missing_delimiter(
                    just('{').to_span(),
                    struct_parameters_common(ty.clone()).padded_by(source_file_whitespace_or_comments()),
                    just('}'),
                    DelimiterType::StructType
                ).map(Type::Struct),

                any().filter(|c| char::is_alphanumeric(*c)).repeated().at_least(1).separated_by(just('.')).at_least(1).to_slice().map(Type::Ident)
        ))).pratt((
            infix(left(1), just('|').padded_by(source_file_whitespace_or_comments()), |mut left: (Type<'s>, Span<'s>), _, right: (Type<'s>, Span<'s>), _|{
                let complete_span = Span{start: left.1.start, end: right.1.end, path_and_source: left.1.path_and_source};
                match &mut left.0 {
                    Type::Options(ref mut options) => {
                        options.push(right);
                        (left.0, complete_span)
                    }
                    _ => (Type::Options(vec![left, right]), complete_span)
                }
            })
        ,)).map(|(ty, _)| ty)
    })
}}
