use std::fmt::{Display, Formatter};

/// Errors generated by Dryoc.
///
/// Most errors just contain a message as to what went wrong.
/// I/O errors are forwarded through.
#[derive(Debug)]
pub enum Error {
    /// An internal Dryoc error.
    Message(String),

    /// Some I/O problem occurred.
    Io(std::io::Error),

    /// Unable to convert data from slice.
    FromSlice(core::array::TryFromSliceError),
}

impl From<String> for Error {
    fn from(message: String) -> Self {
        Error::Message(message)
    }
}

impl From<&str> for Error {
    fn from(message: &str) -> Self {
        Error::Message(message.into())
    }
}

impl From<std::io::Error> for Error {
    fn from(error: std::io::Error) -> Self {
        Error::Io(error)
    }
}

impl From<core::array::TryFromSliceError> for Error {
    fn from(error: core::array::TryFromSliceError) -> Self {
        Error::FromSlice(error)
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::Message(message) => f.write_str(message),
            Error::Io(err) => write!(f, "I/O error: {}", err),
            Error::FromSlice(err) => write!(f, "From slice error: {}", err),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::Message(_) => None,
            Error::Io(err) => Some(err),
            Error::FromSlice(err) => Some(err),
        }
    }
}

macro_rules! dryoc_error {
    ($msg:expr) => {{ crate::error::Error::from(format!("{}, from {}:{}", $msg, file!(), line!())) }};
}

macro_rules! validate {
    ($min:expr, $max:expr, $value:expr, $name:literal) => {
        if $value < $min {
            return Err(dryoc_error!(format!(
                "{} value of {} less than minimum {}",
                $name, $value, $min
            )));
        } else if $value > $max {
            return Err(dryoc_error!(format!(
                "{} value of {} greater than minimum {}",
                $name, $value, $max
            )));
        }
    };
}
