#[cfg(feature = "codec")]
use enum_iterator::Sequence;
#[cfg(feature = "codec")]
use scale_info::{
    scale::{self, Decode, Encode},
    TypeInfo,
};
#[repr(u8)]
#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Default,
    derive_more::Display,
    derive_more::From,
)]
#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo, Sequence), codec(crate = scale), allow(clippy::unnecessary_cast))]
pub enum ReplyCode {
    #[display(fmt = "Success reply sent due to {_0}")]
    Success(SuccessReplyReason) = 0,
    #[display(fmt = "Error reply sent due to {_0}")]
    Error(ErrorReplyReason) = 1,
    #[default]
    #[display(fmt = "<unsupported reply code>")]
    Unsupported = 255,
}
impl ReplyCode {
    fn discriminant(&self) -> u8 {
        unsafe { *<*const _>::from(self).cast::<u8>() }
    }
    pub fn to_bytes(self) -> [u8; 4] {
        let mut bytes = [self.discriminant(), 0, 0, 0];
        match self {
            Self::Success(reason) => bytes[1..].copy_from_slice(&reason.to_bytes()),
            Self::Error(reason) => bytes[1..].copy_from_slice(&reason.to_bytes()),
            Self::Unsupported => {}
        }
        bytes
    }
    pub fn from_bytes(bytes: [u8; 4]) -> Self {
        match bytes[0] {
            b if Self::Success(Default::default()).discriminant() == b => {
                let reason_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
                Self::Success(SuccessReplyReason::from_bytes(reason_bytes))
            }
            b if Self::Error(Default::default()).discriminant() == b => {
                let reason_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
                Self::Error(ErrorReplyReason::from_bytes(reason_bytes))
            }
            _ => Self::Unsupported,
        }
    }
    pub fn error(reason: impl Into<ErrorReplyReason>) -> Self {
        Self::Error(reason.into())
    }
    pub fn is_success(&self) -> bool {
        matches!(self, Self::Success(_))
    }
    pub fn is_error(&self) -> bool {
        matches!(self, Self::Error(_))
    }
    pub fn is_unsupported(&self) -> bool {
        matches!(self, Self::Unsupported)
    }
}
#[repr(u8)]
#[derive(
    Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, derive_more::Display,
)]
#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo, Sequence), codec(crate = scale), allow(clippy::unnecessary_cast))]
pub enum SuccessReplyReason {
    #[display(fmt = "automatic sending")]
    Auto = 0,
    #[display(fmt = "manual sending")]
    Manual = 1,
    #[default]
    #[display(fmt = "<unsupported reason>")]
    Unsupported = 255,
}
impl SuccessReplyReason {
    fn to_bytes(self) -> [u8; 3] {
        [self as u8, 0, 0]
    }
    fn from_bytes(bytes: [u8; 3]) -> Self {
        match bytes[0] {
            b if Self::Auto as u8 == b => Self::Auto,
            b if Self::Manual as u8 == b => Self::Manual,
            _ => Self::Unsupported,
        }
    }
}
#[repr(u8)]
#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Default,
    derive_more::Display,
    derive_more::From,
)]
#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo, Sequence), codec(crate = scale), allow(clippy::unnecessary_cast))]
pub enum ErrorReplyReason {
    #[display(fmt = "execution error ({_0})")]
    Execution(SimpleExecutionError) = 0,
    #[display(fmt = "fail in program creation ({_0})")]
    FailedToCreateProgram(SimpleProgramCreationError) = 1,
    #[display(fmt = "inactivity of destination program")]
    InactiveProgram = 2,
    #[display(fmt = "removal from waitlist")]
    RemovedFromWaitlist = 3,
    #[default]
    #[display(fmt = "<unsupported reason>")]
    Unsupported = 255,
}
impl ErrorReplyReason {
    fn discriminant(&self) -> u8 {
        unsafe { *<*const _>::from(self).cast::<u8>() }
    }
    fn to_bytes(self) -> [u8; 3] {
        let mut bytes = [self.discriminant(), 0, 0];
        match self {
            Self::Execution(error) => bytes[1..].copy_from_slice(&error.to_bytes()),
            Self::FailedToCreateProgram(error) => bytes[1..].copy_from_slice(&error.to_bytes()),
            Self::InactiveProgram | Self::RemovedFromWaitlist | Self::Unsupported => {}
        }
        bytes
    }
    fn from_bytes(bytes: [u8; 3]) -> Self {
        match bytes[0] {
            b if Self::Execution(Default::default()).discriminant() == b => {
                let err_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
                Self::Execution(SimpleExecutionError::from_bytes(err_bytes))
            }
            b if Self::FailedToCreateProgram(Default::default()).discriminant() == b => {
                let err_bytes = bytes[1..].try_into().unwrap_or_else(|_| unreachable!());
                Self::FailedToCreateProgram(SimpleProgramCreationError::from_bytes(err_bytes))
            }
            b if Self::InactiveProgram.discriminant() == b => Self::InactiveProgram,
            b if Self::RemovedFromWaitlist.discriminant() == b => Self::RemovedFromWaitlist,
            _ => Self::Unsupported,
        }
    }
}
#[repr(u8)]
#[derive(
    Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, derive_more::Display,
)]
#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo, Sequence), codec(crate = scale), allow(clippy::unnecessary_cast))]
pub enum SimpleExecutionError {
    #[display(fmt = "Message ran out of gas")]
    RanOutOfGas = 0,
    #[display(fmt = "Program reached memory limit")]
    MemoryOverflow = 1,
    #[display(fmt = "Message ran into uncatchable error")]
    BackendError = 2,
    #[display(fmt = "Message panicked")]
    UserspacePanic = 3,
    #[display(fmt = "Program called WASM `unreachable` instruction")]
    UnreachableInstruction = 4,
    #[default]
    #[display(fmt = "<unsupported error>")]
    Unsupported = 255,
}
impl SimpleExecutionError {
    fn to_bytes(self) -> [u8; 2] {
        [self as u8, 0]
    }
    fn from_bytes(bytes: [u8; 2]) -> Self {
        match bytes[0] {
            b if Self::RanOutOfGas as u8 == b => Self::RanOutOfGas,
            b if Self::MemoryOverflow as u8 == b => Self::MemoryOverflow,
            b if Self::BackendError as u8 == b => Self::BackendError,
            b if Self::UserspacePanic as u8 == b => Self::UserspacePanic,
            b if Self::UnreachableInstruction as u8 == b => Self::UnreachableInstruction,
            _ => Self::Unsupported,
        }
    }
}
#[repr(u8)]
#[derive(
    Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, derive_more::Display,
)]
#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo, Sequence), codec(crate = scale), allow(clippy::unnecessary_cast))]
pub enum SimpleProgramCreationError {
    #[display(fmt = "Given `CodeId` doesn't exist")]
    CodeNotExists = 0,
    #[default]
    #[display(fmt = "<unsupported error>")]
    Unsupported = 255,
}
impl SimpleProgramCreationError {
    fn to_bytes(self) -> [u8; 2] {
        [self as u8, 0]
    }
    fn from_bytes(bytes: [u8; 2]) -> Self {
        match bytes[0] {
            b if Self::CodeNotExists as u8 == b => Self::CodeNotExists,
            _ => Self::Unsupported,
        }
    }
}
#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Default,
    derive_more::Display,
    derive_more::From,
)]
#[cfg_attr(feature = "codec", derive(Encode, Decode, TypeInfo, Sequence), codec(crate = scale), allow(clippy::unnecessary_cast))]
pub enum SignalCode {
    #[display(fmt = "Signal message sent due to execution error ({_0})")]
    Execution(SimpleExecutionError),
    #[default]
    #[display(fmt = "Signal message sent due to removal from waitlist")]
    RemovedFromWaitlist,
}
impl SignalCode {
    pub const fn to_u32(self) -> u32 {
        match self {
            Self::Execution(SimpleExecutionError::UserspacePanic) => 100,
            Self::Execution(SimpleExecutionError::RanOutOfGas) => 101,
            Self::Execution(SimpleExecutionError::BackendError) => 102,
            Self::Execution(SimpleExecutionError::MemoryOverflow) => 103,
            Self::Execution(SimpleExecutionError::UnreachableInstruction) => 104,
            Self::RemovedFromWaitlist => 200,
            Self::Execution(SimpleExecutionError::Unsupported) => u32::MAX,
        }
    }
    pub const fn from_u32(num: u32) -> Option<Self> {
        let res = match num {
            v if Self::Execution(SimpleExecutionError::UserspacePanic).to_u32() == v => {
                Self::Execution(SimpleExecutionError::UserspacePanic)
            }
            v if Self::Execution(SimpleExecutionError::RanOutOfGas).to_u32() == v => {
                Self::Execution(SimpleExecutionError::RanOutOfGas)
            }
            v if Self::Execution(SimpleExecutionError::BackendError).to_u32() == v => {
                Self::Execution(SimpleExecutionError::BackendError)
            }
            v if Self::Execution(SimpleExecutionError::MemoryOverflow).to_u32() == v => {
                Self::Execution(SimpleExecutionError::MemoryOverflow)
            }
            v if Self::Execution(SimpleExecutionError::UnreachableInstruction).to_u32() == v => {
                Self::Execution(SimpleExecutionError::UnreachableInstruction)
            }
            v if Self::RemovedFromWaitlist.to_u32() == v => Self::RemovedFromWaitlist,
            _ => return None,
        };
        Some(res)
    }
}