/*
    Appellation: error <module>
    Contrib: @FL03
*/
#[cfg(feature = "alloc")]
use alloc::{
    boxed::Box,
    string::{String, ToString},
};

/// a type alias for a [`Result`](core::result::Result) equipped with the [`ActorError`] type
pub(crate) type Result<T = ()> = core::result::Result<T, ActorError>;

#[derive(Debug, thiserror::Error)]
pub enum ActorError {
    #[cfg(feature = "alloc")]
    #[error("Driver Error: {0}")]
    DriverError(String),
    #[error("Generative Error: {0}")]
    GenerativeError(String),
    #[error("Invalid Critical Point")]
    InvalidCriticalPoint,
    #[error("Actor is in an infinite loop")]
    InfiniteLoop,
    #[cfg(feature = "alloc")]
    #[error("Invalid Shape: {0}")]
    InvalidShape(String),
    #[cfg(feature = "alloc")]
    #[error("Invalid Parameter: {0}")]
    InvalidParameter(String),
    #[error("Actor is in an invalid state")]
    InvalidState,
    #[cfg(feature = "alloc")]
    #[error("Not Found: {0}")]
    NotFound(String),
    #[cfg(feature = "alloc")]
    #[error("No path found between {src} and {tgt}")]
    NoPathFound { src: String, tgt: String },
    #[error("No rule found")]
    NoRuleFound,
    #[error("{0} is not initialized")]
    NotInitialized(String),
    #[error("Transformation error")]
    TransformationError,
    #[error("Actor is in an unknown state")]
    UnknownState,
    #[error("Unknown Symbol")]
    UnknownSymbol,
    #[error(transparent)]
    CoreError(#[from] eryon::Error),
    #[error(transparent)]
    MemoryError(#[from] eryon_mem::MemoryError),
    #[error(transparent)]
    SurfaceError(#[from] eryon_surface::SurfaceError),
    #[error(transparent)]
    CncError(#[from] cnc::Error),
    #[error(transparent)]
    NeuralError(#[from] cnc::nn::NeuralError),
}

impl ActorError {
    /// returns a new [`NoRuleFound`](ActorError::NoRuleFound) variant
    pub const fn no_rule_found() -> Self {
        ActorError::NoRuleFound
    }
}

#[cfg(feature = "alloc")]
impl ActorError {
    /// returns a new [`NotFound`](ActorError::NotFound) variant
    pub fn not_found(what: impl ToString) -> Self {
        ActorError::NotFound(what.to_string())
    }
    /// returns a new [`NotInitialized`](ActorError::NotInitialized) variant
    pub fn not_initialized(what: impl ToString) -> Self {
        ActorError::NotInitialized(what.to_string())
    }
    /// returns a new [`InvalidParameter`](ActorError::InvalidParameter) variant
    pub fn invalid_parameter(what: impl ToString) -> Self {
        ActorError::InvalidParameter(what.to_string())
    }
    /// returns a new [`NoPathFound`](ActorError::NoPathFound) variant
    pub fn no_path_found(src: impl ToString, tgt: impl ToString) -> Self {
        ActorError::NoPathFound {
            src: src.to_string(),
            tgt: tgt.to_string(),
        }
    }
}

impl From<ActorError> for eryon::Error {
    fn from(err: ActorError) -> Self {
        match err {
            ActorError::CoreError(e) => e,
            _ => eryon::Error::BoxError(Box::new(err)),
        }
    }
}
#[cfg(feature = "alloc")]
impl From<&str> for ActorError {
    fn from(err: &str) -> Self {
        eryon::Error::from(err).into()
    }
}
#[cfg(feature = "alloc")]
impl From<String> for ActorError {
    fn from(err: String) -> Self {
        eryon::Error::from(err).into()
    }
}
