use crate::indexes::{FunctionIndex, GlobalIndex};
use crate::lib::std::borrow::ToOwned;
use crate::lib::std::fmt;
use crate::lib::std::format;
use crate::lib::std::string::{String, ToString};
use crate::lib::std::vec::Vec;
use crate::units::Pages;
use bytecheck::CheckBytes;
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes)]
#[archive(as = "Self")]
#[repr(u8)]
pub enum Type {
    I32,
    I64,
    F32,
    F64,
    V128,
    ExternRef, FuncRef,
}
impl Type {
    pub fn is_num(self) -> bool {
        matches!(
            self,
            Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128
        )
    }
    pub fn is_ref(self) -> bool {
        matches!(self, Self::ExternRef | Self::FuncRef)
    }
}
impl fmt::Display for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, CheckBytes)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
#[archive(as = "Self")]
pub struct V128(pub(crate) [u8; 16]);
impl V128 {
    pub fn bytes(&self) -> &[u8; 16] {
        &self.0
    }
    pub fn iter(&self) -> impl Iterator<Item = &u8> {
        self.0.iter()
    }
    pub fn to_vec(self) -> Vec<u8> {
        self.0.to_vec()
    }
    pub fn as_slice(&self) -> &[u8] {
        &self.0[..]
    }
}
impl From<[u8; 16]> for V128 {
    fn from(array: [u8; 16]) -> Self {
        Self(array)
    }
}
impl From<&[u8]> for V128 {
    fn from(slice: &[u8]) -> Self {
        assert_eq!(slice.len(), 16);
        let mut buffer = [0; 16];
        buffer.copy_from_slice(slice);
        Self(buffer)
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum ExternType {
    Function(FunctionType),
    Global(GlobalType),
    Table(TableType),
    Memory(MemoryType),
}
fn is_global_compatible(exported: GlobalType, imported: GlobalType) -> bool {
    let GlobalType {
        ty: exported_ty,
        mutability: exported_mutability,
    } = exported;
    let GlobalType {
        ty: imported_ty,
        mutability: imported_mutability,
    } = imported;
    exported_ty == imported_ty && imported_mutability == exported_mutability
}
fn is_table_element_type_compatible(exported_type: Type, imported_type: Type) -> bool {
    match exported_type {
        Type::FuncRef => true,
        _ => imported_type == exported_type,
    }
}
fn is_table_compatible(
    exported: &TableType,
    imported: &TableType,
    imported_runtime_size: Option<u32>,
) -> bool {
    let TableType {
        ty: exported_ty,
        minimum: exported_minimum,
        maximum: exported_maximum,
    } = exported;
    let TableType {
        ty: imported_ty,
        minimum: imported_minimum,
        maximum: imported_maximum,
    } = imported;
    is_table_element_type_compatible(*exported_ty, *imported_ty)
        && *imported_minimum <= imported_runtime_size.unwrap_or(*exported_minimum)
        && (imported_maximum.is_none()
            || (!exported_maximum.is_none()
                && imported_maximum.unwrap() >= exported_maximum.unwrap()))
}
fn is_memory_compatible(
    exported: &MemoryType,
    imported: &MemoryType,
    imported_runtime_size: Option<u32>,
) -> bool {
    let MemoryType {
        minimum: exported_minimum,
        maximum: exported_maximum,
        shared: exported_shared,
    } = exported;
    let MemoryType {
        minimum: imported_minimum,
        maximum: imported_maximum,
        shared: imported_shared,
    } = imported;
    imported_minimum.0 <= imported_runtime_size.unwrap_or(exported_minimum.0)
        && (imported_maximum.is_none()
            || (!exported_maximum.is_none()
                && imported_maximum.unwrap() >= exported_maximum.unwrap()))
        && exported_shared == imported_shared
}
macro_rules! accessors {
    ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
        pub fn $get(&self) -> Option<&$ty> {
            if let Self::$variant(e) = self {
                Some(e)
            } else {
                None
            }
        }
        pub fn $unwrap(&self) -> &$ty {
            self.$get().expect(concat!("expected ", stringify!($ty)))
        }
    )*)
}
impl ExternType {
    accessors! {
        (Function(FunctionType) func unwrap_func)
        (Global(GlobalType) global unwrap_global)
        (Table(TableType) table unwrap_table)
        (Memory(MemoryType) memory unwrap_memory)
    }
    pub fn is_compatible_with(&self, other: &Self, runtime_size: Option<u32>) -> bool {
        match (self, other) {
            (Self::Function(a), Self::Function(b)) => a == b,
            (Self::Global(a), Self::Global(b)) => is_global_compatible(*a, *b),
            (Self::Table(a), Self::Table(b)) => is_table_compatible(a, b, runtime_size),
            (Self::Memory(a), Self::Memory(b)) => is_memory_compatible(a, b, runtime_size),
            _ => false,
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
#[archive_attr(derive(CheckBytes, Debug))]
pub struct FunctionType {
    params: Box<[Type]>,
    results: Box<[Type]>,
}
impl FunctionType {
    pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
    where
        Params: Into<Box<[Type]>>,
        Returns: Into<Box<[Type]>>,
    {
        Self {
            params: params.into(),
            results: returns.into(),
        }
    }
    pub fn params(&self) -> &[Type] {
        &self.params
    }
    pub fn results(&self) -> &[Type] {
        &self.results
    }
}
impl fmt::Display for FunctionType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let params = self
            .params
            .iter()
            .map(|p| format!("{:?}", p))
            .collect::<Vec<_>>()
            .join(", ");
        let results = self
            .results
            .iter()
            .map(|p| format!("{:?}", p))
            .collect::<Vec<_>>()
            .join(", ");
        write!(f, "[{}] -> [{}]", params, results)
    }
}
macro_rules! implement_from_pair_to_functiontype {
    ($($N:literal,$M:literal)+) => {
        $(
            impl From<([Type; $N], [Type; $M])> for FunctionType {
                fn from(pair: ([Type; $N], [Type; $M])) -> Self {
                    Self::new(pair.0, pair.1)
                }
            }
        )+
    }
}
implement_from_pair_to_functiontype! {
    0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9
    1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9
    2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9
    3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9
    4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9
    5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9
    6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9
    7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9
    8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9
    9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9
}
impl From<&Self> for FunctionType {
    fn from(as_ref: &Self) -> Self {
        as_ref.clone()
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, CheckBytes)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
#[archive(as = "Self")]
#[repr(u8)]
pub enum Mutability {
    Const,
    Var,
}
impl Mutability {
    pub fn is_mutable(self) -> bool {
        self.into()
    }
}
impl From<bool> for Mutability {
    fn from(value: bool) -> Self {
        if value {
            Self::Var
        } else {
            Self::Const
        }
    }
}
impl From<Mutability> for bool {
    fn from(value: Mutability) -> Self {
        match value {
            Mutability::Var => true,
            Mutability::Const => false,
        }
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, CheckBytes)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
#[archive(as = "Self")]
pub struct GlobalType {
    pub ty: Type,
    pub mutability: Mutability,
}
impl GlobalType {
    pub fn new(ty: Type, mutability: Mutability) -> Self {
        Self { ty, mutability }
    }
}
impl fmt::Display for GlobalType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mutability = match self.mutability {
            Mutability::Const => "constant",
            Mutability::Var => "mutable",
        };
        write!(f, "{} ({})", self.ty, mutability)
    }
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes)]
#[archive(as = "Self")]
#[repr(u8)]
pub enum GlobalInit {
    I32Const(i32),
    I64Const(i64),
    F32Const(f32),
    F64Const(f64),
    V128Const(V128),
    GetGlobal(GlobalIndex),
    RefNullConst,
    RefFunc(FunctionIndex),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
#[archive_attr(derive(CheckBytes, Debug))]
pub struct TableType {
    pub ty: Type,
    pub minimum: u32,
    pub maximum: Option<u32>,
}
impl TableType {
    pub fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self {
        Self {
            ty,
            minimum,
            maximum,
        }
    }
}
impl fmt::Display for TableType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(maximum) = self.maximum {
            write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
        } else {
            write!(f, "{} ({}..)", self.ty, self.minimum)
        }
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
#[archive_attr(derive(CheckBytes, Debug))]
pub struct MemoryType {
    pub minimum: Pages,
    pub maximum: Option<Pages>,
    pub shared: bool,
}
impl MemoryType {
    pub fn new<IntoPages>(minimum: IntoPages, maximum: Option<IntoPages>, shared: bool) -> Self
    where
        IntoPages: Into<Pages>,
    {
        Self {
            minimum: minimum.into(),
            maximum: maximum.map(Into::into),
            shared,
        }
    }
}
impl fmt::Display for MemoryType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let shared = if self.shared { "shared" } else { "not shared" };
        if let Some(maximum) = self.maximum {
            write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
        } else {
            write!(f, "{} ({:?}..)", shared, self.minimum)
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ImportType<T = ExternType> {
    module: String,
    name: String,
    ty: T,
}
impl<T> ImportType<T> {
    pub fn new(module: &str, name: &str, ty: T) -> Self {
        Self {
            module: module.to_owned(),
            name: name.to_owned(),
            ty,
        }
    }
    pub fn module(&self) -> &str {
        &self.module
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn ty(&self) -> &T {
        &self.ty
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ExportType<T = ExternType> {
    name: String,
    ty: T,
}
impl<T> ExportType<T> {
    pub fn new(name: &str, ty: T) -> Self {
        Self {
            name: name.to_string(),
            ty,
        }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn ty(&self) -> &T {
        &self.ty
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
    const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
    const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
    const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);
    #[test]
    fn convert_tuple_to_functiontype() {
        let ty: FunctionType = VOID_TO_VOID.into();
        assert_eq!(ty.params().len(), 0);
        assert_eq!(ty.results().len(), 0);
        let ty: FunctionType = I32_I32_TO_VOID.into();
        assert_eq!(ty.params().len(), 2);
        assert_eq!(ty.params()[0], Type::I32);
        assert_eq!(ty.params()[1], Type::I32);
        assert_eq!(ty.results().len(), 0);
        let ty: FunctionType = V128_I64_TO_I32.into();
        assert_eq!(ty.params().len(), 2);
        assert_eq!(ty.params()[0], Type::V128);
        assert_eq!(ty.params()[1], Type::I64);
        assert_eq!(ty.results().len(), 1);
        assert_eq!(ty.results()[0], Type::I32);
        let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
        assert_eq!(ty.params().len(), 9);
        assert_eq!(ty.results().len(), 9);
    }
}