use std::borrow::Cow;
use std::fmt::{self, Display, Debug};
use Input;
pub trait AsResult<T, I: Input> {
    fn as_result(self) -> Result<T, I>;
}
impl<T, I: Input> AsResult<T, I> for T {
    fn as_result(self) -> Result<T, I> {
        Ok(self)
    }
}
impl<T, I: Input> AsResult<T, I> for Result<T, I> {
    fn as_result(self) -> Result<T, I> {
        self
    }
}
#[derive(Debug, Clone, PartialEq)]
pub enum Expected<T, I, S> {
    Token(Option<T>, Option<T>),
    Slice(Option<I>, Option<S>),
    Custom(Cow<'static, str>),
    EOF(Option<T>)
}
impl<T, I, S> Expected<T, I, S> {
    pub fn map<FT, FI, FS, OT, OI, OS>(self, f_t: FT, f_i: FI, f_s: FS) -> Expected<OT, OI, OS>
        where FT: Copy + Fn(T) -> OT, FI: Fn(I) -> OI, FS: Fn(S) -> OS
    {
        match self {
            Expected::Token(a, b) => Expected::Token(a.map(f_t), b.map(f_t)),
            Expected::Slice(a, b) => Expected::Slice(a.map(f_i), b.map(f_s)),
            Expected::Custom(msg) => Expected::Custom(msg),
            Expected::EOF(t) => Expected::EOF(t.map(f_t))
        }
    }
}
impl<T: Debug, I: Debug, S: Debug> Display for Expected<T, I, S> {
    default fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Expected::Token(Some(ref expected), Some(ref found)) => {
                write!(f, "expected token {:?} but found {:?}", expected, found)
            }
            Expected::Token(None, Some(ref found)) => {
                write!(f, "unexpected token: {:?}", found)
            }
            Expected::Token(Some(ref expected), None) => {
                write!(f, "expected token {:?} but none was found", expected)
            }
            Expected::Token(None, None) => {
                write!(f, "expected any token but none was found")
            }
            Expected::Slice(Some(ref expected), Some(ref found)) => {
                write!(f, "expected slice {:?} but found {:?}", expected, found)
            }
            Expected::Slice(None, Some(ref found)) => {
                write!(f, "unexpected slice: {:?}", found)
            }
            Expected::Slice(Some(ref expected), None) => {
                write!(f, "expected slice {:?} but none was found", expected)
            }
            Expected::Slice(None, None) => {
                write!(f, "expected any slice but none was found")
            }
            Expected::Custom(ref message) => {
                write!(f, "{}", message)
            }
            Expected::EOF(None) => {
                write!(f, "expected EOF but input remains")
            }
            Expected::EOF(Some(ref token)) => {
                write!(f, "expected EOF but found {:?}", token)
            }
        }
    }
}
impl<T: Display + Debug, I: Display + Debug, S: Display + Debug> Display for Expected<T, I, S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Expected::Token(Some(ref expected), Some(ref found)) => {
                write!(f, "expected token {} but found {}", expected, found)
            }
            Expected::Token(None, Some(ref found)) => {
                write!(f, "unexpected token: {}", found)
            }
            Expected::Token(Some(ref expected), None) => {
                write!(f, "expected token {} but none was found", expected)
            }
            Expected::Token(None, None) => {
                write!(f, "expected any token but none was found")
            }
            Expected::Slice(Some(ref expected), Some(ref found)) => {
                write!(f, "expected slice {} but found {}", expected, found)
            }
            Expected::Slice(None, Some(ref found)) => {
                write!(f, "unexpected slice: {}", found)
            }
            Expected::Slice(Some(ref expected), None) => {
                write!(f, "expected slice {} but none was found", expected)
            }
            Expected::Slice(None, None) => {
                write!(f, "expected any slice but none was found")
            }
            Expected::Custom(ref message) => {
                write!(f, "{}", message)
            }
            Expected::EOF(None) => {
                write!(f, "expected EOF but input remains")
            }
            Expected::EOF(Some(ref token)) => {
                write!(f, "expected EOF but found {}", token)
            }
        }
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct ParseErr<I: Input> {
    pub parser: &'static str,
    pub expected: Expected<I::Token, I::InSlice, I::Slice>,
    pub context: Option<I::Context>,
}
impl<I: Input> ParseErr<I> {
    #[inline(always)]
    pub fn new<T>(parser: &'static str, message: T) -> ParseErr<I>
        where T: Into<Cow<'static, str>>
    {
        ParseErr {
            parser,
            expected: Expected::Custom(message.into()),
            context: None
        }
    }
    #[inline(always)]
    pub fn from_context<T>(input: &mut I, parser: &'static str, message: T) -> ParseErr<I>
        where T: Into<Cow<'static, str>>
    {
        ParseErr {
            parser,
            expected: Expected::Custom(message.into()),
            context: input.context()
        }
    }
}
impl<I: Input> fmt::Display for ParseErr<I>
    where I::Token: Debug + Display,
          I::Slice: Display + Debug,
          I::InSlice: Display + Debug
{
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.expected)?;
        if let Some(ref context) = self.context {
            write!(f, " ({} at {})", self.parser, context)?;
        } else {
            write!(f, " ({})", self.parser)?;
        }
        Ok(())
    }
}
impl<I: Input> fmt::Display for ParseErr<I>
    where I::Token: Debug, I::Slice: Debug, I::InSlice: Debug
{
    #[inline]
    default fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.expected)?;
        if let Some(ref context) = self.context {
            write!(f, " ({} at {})", self.parser, context)?;
        } else {
            write!(f, " ({})", self.parser)?;
        }
        Ok(())
    }
}
pub type Result<R, I> = ::std::result::Result<R, ParseErr<I>>;