use crate::input::{Show, ParserInfo};

pub use crate::expected::Expected;

#[derive(Debug, Clone)]
pub struct ParseError<C, E> {
    pub error: E,
    pub info: ErrorInfo<C>,
    pub stack: Vec<ErrorInfo<C>>,
}

#[derive(Debug, Clone)]
pub struct ErrorInfo<C> {
    pub parser: ParserInfo,
    pub context: C,
}

impl<C> ErrorInfo<C> {
    pub fn new(parser: ParserInfo, context: C) -> Self {
        Self { parser, context }
    }
}

impl<C, E> ParseError<C, E> {
    pub fn new(parser: ParserInfo, error: E, context: C) -> ParseError<C, E> {
        ParseError { error, info: ErrorInfo::new(parser, context), stack: vec![] }
    }

    pub fn push_info(&mut self, parser: ParserInfo, context: C) {
        self.stack.push(ErrorInfo::new(parser, context));
    }

    #[inline(always)]
    pub fn into<E2: From<E>>(self) -> ParseError<C, E2> {
        ParseError {
            error: self.error.into(),
            info: self.info,
            stack: self.stack,
        }
    }
}

impl<C: Show, E: std::fmt::Display> std::fmt::Display for ParseError<C, E> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        #[cfg(feature = "color")] yansi::disable();
        write!(f, "{} ({})", self.error, &self.info.context as &dyn Show)?;
        #[cfg(feature = "color")] yansi::whenever(yansi::Condition::DEFAULT);

        for info in &self.stack {
            write!(f, "\n + {}", info.parser.name)?;
            write!(f, " {}", &info.context as &dyn Show)?;
        }

        Ok(())
    }
}
