// Copyright Rob Gage 2025

mod choice;
mod end;
mod first_match;
mod iterated;
mod mapped;
mod nothing;
mod optional;
mod recoverable;
mod sequenced;

use crate::{
    Check,
    Input,
    Mode,
    Parse,
    ModeResult,
};

use choice::Choice;
use end::End;
use first_match::FirstMatch;
use iterated::Iterated;
use mapped::{
    OutputMapped,
    ErrorMapped,
    MessagesMapped,
};
use nothing::Nothing;
use optional::Optional;
use recoverable::Recoverable;
use sequenced::Sequenced;


/// Implementors can be parsed from an input type
pub trait Parser<'a, I>
where
    Self: Sized,
    I: Input<'a>
{

    // PARSER IMPLEMENTATION

    /// The output type that is parsed by this parser
    type Output: Sized;

    /// The type for errors that can occur with this parser
    type Error: Sized;

    /// The type for messages that may be returned by this parser
    type Message: Sized;

    /// Applies a parser
    fn apply<_Mode: Mode>(
        &self,
        input: &'a I
    ) -> ModeResult<Self::Output, Self::Error, Self::Message, _Mode>;

    /// Checks that the input matches this parser, and consumes matched input
    fn check(
        &self,
        input: &'a I,
    ) -> ModeResult<Self::Output, Self::Error, Self::Message, Check>
    { self.apply::<Check>(input) }

    /// Checks that the input matches this parser, and consumes matched input
    fn parse(
        &self,
        input: &'a I,
    ) -> ModeResult<Self::Output, Self::Error, Self::Message, Parse>
    { self.apply::<Parse>(input) }

    // COMBINATOR METHODS

    /// If necessary, runs a fallback parser to recover from the failure of the original parser
    /// while preserving all messages from the original parser
    fn catch<P>(
        self,
        fallback: P
    ) -> impl Parser<'a, I, Output = Self::Output, Error = Self::Error, Message = Self::Message>
    where
        P: Parser<'a, I, Output = Self::Output, Error = Self::Error, Message = Self::Message>
    { Recoverable { fallback, parser: self } }

    /// Ignores this parser's result and then applies another
    fn ignore_then<P, O>(
        self,
        next: P
    ) -> impl Parser<'a, I, Output = O, Error = Self::Error, Message = Self::Message>
    where
        P: Parser<'a, I, Output = O, Error = Self::Error, Message = Self::Message>
    { preceded(self, next) }

    /// Tries another parser if this one fails
    fn or<P>(
        self,
        alternate: P
    ) -> impl Parser<'a, I, Output = Self::Output, Error = Self::Error, Message = Self::Message>
    where
        P: Parser<'a, I, Output = Self::Output, Error = Self::Error, Message = Self::Message>
    {  Choice { primary: self, alternate } }

    /// Applies a parser optionally, returning `None` instead of an error if it fails
    fn or_not(
        self
    ) -> impl Parser<'a, I, Output = Option<Self::Output>, Error = Self::Error, Message = Self::Message>
    { optional(self) }

    /// Maps a parser's output to another type using a function
    fn map<O>(
        self,
        f: impl Fn(Self::Output) -> O + Clone
    ) -> impl Parser<'a, I, Output = O, Error = Self::Error, Message = Self::Message> {
        OutputMapped { parser: self, function: f }
    }

    /// Maps a parser's error to another type using a function
    fn map_error<E>(
        self,
        f: impl Fn(Self::Error) -> E + Clone
    ) -> impl Parser<'a, I, Output = Self::Output, Error = E, Message = Self::Message> {
        ErrorMapped { parser: self, function: f }
    }

    /// Maps a parser's messages to another type using a function
    fn map_messages<M>(
        self,
        f: impl Fn(Self::Message) -> M + Clone
    ) -> impl Parser<'a, I, Output = Self::Output, Error = Self::Error, Message = M> {
        MessagesMapped { parser: self, function: f }
    }

    /// Applies another parser in sequence after this one, and returns both results as a tuple
    fn then<P, O>(
        self,
        next: P
    ) -> impl Parser<'a, I, Output = (Self::Output, O), Error = Self::Error, Message = Self::Message>
    where
        P: Parser<'a, I, Output = O, Error = Self::Error, Message = Self::Message>
    { Sequenced { head: self, tail: next } }

    /// Applies another parser in sequence after this one, but ignores its result
    fn then_ignore<P, O>(
        self,
        next: P
    ) -> impl Parser<'a, I, Output = Self::Output, Error = Self::Error, Message = Self::Message>
    where
        P: Parser<'a, I, Output = O, Error = Self::Error, Message = Self::Message>
    { terminated(self, next) }

}


impl<'a, O, E, M, F, I> Parser<'a, I> for F
where
    F: Fn(&'a I) -> ModeResult<O, E, M, Parse>,
    I: Input<'a> +'a
{

    type Output = O;

    type Error = E;

    type Message = M;

    fn apply<_Mode: Mode>(&self, input: &'a I) -> ModeResult<O, E, M, _Mode> {
        _Mode::apply_parser(self, input)
    }

    fn check(&self, input: &'a I) -> ModeResult<O, E, M, Check> {
        if (self)(input).is_success() {
            ModeResult::Success((), ())
        } else { ModeResult::Failure((), ()) }
    }

    fn parse(&self, input: &'a I) -> ModeResult<O, E, M, Parse> { self(input) }

}

/// Applies a parser preceded by an ignored prefix parser, and followed by an ignored terminator
/// parser
pub fn delimited<'a, O1, O2, O3, E, M, I, P1, P2, P3>(
    prefix: P1,
    parser: P2,
    terminator: P3,
) -> impl Parser<'a, I, Error = E, Output = O2, Message = M>
where
    I: Input<'a>,
    P1: Parser<'a, I, Output = O1, Error = E, Message = M>,
    P2: Parser<'a, I, Output = O2, Error = E, Message = M>,
    P3: Parser<'a, I, Output = O3, Error = E, Message = M>,
{ preceded(prefix, terminated(parser, terminator)) }

/// Matches the end of the provided input
pub const fn end<'a, I>() -> impl Parser<'a, I, Output = (), Error = (), Message = ()>
where
    I: Input<'a>,
{ End }

/// Parses absolutely nothing
pub const fn nothing<'a, E, M, I>() -> impl Parser<'a, I, Output = (), Error = E, Message = M>
where
    I: Input<'a>,
{ Nothing::new() }

/// Iterates application of a parser
pub const fn many<'a, O, E, M, I, P>(
    parser: P,
) -> impl Parser<'a, I, Output = Vec<O>, Error = E, Message = M> where
    I: Input<'a>,
    P: Parser<'a, I, Output = O, Error = E, Message = M>,
{ separated(parser, nothing()) }

/// Optionally applies a parser, converting a failure into `Option::None`
pub const fn optional<'a, O, E, M, I, P>(
    parser: P,
) -> impl Parser<'a, I, Output = Option<O>, Error = E, Message = M> where
    I: Input<'a>,
    P: Parser<'a, I, Output = O, Error = E, Message = M>,
{ Optional (parser) }

/// Consumes input until a parser can be applied successfully or there is no input left
pub const fn seek<'a, O, E, M, I, P>(
    parser: P,
) -> impl Parser<'a, I, Output = Option<O>, Error = E, Message = M> where
    I: Input<'a>,
    P: Parser<'a, I, Output = O, Error = E, Message = M>,
{ FirstMatch (parser) }

/// Applies a parser after an ignored prefix parser
pub const fn preceded<'a, O1, O2, E,  M, I, P1, P2>(
    prefix: P1,
    parser: P2
) -> impl Parser<'a, I, Output = O2, Error = E, Message = M> where
    I: Input<'a>,
    P1: Parser<'a, I, Output = O1, Error = E, Message = M>,
    P2: Parser<'a, I, Output = O2, Error = E, Message = M>,
{ OutputMapped {
    parser: Sequenced { head: prefix, tail: parser },
    function: |(_, output)| output
} }

/// Iterates application of a parser separated by application of another parser
pub const fn separated<'a, E, I, M, O1, O2, P1, P2>(
    parser: P1,
    separator: P2
) -> impl Parser<'a, I, Output = Vec<O1>, Error = E, Message = M> where
    I: Input<'a>,
    P1: Parser<'a, I, Output = O1, Error = E, Message = M>,
    P2: Parser<'a, I, Output = O2, Error = E, Message = M>,
{ Iterated { maximum: None, minimum: 0, parser, separator } }

/// Applies a parser followed by an ignored terminator parser
pub const fn terminated<'a, O1, O2, E, M, I, P1, P2>(
    parser: P1,
    terminator: P2
) -> impl Parser<'a, I, Output = O1, Error = E, Message = M> where
    I: Input<'a>,
    P1: Parser<'a, I, Output = O1, Error = E, Message = M>,
    P2: Parser<'a, I, Output = O2, Error = E, Message = M>,
{
    OutputMapped {
        parser: Sequenced { head: parser, tail: terminator },
        function: |(output, _)| output
    }
}