use nom::{
  AsChar, Emit, IResult, Input, Mode as _, OutputM, OutputMode, PResult, Parser,
  character::complete::multispace0,
  error::{FromExternalError, ParseError},
  sequence::preceded,
};
use nom_language::error::VerboseError;

pub(crate) type VResult<I, O> = IResult<I, O, VerboseError<I>>;

pub(crate) fn ws0<I, E: ParseError<I>, F>(
  f: F,
) -> impl Parser<I, Output = <F as Parser<I>>::Output, Error = E>
where
  F: Parser<I, Error = E>,
  I: Input,
  <I as Input>::Item: AsChar,
{
  preceded(multispace0, f)
}

pub fn map_res_failure<I: Clone, O, E: ParseError<I> + FromExternalError<I, E2>, E2, F, G>(
  parser: F,
  g: G,
) -> impl Parser<I, Output = O, Error = E>
where
  F: Parser<I, Error = E>,
  G: FnMut(<F as Parser<I>>::Output) -> Result<O, E2>,
{
  MapResFailure { f: parser, g }
}

/// Implementation of `Parser::map_res`
pub struct MapResFailure<F, G>
{
  f: F,
  g: G,
}

impl<I, O2, E2, F, G> Parser<I> for MapResFailure<F, G>
where
  I: Clone,
  <F as Parser<I>>::Error: FromExternalError<I, E2>,
  F: Parser<I>,
  G: FnMut(<F as Parser<I>>::Output) -> Result<O2, E2>,
{
  type Output = O2;
  type Error = <F as Parser<I>>::Error;

  fn process<OM: OutputMode>(&mut self, i: I) -> PResult<OM, I, Self::Output, Self::Error>
  {
    let (input, o1) = self
      .f
      .process::<OutputM<Emit, OM::Error, OM::Incomplete>>(i.clone())?;

    match (self.g)(o1)
    {
      Ok(o2) => Ok((input, OM::Output::bind(|| o2))),
      Err(e) => Err(nom::Err::Failure(
        <F as Parser<I>>::Error::from_external_error(i, nom::error::ErrorKind::MapRes, e),
      )),
    }
  }
}
