use crate::coords::Coords;
use crate::lexer::Token;
use std::borrow::Cow;
use std::fmt::{Display, Formatter};
use std::io::BufRead;
pub type ParserResult<T> = Result<T, ParserError>;
#[derive(Debug, Copy, Clone)]
pub enum ParserErrorSource {
    Lexer,
    DomParser,
    SaxParser,
}
impl Display for ParserErrorSource {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            ParserErrorSource::Lexer => write!(f, "lexing"),
            ParserErrorSource::DomParser => write!(f, "DOM parsing"),
            ParserErrorSource::SaxParser => write!(f, "SAX parsing"),
        }
    }
}
#[derive(Debug, Clone, PartialEq)]
pub enum ParserErrorDetails {
    InvalidFile,
    ZeroLengthInput,
    EndOfInput,
    StreamFailure,
    NonUtf8InputDetected,
    UnexpectedToken(Token),
    PairExpected,
    InvalidRootObject,
    InvalidObject,
    InvalidArray,
    InvalidCharacter(char),
    MatchFailed(String, String),
    InvalidNumericRepresentation(String),
    InvalidEscapeSequence(String),
    InvalidUnicodeEscapeSequence(String),
}
impl Display for ParserErrorDetails {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            ParserErrorDetails::InvalidFile => write!(f, "invalid file specified"),
            ParserErrorDetails::ZeroLengthInput => write!(f, "zero length input"),
            ParserErrorDetails::EndOfInput => write!(f, "end of input reached"),
            ParserErrorDetails::StreamFailure => write!(f, "failure in the underlying stream"),
            ParserErrorDetails::NonUtf8InputDetected => write!(f, "non-UTF8 input"),
            ParserErrorDetails::UnexpectedToken(token) => {
                write!(f, "unexpected token found: {}", token)
            }
            ParserErrorDetails::PairExpected => {
                write!(f, "pair expected, something else was found")
            }
            ParserErrorDetails::InvalidRootObject => write!(f, "invalid JSON"),
            ParserErrorDetails::InvalidObject => write!(f, "invalid object"),
            ParserErrorDetails::InvalidArray => write!(f, "invalid array"),
            ParserErrorDetails::InvalidCharacter(ch) => write!(f, "invalid character: \'{}\'", ch),
            ParserErrorDetails::MatchFailed(first, second) => write!(
                f,
                "a match failed. Looking for \"{}\", found \"{}\"",
                first, second
            ),
            ParserErrorDetails::InvalidNumericRepresentation(repr) => {
                write!(f, "invalid number representation: \"{}\"", repr)
            }
            ParserErrorDetails::InvalidEscapeSequence(seq) => {
                write!(f, "invalid escape sequence: \"{}\"", seq)
            }
            ParserErrorDetails::InvalidUnicodeEscapeSequence(seq) => {
                write!(f, "invalid unicode escape sequence: \"{}\"", seq)
            }
        }
    }
}
#[derive(Debug, Clone)]
pub struct ParserError {
    pub source: ParserErrorSource,
    pub details: ParserErrorDetails,
    pub coords: Option<Coords>,
}
impl Display for ParserError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        if self.coords.is_some() {
            write!(
                f,
                "Source: {}, Details: {}, Coords: {}",
                self.source,
                self.details,
                self.coords.unwrap()
            )
        } else {
            write!(f, "Source: {}, Details: {}", self.source, self.details)
        }
    }
}
#[macro_export]
macro_rules! lexer_error {
    ($details: expr, $coords : expr) => {
        Err(ParserError {
            source: ParserErrorSource::Lexer,
            details: $details,
            coords: Some($coords),
        })
    };
    ($details: expr) => {
        Err(ParserError {
            source: ParserErrorSource::Lexer,
            details: $details,
            coords: None,
        })
    };
}
#[macro_export]
macro_rules! dom_parser_error {
    ($details: expr, $coords: expr) => {
        Err(ParserError {
            source: ParserErrorSource::DomParser,
            details: $details,
            coords: Some($coords),
        })
    };
    ($details: expr) => {
        Err(ParserError {
            source: ParserErrorSource::DomParser,
            details: $details,
            coords: None,
        })
    };
}
#[macro_export]
macro_rules! sax_parser_error {
    ($details: expr, $coords: expr) => {
        Err(ParserError {
            source: ParserErrorSource::SaxParser,
            details: $details,
            coords: Some($coords),
        })
    };
    ($details: expr) => {
        Err(ParserError {
            source: ParserErrorSource::SaxParser,
            details: $details,
            coords: None,
        })
    };
}