use core::fmt::{self, Display, Formatter};
use core::str::FromStr;
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(
    Clone,
    Copy,
    PartialEq,
    Eq,
    Debug,
    Hash,
    Error,
    RkyvSerialize,
    RkyvDeserialize,
    Archive,
    rkyv::CheckBytes,
)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[repr(u32)]
#[archive(as = "Self")]
pub enum TrapCode {
    StackOverflow = 0,
    HeapAccessOutOfBounds = 1,
    HeapMisaligned = 2,
    TableAccessOutOfBounds = 3,
    IndirectCallToNull = 4,
    BadSignature = 5,
    IntegerOverflow = 6,
    IntegerDivisionByZero = 7,
    BadConversionToInteger = 8,
    UnreachableCodeReached = 9,
    UnalignedAtomic = 10,
}
impl TrapCode {
    pub fn message(&self) -> &str {
        match self {
            Self::StackOverflow => "call stack exhausted",
            Self::HeapAccessOutOfBounds => "out of bounds memory access",
            Self::HeapMisaligned => "misaligned heap",
            Self::TableAccessOutOfBounds => "undefined element: out of bounds table access",
            Self::IndirectCallToNull => "uninitialized element",
            Self::BadSignature => "indirect call type mismatch",
            Self::IntegerOverflow => "integer overflow",
            Self::IntegerDivisionByZero => "integer divide by zero",
            Self::BadConversionToInteger => "invalid conversion to integer",
            Self::UnreachableCodeReached => "unreachable",
            Self::UnalignedAtomic => "unaligned atomic access",
        }
    }
}
impl Display for TrapCode {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let identifier = match *self {
            Self::StackOverflow => "stk_ovf",
            Self::HeapAccessOutOfBounds => "heap_get_oob",
            Self::HeapMisaligned => "heap_misaligned",
            Self::TableAccessOutOfBounds => "table_get_oob",
            Self::IndirectCallToNull => "icall_null",
            Self::BadSignature => "bad_sig",
            Self::IntegerOverflow => "int_ovf",
            Self::IntegerDivisionByZero => "int_divz",
            Self::BadConversionToInteger => "bad_toint",
            Self::UnreachableCodeReached => "unreachable",
            Self::UnalignedAtomic => "unalign_atom",
        };
        f.write_str(identifier)
    }
}
impl FromStr for TrapCode {
    type Err = ();
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "stk_ovf" => Ok(Self::StackOverflow),
            "heap_get_oob" => Ok(Self::HeapAccessOutOfBounds),
            "heap_misaligned" => Ok(Self::HeapMisaligned),
            "table_get_oob" => Ok(Self::TableAccessOutOfBounds),
            "icall_null" => Ok(Self::IndirectCallToNull),
            "bad_sig" => Ok(Self::BadSignature),
            "int_ovf" => Ok(Self::IntegerOverflow),
            "int_divz" => Ok(Self::IntegerDivisionByZero),
            "bad_toint" => Ok(Self::BadConversionToInteger),
            "unreachable" => Ok(Self::UnreachableCodeReached),
            "unalign_atom" => Ok(Self::UnalignedAtomic),
            _ => Err(()),
        }
    }
}
#[derive(Debug)]
pub enum OnCalledAction {
    InvokeAgain,
    Finish,
    Trap(Box<dyn std::error::Error + Send + Sync>),
}
#[cfg(test)]
mod tests {
    use super::*;
    const CODES: [TrapCode; 11] = [
        TrapCode::StackOverflow,
        TrapCode::HeapAccessOutOfBounds,
        TrapCode::HeapMisaligned,
        TrapCode::TableAccessOutOfBounds,
        TrapCode::IndirectCallToNull,
        TrapCode::BadSignature,
        TrapCode::IntegerOverflow,
        TrapCode::IntegerDivisionByZero,
        TrapCode::BadConversionToInteger,
        TrapCode::UnreachableCodeReached,
        TrapCode::UnalignedAtomic,
    ];
    #[test]
    fn display() {
        for r in &CODES {
            let tc = *r;
            assert_eq!(tc.to_string().parse(), Ok(tc));
        }
        assert_eq!("bogus".parse::<TrapCode>(), Err(()));
        assert_eq!("user".parse::<TrapCode>(), Err(()));
        assert_eq!("user-1".parse::<TrapCode>(), Err(()));
        assert_eq!("users".parse::<TrapCode>(), Err(()));
    }
}