use crate::environ::{TargetEnvironment, WasmResult, WasmType};
use crate::wasm_unsupported;
use core::convert::TryInto;
use core::u32;
use cranelift_codegen::entity::entity_impl;
use cranelift_codegen::ir;
use cranelift_codegen::ir::immediates::V128Imm;
use cranelift_frontend::FunctionBuilder;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
use wasmparser::{FuncValidator, WasmFuncType, WasmModuleResources};
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct FuncIndex(u32);
entity_impl!(FuncIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct DefinedFuncIndex(u32);
entity_impl!(DefinedFuncIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct DefinedTableIndex(u32);
entity_impl!(DefinedTableIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct DefinedMemoryIndex(u32);
entity_impl!(DefinedMemoryIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct DefinedGlobalIndex(u32);
entity_impl!(DefinedGlobalIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct TableIndex(u32);
entity_impl!(TableIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct GlobalIndex(u32);
entity_impl!(GlobalIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct MemoryIndex(u32);
entity_impl!(MemoryIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct SignatureIndex(u32);
entity_impl!(SignatureIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct DataIndex(u32);
entity_impl!(DataIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ElemIndex(u32);
entity_impl!(ElemIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct TypeIndex(u32);
entity_impl!(TypeIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ModuleIndex(u32);
entity_impl!(ModuleIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct InstanceIndex(u32);
entity_impl!(InstanceIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct EventIndex(u32);
entity_impl!(EventIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ModuleTypeIndex(u32);
entity_impl!(ModuleTypeIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct InstanceTypeIndex(u32);
entity_impl!(InstanceTypeIndex);
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum EntityIndex {
    
    Function(FuncIndex),
    
    Table(TableIndex),
    
    Memory(MemoryIndex),
    
    Global(GlobalIndex),
    
    Module(ModuleIndex),
    
    Instance(InstanceIndex),
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum EntityType {
    
    Global(Global),
    
    Memory(Memory),
    
    Event(Event),
    
    Table(Table),
    
    
    Function(SignatureIndex),
    
    
    Instance(InstanceTypeIndex),
    
    
    Module(ModuleTypeIndex),
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Global {
    
    pub wasm_ty: crate::WasmType,
    
    pub ty: ir::Type,
    
    pub mutability: bool,
    
    pub initializer: GlobalInit,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum GlobalInit {
    
    I32Const(i32),
    
    I64Const(i64),
    
    F32Const(u32),
    
    F64Const(u64),
    
    V128Const(V128Imm),
    
    GetGlobal(GlobalIndex),
    
    RefNullConst,
    
    RefFunc(FuncIndex),
    
    Import,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Table {
    
    pub wasm_ty: WasmType,
    
    pub ty: TableElementType,
    
    pub minimum: u32,
    
    pub maximum: Option<u32>,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum TableElementType {
    
    Val(ir::Type),
    
    Func,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Memory {
    
    pub minimum: u32,
    
    pub maximum: Option<u32>,
    
    pub shared: bool,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Event {
    
    pub ty: TypeIndex,
}
pub fn type_to_type<PE: TargetEnvironment + ?Sized>(
    ty: wasmparser::Type,
    environ: &PE,
) -> WasmResult<ir::Type> {
    match ty {
        wasmparser::Type::I32 => Ok(ir::types::I32),
        wasmparser::Type::I64 => Ok(ir::types::I64),
        wasmparser::Type::F32 => Ok(ir::types::F32),
        wasmparser::Type::F64 => Ok(ir::types::F64),
        wasmparser::Type::V128 => Ok(ir::types::I8X16),
        wasmparser::Type::ExternRef | wasmparser::Type::FuncRef => {
            Ok(environ.reference_type(ty.try_into()?))
        }
        ty => Err(wasm_unsupported!("type_to_type: wasm type {:?}", ty)),
    }
}
pub fn tabletype_to_type<PE: TargetEnvironment + ?Sized>(
    ty: wasmparser::Type,
    environ: &PE,
) -> WasmResult<Option<ir::Type>> {
    match ty {
        wasmparser::Type::I32 => Ok(Some(ir::types::I32)),
        wasmparser::Type::I64 => Ok(Some(ir::types::I64)),
        wasmparser::Type::F32 => Ok(Some(ir::types::F32)),
        wasmparser::Type::F64 => Ok(Some(ir::types::F64)),
        wasmparser::Type::V128 => Ok(Some(ir::types::I8X16)),
        wasmparser::Type::ExternRef => Ok(Some(environ.reference_type(ty.try_into()?))),
        wasmparser::Type::FuncRef => Ok(None),
        ty => Err(wasm_unsupported!(
            "tabletype_to_type: table wasm type {:?}",
            ty
        )),
    }
}
pub fn blocktype_params_results<'a, T>(
    validator: &'a FuncValidator<T>,
    ty_or_ft: wasmparser::TypeOrFuncType,
) -> WasmResult<(
    impl ExactSizeIterator<Item = wasmparser::Type> + Clone + 'a,
    impl ExactSizeIterator<Item = wasmparser::Type> + Clone + 'a,
)>
where
    T: WasmModuleResources,
{
    return Ok(match ty_or_ft {
        wasmparser::TypeOrFuncType::Type(ty) => {
            let (params, results): (&'static [wasmparser::Type], &'static [wasmparser::Type]) =
                match ty {
                    wasmparser::Type::I32 => (&[], &[wasmparser::Type::I32]),
                    wasmparser::Type::I64 => (&[], &[wasmparser::Type::I64]),
                    wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]),
                    wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]),
                    wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]),
                    wasmparser::Type::ExternRef => (&[], &[wasmparser::Type::ExternRef]),
                    wasmparser::Type::FuncRef => (&[], &[wasmparser::Type::FuncRef]),
                    wasmparser::Type::EmptyBlockType => (&[], &[]),
                    ty => return Err(wasm_unsupported!("blocktype_params_results: type {:?}", ty)),
                };
            (
                itertools::Either::Left(params.iter().copied()),
                itertools::Either::Left(results.iter().copied()),
            )
        }
        wasmparser::TypeOrFuncType::FuncType(ty_index) => {
            let ty = validator
                .resources()
                .func_type_at(ty_index)
                .expect("should be valid");
            (
                itertools::Either::Right(ty.inputs()),
                itertools::Either::Right(ty.outputs()),
            )
        }
    });
}
pub fn block_with_params<PE: TargetEnvironment + ?Sized>(
    builder: &mut FunctionBuilder,
    params: impl IntoIterator<Item = wasmparser::Type>,
    environ: &PE,
) -> WasmResult<ir::Block> {
    let block = builder.create_block();
    for ty in params {
        match ty {
            wasmparser::Type::I32 => {
                builder.append_block_param(block, ir::types::I32);
            }
            wasmparser::Type::I64 => {
                builder.append_block_param(block, ir::types::I64);
            }
            wasmparser::Type::F32 => {
                builder.append_block_param(block, ir::types::F32);
            }
            wasmparser::Type::F64 => {
                builder.append_block_param(block, ir::types::F64);
            }
            wasmparser::Type::ExternRef | wasmparser::Type::FuncRef => {
                builder.append_block_param(block, environ.reference_type(ty.try_into()?));
            }
            wasmparser::Type::V128 => {
                builder.append_block_param(block, ir::types::I8X16);
            }
            ty => {
                return Err(wasm_unsupported!(
                    "block_with_params: type {:?} in multi-value block's signature",
                    ty
                ))
            }
        }
    }
    Ok(block)
}
pub fn f32_translation(x: wasmparser::Ieee32) -> ir::immediates::Ieee32 {
    ir::immediates::Ieee32::with_bits(x.bits())
}
pub fn f64_translation(x: wasmparser::Ieee64) -> ir::immediates::Ieee64 {
    ir::immediates::Ieee64::with_bits(x.bits())
}
pub fn get_vmctx_value_label() -> ir::ValueLabel {
    const VMCTX_LABEL: u32 = 0xffff_fffe;
    ir::ValueLabel::from_u32(VMCTX_LABEL)
}