use std::error::Error;
use std::fmt::{self, Debug, Display, Formatter};
#[cfg(feature = "serde-serialize")]
use serde::{Deserialize, Serialize};
use crate::thread::ThreadError;
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum Never {}
impl Display for Never {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}
impl Error for Never {}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum ImplementationError {
    Unreachable,
    OptionWithUnexpectedNone,
}
impl Display for ImplementationError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            ImplementationError::Unreachable => {
                write!(f, "internal error: entered unreachable code")
            }
            ImplementationError::OptionWithUnexpectedNone => {
                write!(f, "an option contained an unexpected None value")
            }
        }
    }
}
impl Error for ImplementationError {}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum MultiIntegrationError<Error> {
    ZeroIntegration,
    IntegrationError(usize, Error),
}
impl<Error: Display> Display for MultiIntegrationError<Error> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            MultiIntegrationError::ZeroIntegration => write!(f, "no integration steps"),
            MultiIntegrationError::IntegrationError(index, error) => {
                write!(f, "error during integration step {}: {}", index, error)
            }
        }
    }
}
impl<E: Display + Debug + Error + 'static> Error for MultiIntegrationError<E> {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            MultiIntegrationError::ZeroIntegration => None,
            MultiIntegrationError::IntegrationError(_, error) => Some(error),
        }
    }
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Copy, Eq)]
pub enum StateInitializationError {
    InvalidParameterNormalDistribution(rand_distr::NormalError),
    IncompatibleSize,
    LatticeInitializationError(LatticeInitializationError),
    GaussProjectionError,
}
impl From<rand_distr::NormalError> for StateInitializationError {
    fn from(err: rand_distr::NormalError) -> Self {
        Self::InvalidParameterNormalDistribution(err)
    }
}
impl From<LatticeInitializationError> for StateInitializationError {
    fn from(err: LatticeInitializationError) -> Self {
        Self::LatticeInitializationError(err)
    }
}
impl Display for StateInitializationError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidParameterNormalDistribution(error) => {
                write!(f, "normal distribution error: {}", error)
            }
            Self::IncompatibleSize => write!(f, "size of lattice and data are incompatible"),
            Self::LatticeInitializationError(err) => {
                write!(f, "lattice Initialization error: {}", err)
            }
            Self::GaussProjectionError => write!(f, "gauss projection could not finish"),
        }
    }
}
impl Error for StateInitializationError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            Self::InvalidParameterNormalDistribution(error) => Some(error),
            Self::IncompatibleSize => None,
            Self::LatticeInitializationError(err) => Some(err),
            Self::GaussProjectionError => None,
        }
    }
}
#[non_exhaustive]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ThreadedStateInitializationError {
    ThreadingError(ThreadError),
    StateInitializationError(StateInitializationError),
}
impl From<ThreadError> for ThreadedStateInitializationError {
    fn from(err: ThreadError) -> Self {
        Self::ThreadingError(err)
    }
}
impl From<StateInitializationError> for ThreadedStateInitializationError {
    fn from(err: StateInitializationError) -> Self {
        Self::StateInitializationError(err)
    }
}
impl Display for ThreadedStateInitializationError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::ThreadingError(error) => write!(f, "thread error: {}", error),
            Self::StateInitializationError(error) => write!(f, "{}", error),
        }
    }
}
impl Error for ThreadedStateInitializationError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            Self::ThreadingError(error) => Some(error),
            Self::StateInitializationError(error) => Some(error),
        }
    }
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum LatticeInitializationError {
    NonPositiveSize,
    DimTooSmall,
    ZeroDimension,
}
impl Display for LatticeInitializationError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::NonPositiveSize => write!(
                f,
                "`size` must be strictly greater than 0 and be a finite number"
            ),
            Self::DimTooSmall => write!(f, "`dim` must be greater or equal to 2"),
            Self::ZeroDimension => write!(f, "the dimension parameter `D = 0` is not valid"),
        }
    }
}
impl Error for LatticeInitializationError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct ErrorWithOwnedValue<Error, State> {
    error: Error,
    owned: State,
}
impl<Error, State> ErrorWithOwnedValue<Error, State> {
    getter!(
        pub const error() -> Error
    );
    getter!(
        pub const owned() -> State
    );
    pub const fn new(error: Error, owned: State) -> Self {
        Self { error, owned }
    }
    #[allow(clippy::missing_const_for_fn)] pub fn deconstruct(self) -> (Error, State) {
        (self.error, self.owned)
    }
    #[allow(clippy::missing_const_for_fn)] pub fn error_owned(self) -> Error {
        self.error
    }
}
impl<Error, State> From<(Error, State)> for ErrorWithOwnedValue<Error, State> {
    fn from(data: (Error, State)) -> Self {
        Self::new(data.0, data.1)
    }
}
impl<Error: Display, State: Display> Display for ErrorWithOwnedValue<Error, State> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "error {} with data {}", self.error, self.owned)
    }
}
impl<E: Display + Error + Debug + 'static, State: Display + Debug> Error
    for ErrorWithOwnedValue<E, State>
{
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        Some(&self.error)
    }
}
impl<State> From<ErrorWithOwnedValue<StateInitializationError, State>>
    for StateInitializationError
{
    fn from(data: ErrorWithOwnedValue<StateInitializationError, State>) -> Self {
        data.error
    }
}
impl<Error, Data1, Data2> From<ErrorWithOwnedValue<Error, (Data1, Data2)>>
    for ErrorWithOwnedValue<Error, Data1>
{
    fn from(data: ErrorWithOwnedValue<Error, (Data1, Data2)>) -> Self {
        Self::new(data.error, data.owned.0)
    }
}
#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn implementation_error() {
        assert_eq!(
            ImplementationError::Unreachable.to_string(),
            "internal error: entered unreachable code"
        );
        assert_eq!(
            ImplementationError::OptionWithUnexpectedNone.to_string(),
            "an option contained an unexpected None value"
        );
    }
    #[test]
    fn multi_integration_error() {
        let e1 = MultiIntegrationError::<LatticeInitializationError>::ZeroIntegration;
        assert_eq!(e1.to_string(), "no integration steps");
        let index = 2;
        let error = LatticeInitializationError::DimTooSmall;
        let e2 = MultiIntegrationError::IntegrationError(index, error);
        assert_eq!(
            e2.to_string(),
            format!("error during integration step {}: {}", index, error)
        );
        assert!(e1.source().is_none());
        assert!(e2.source().is_some());
    }
    #[allow(clippy::missing_const_for_fn)] #[test]
    fn never_size() {
        assert_eq!(std::mem::size_of::<Never>(), 0);
    }
}