use std::num::{
    NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,
    NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
};
use std::ptr::NonNull;

/// 関数の引数・返り値を扱うための型とトレイト。
#[derive(Debug)]
pub struct ScriptModuleCallHandle {
    pub(crate) internal: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
    pub(crate) read_section: crate::generic::ReadSection,
}

/// [`ScriptModuleCallHandle`]関連のエラー。
#[derive(thiserror::Error, Debug)]
pub enum ScriptModuleCallHandleError {
    #[error("key contains null byte")]
    KeyContainsNullByte(std::ffi::NulError),

    #[error("value contains null byte")]
    ValueContainsNullByte(std::ffi::NulError),

    #[error("too many elements")]
    TooManyElements,
}

pub type ScriptModuleCallHandleResult<T> = std::result::Result<T, ScriptModuleCallHandleError>;

/// `push_result_function` で返すスクリプトモジュール関数コールバック。
#[derive(Debug, Clone, Copy)]
pub struct ScriptModuleFunctionCallback {
    pub func: unsafe extern "C" fn(*mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM),
    pub userdata: *mut std::ffi::c_void,
}

/// ユーザーデータとして相互変換する構造体。
pub struct ScriptModuleUserData<T: Send + Sync + 'static + AsScriptModuleUserData> {
    data: std::sync::Arc<std::sync::Mutex<T>>,
}

/// [ScriptModuleUserData]から型パラメータを消去した構造体。
#[derive(Debug)]
pub struct ErasedScriptModuleUserData {
    pub meta_method_functions: &'static [aviutl2_sys::module2::META_METHOD_FUNCTION],
    pub userdata: *mut std::ffi::c_void,
}

impl<T: Send + Sync + 'static + AsScriptModuleUserData> From<ScriptModuleUserData<T>>
    for ErasedScriptModuleUserData
{
    fn from(meta_table: ScriptModuleUserData<T>) -> Self {
        let data = std::sync::Arc::into_raw(meta_table.data) as *mut std::ffi::c_void;
        let meta_method_functions = T::META_METHOD_FUNCTIONS;
        ErasedScriptModuleUserData {
            meta_method_functions,
            userdata: data,
        }
    }
}
impl<T: Send + Sync + 'static + AsScriptModuleUserData> ScriptModuleUserData<T> {
    /// 新しいメタテーブルを作成する。
    pub fn new(data: T) -> Self {
        ScriptModuleUserData {
            data: std::sync::Arc::new(std::sync::Mutex::new(data)),
        }
    }

    /// メタテーブルのユーザーデータを取得する。
    pub fn lock(
        &self,
    ) -> Result<std::sync::MutexGuard<'_, T>, std::sync::PoisonError<std::sync::MutexGuard<'_, T>>>
    {
        self.data.lock()
    }
}
impl<T: Send + Sync + 'static + AsScriptModuleUserData> From<T> for ScriptModuleUserData<T> {
    fn from(data: T) -> Self {
        ScriptModuleUserData::new(data)
    }
}

unsafe extern "C" fn dummy_meta_method_function(
    _smp: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
) {
    // 何もしない
}
unsafe extern "C" fn script_module_user_data_gc<
    T: Send + Sync + 'static + AsScriptModuleUserData,
>(
    smp: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
) {
    let userdata = unsafe { (*smp).userdata };
    if !userdata.is_null() {
        unsafe {
            drop(std::sync::Arc::<std::sync::Mutex<T>>::from_raw(
                userdata as *const std::sync::Mutex<T>,
            ));
        }
    }
}

/// ユーザーデータとして変換できる型のトレイト。
///
/// # Note
///
/// [`crate::module::metatable`]を使うと自動的に実装されます。
pub trait AsScriptModuleUserData: Send + Sync + Sized + 'static {
    const META_METHOD_FUNCTIONS: &'static [aviutl2_sys::module2::META_METHOD_FUNCTION] = &[
        aviutl2_sys::module2::META_METHOD_FUNCTION {
            method: c"__gc".as_ptr(),
            func: script_module_user_data_gc::<Self>,
        },
        aviutl2_sys::module2::META_METHOD_FUNCTION {
            method: std::ptr::null(),
            func: dummy_meta_method_function,
        },
    ];
}

/// スクリプトモジュールで渡された引数の型。
///
/// # See Also
///
/// <https://www.lua.org/pil/2.html>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParamType {
    Nil,
    Boolean,
    LightUserdata,
    Number,
    String,
    Table,
    Function,
    Userdata,
    Thread,
}

/// 引数の取得に失敗した場合のエラー。
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum GetParamError<T: std::fmt::Debug> {
    #[error("expected {expected:?}, but got {actual:?}")]
    TypeMismatch {
        /// 期待される型。
        expected: ParamType,
        /// 実際の型。
        actual: ParamType,
    },
    #[error("index {index} is out of bounds, {len} given")]
    IndexOutOfBounds {
        /// 取得しようとした引数のインデックス。
        index: usize,
        /// 引数の総数。
        len: usize,
    },
    #[error("failed to convert value: {0}")]
    ConversionError(T),
}
pub type GetParamResult<T, E = std::convert::Infallible> = std::result::Result<T, GetParamError<E>>;

/// 引数変換に失敗した場合のエラー。
#[derive(Debug, thiserror::Error)]
#[error("{message}")]
pub struct ParamConversionError {
    message: String,
}

impl ParamConversionError {
    pub fn new(message: impl Into<String>) -> Self {
        Self {
            message: message.into(),
        }
    }
}

impl GetParamError<std::convert::Infallible> {
    fn into_conversion_error<T: std::fmt::Debug>(self) -> GetParamError<T> {
        match self {
            GetParamError::TypeMismatch { expected, actual } => {
                GetParamError::TypeMismatch { expected, actual }
            }
            GetParamError::IndexOutOfBounds { index, len } => {
                GetParamError::IndexOutOfBounds { index, len }
            }
            GetParamError::ConversionError(error) => match error {},
        }
    }
}

impl ScriptModuleCallHandle {
    /// ポインタから`ScriptModuleParam`を作成する。
    ///
    /// # Safety
    ///
    /// `ptr`は有効な`SCRIPT_MODULE_PARAM`へのポインタである必要があります。
    pub unsafe fn from_raw(
        ptr: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
    ) -> ScriptModuleCallHandle {
        ScriptModuleCallHandle {
            internal: ptr,
            read_section: unsafe { crate::generic::ReadSection::from_raw((*ptr).edit) },
        }
    }

    /// 引数の数を返す。
    pub fn len(&self) -> usize {
        unsafe { ((*self.internal).get_param_num)() as usize }
    }

    /// 引数が与えられていないかを返す。
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// 渡された引数の型を取得する。
    ///
    /// # Note
    ///
    /// 引数が渡されていない場合は`None`を返します。
    pub fn get_param_type(&self, index: usize) -> Option<ParamType> {
        let param_type = unsafe { ((*self.internal).get_param_type)(index as i32) };
        match param_type {
            aviutl2_sys::module2::PARAM_TYPE::NONE => None,
            aviutl2_sys::module2::PARAM_TYPE::NIL => Some(ParamType::Nil),
            aviutl2_sys::module2::PARAM_TYPE::BOOLEAN => Some(ParamType::Boolean),
            aviutl2_sys::module2::PARAM_TYPE::LIGHTUSERDATA => Some(ParamType::LightUserdata),
            aviutl2_sys::module2::PARAM_TYPE::NUMBER => Some(ParamType::Number),
            aviutl2_sys::module2::PARAM_TYPE::STRING => Some(ParamType::String),
            aviutl2_sys::module2::PARAM_TYPE::TABLE => Some(ParamType::Table),
            aviutl2_sys::module2::PARAM_TYPE::FUNCTION => Some(ParamType::Function),
            aviutl2_sys::module2::PARAM_TYPE::USERDATA => Some(ParamType::Userdata),
            aviutl2_sys::module2::PARAM_TYPE::THREAD => Some(ParamType::Thread),
        }
    }

    fn assert_param_type(&self, index: usize, expected: ParamType) -> GetParamResult<()> {
        let actual = self
            .get_param_type(index)
            .ok_or(GetParamError::IndexOutOfBounds {
                index,
                len: self.len(),
            })?;
        if actual != expected {
            return Err(GetParamError::TypeMismatch { expected, actual });
        }
        Ok(())
    }

    /// 引数を取得する。
    pub fn get_param<'a, T: FromScriptModuleParam<'a>>(
        &'a self,
        index: usize,
    ) -> GetParamResult<T, T::Error> {
        T::from_param(self, index)
    }

    /// 引数を整数として取得する。
    pub fn get_param_int(&self, index: usize) -> GetParamResult<i32> {
        self.assert_param_type(index, ParamType::Number)?;
        Ok(unsafe { ((*self.internal).get_param_int)(index as i32) })
    }

    /// 引数を浮動小数点数として取得する。
    pub fn get_param_float(&self, index: usize) -> GetParamResult<f64> {
        self.assert_param_type(index, ParamType::Number)?;
        Ok(unsafe { ((*self.internal).get_param_double)(index as i32) })
    }

    /// 引数を文字列として取得する。
    pub fn get_param_str(&self, index: usize) -> GetParamResult<String> {
        self.assert_param_type(index, ParamType::String)?;
        unsafe {
            let c_str = ((*self.internal).get_param_string)(index as i32);
            assert!(!c_str.is_null(), "get_param_string returned null");
            Ok(std::ffi::CStr::from_ptr(c_str)
                .to_string_lossy()
                .into_owned())
        }
    }

    /// 引数をデータポインタとして取得する。
    ///
    /// # Note
    ///
    /// 引数を取得できない場合は`None`を返します。
    pub fn get_param_data<T>(&self, index: usize) -> Option<NonNull<T>> {
        unsafe {
            let data_ptr = ((*self.internal).get_param_data)(index as i32);
            NonNull::new(data_ptr as *mut T)
        }
    }

    /// 引数をブール値として取得する。
    ///
    /// # Note
    ///
    /// 引数を取得できない場合は`false`を返します。
    pub fn get_param_boolean(&self, index: usize) -> bool {
        unsafe { ((*self.internal).get_param_boolean)(index as i32) }
    }

    /// 引数のテーブルの要素を整数として取得する。
    ///
    /// # Note
    ///
    /// 引数を取得できない場合は0を返します。
    pub fn get_param_table_int(
        &self,
        index: usize,
        key: &str,
    ) -> ScriptModuleCallHandleResult<i32> {
        let c_key = std::ffi::CString::new(key)
            .map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
        Ok(unsafe { ((*self.internal).get_param_table_int)(index as i32, c_key.as_ptr()) })
    }

    /// 引数のテーブルの要素を浮動小数点数として取得する。
    ///
    /// # Note
    ///
    /// 引数を取得できない場合は0.0を返します。
    pub fn get_param_table_float(
        &self,
        index: usize,
        key: &str,
    ) -> ScriptModuleCallHandleResult<f64> {
        let c_key = std::ffi::CString::new(key)
            .map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
        Ok(unsafe { ((*self.internal).get_param_table_double)(index as i32, c_key.as_ptr()) })
    }

    /// 引数のテーブルの要素を文字列として取得する。
    pub fn get_param_table_str(
        &self,
        index: usize,
        key: &str,
    ) -> ScriptModuleCallHandleResult<Option<String>> {
        let c_key = std::ffi::CString::new(key)
            .map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
        unsafe {
            let c_str = ((*self.internal).get_param_table_string)(index as i32, c_key.as_ptr());
            Ok(if c_str.is_null() {
                None
            } else {
                Some(
                    std::ffi::CStr::from_ptr(c_str)
                        .to_string_lossy()
                        .into_owned(),
                )
            })
        }
    }

    /// 引数のテーブルの要素をブール値として取得する。
    ///
    /// # Note
    ///
    /// 引数を取得できない場合は`false`を返します。
    pub fn get_param_table_boolean(
        &self,
        index: usize,
        key: &str,
    ) -> ScriptModuleCallHandleResult<bool> {
        let c_key = std::ffi::CString::new(key)
            .map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
        Ok(unsafe { ((*self.internal).get_param_table_boolean)(index as i32, c_key.as_ptr()) })
    }

    /// 引数の配列の要素の数を取得する。
    pub fn get_param_array_len(&self, index: usize) -> usize {
        unsafe { ((*self.internal).get_param_array_num)(index as i32) as usize }
    }

    /// 引数の配列の要素を数値として取得する。
    pub fn get_param_array_int(&self, index: usize, array_index: usize) -> i32 {
        unsafe { ((*self.internal).get_param_array_int)(index as i32, array_index as i32) }
    }

    /// 引数の配列の要素を浮動小数点数として取得する。
    pub fn get_param_array_float(&self, index: usize, array_index: usize) -> f64 {
        unsafe { ((*self.internal).get_param_array_double)(index as i32, array_index as i32) }
    }

    /// 引数の配列の要素を文字列として取得する。
    pub fn get_param_array_str(&self, index: usize, array_index: usize) -> Option<String> {
        unsafe {
            let c_str = ((*self.internal).get_param_array_string)(index as i32, array_index as i32);
            if c_str.is_null() {
                None
            } else {
                Some(
                    std::ffi::CStr::from_ptr(c_str)
                        .to_string_lossy()
                        .into_owned(),
                )
            }
        }
    }

    /// 関数のエラーを設定する。
    pub fn set_error(&mut self, message: &str) -> ScriptModuleCallHandleResult<()> {
        let c_message = std::ffi::CString::new(message)
            .map_err(ScriptModuleCallHandleError::ValueContainsNullByte)?;
        unsafe {
            ((*self.internal).set_error)(c_message.as_ptr());
        }
        Ok(())
    }

    /// 関数の返り値を追加する。
    pub fn push_result<T: IntoScriptModuleReturnValue>(
        &mut self,
        value: T,
    ) -> Result<(), IntoScriptModuleReturnValueError<T::Err>> {
        value.push_into(self)
    }

    /// 関数の返り値に整数を追加する。
    pub fn push_result_int(&mut self, value: i32) {
        unsafe {
            ((*self.internal).push_result_int)(value);
        }
    }

    /// 関数の返り値に浮動小数点数を追加する。
    pub fn push_result_float(&mut self, value: f64) {
        unsafe {
            ((*self.internal).push_result_double)(value);
        }
    }

    /// 関数の返り値に文字列を追加する。
    pub fn push_result_str(&mut self, value: &str) -> ScriptModuleCallHandleResult<()> {
        let c_value = std::ffi::CString::new(value)
            .map_err(ScriptModuleCallHandleError::ValueContainsNullByte)?;
        unsafe {
            ((*self.internal).push_result_string)(c_value.as_ptr());
        }
        Ok(())
    }

    /// 関数の返り値にデータポインタを追加する。
    pub fn push_result_data<T>(&mut self, value: *const T) {
        unsafe {
            ((*self.internal).push_result_data)(value as *const std::ffi::c_void);
        }
    }

    /// 関数の返り値に関数を追加する。
    ///
    /// # Note
    ///
    /// メモリリークします。
    pub fn push_result_function(&mut self, callback: ScriptModuleFunctionCallback) {
        unsafe {
            ((*self.internal).push_result_function)(callback.func, callback.userdata);
        }
    }

    /// 関数の返り値にメタテーブルを追加する。
    pub fn push_result_meta_table<T: Into<ErasedScriptModuleUserData>>(&mut self, meta_table: T) {
        unsafe {
            let meta_table: ErasedScriptModuleUserData = meta_table.into();
            ((*self.internal).push_result_meta_table)(
                meta_table.meta_method_functions.as_ptr(),
                meta_table.userdata,
            );
        }
    }

    /// 関数の返り値に整数の連想配列を追加する。
    pub fn push_result_table_int<'a, T>(&mut self, table: T) -> ScriptModuleCallHandleResult<()>
    where
        T: std::iter::IntoIterator<Item = (&'a str, i32)>,
    {
        let mut keys = Vec::new();
        let mut values = Vec::new();
        for (key, value) in table {
            let c_key = std::ffi::CString::new(key)
                .map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
            keys.push(c_key);
            values.push(value);
        }
        let key_ptrs: Vec<*const std::os::raw::c_char> = keys.iter().map(|k| k.as_ptr()).collect();
        unsafe {
            ((*self.internal).push_result_table_int)(
                key_ptrs.as_ptr(),
                values.as_ptr(),
                key_ptrs.len() as i32,
            );
        }
        Ok(())
    }

    /// 関数の返り値に浮動小数点数の連想配列を追加する。
    pub fn push_result_table_float<'a, T>(&mut self, table: T) -> ScriptModuleCallHandleResult<()>
    where
        T: std::iter::IntoIterator<Item = (&'a str, f64)>,
    {
        let mut keys = Vec::new();
        let mut values = Vec::new();
        for (key, value) in table {
            let c_key = std::ffi::CString::new(key)
                .map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
            keys.push(c_key);
            values.push(value);
        }
        let key_ptrs: Vec<*const std::os::raw::c_char> = keys.iter().map(|k| k.as_ptr()).collect();
        unsafe {
            ((*self.internal).push_result_table_double)(
                key_ptrs.as_ptr(),
                values.as_ptr(),
                key_ptrs.len() as i32,
            );
        }
        Ok(())
    }

    /// 関数の返り値に文字列の連想配列を追加する。
    pub fn push_result_table_str<'a, T>(&mut self, table: T) -> ScriptModuleCallHandleResult<()>
    where
        T: std::iter::IntoIterator<Item = (&'a str, &'a str)>,
    {
        let mut keys = Vec::new();
        let mut values = Vec::new();
        for (key, value) in table {
            let c_key = std::ffi::CString::new(key)
                .map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
            let c_value = std::ffi::CString::new(value)
                .map_err(ScriptModuleCallHandleError::ValueContainsNullByte)?;
            keys.push(c_key);
            values.push(c_value);
        }
        if keys.len() > i32::MAX as usize {
            return Err(ScriptModuleCallHandleError::TooManyElements);
        }
        let key_ptrs: Vec<*const std::os::raw::c_char> = keys.iter().map(|k| k.as_ptr()).collect();
        let value_ptrs: Vec<*const std::os::raw::c_char> =
            values.iter().map(|v| v.as_ptr()).collect();
        unsafe {
            ((*self.internal).push_result_table_string)(
                key_ptrs.as_ptr(),
                value_ptrs.as_ptr(),
                key_ptrs.len() as i32,
            );
        }
        Ok(())
    }

    /// 関数の返り値にブール値の連想配列を追加する。
    pub fn push_result_table_boolean<'a, T>(&mut self, table: T) -> ScriptModuleCallHandleResult<()>
    where
        T: std::iter::IntoIterator<Item = (&'a str, bool)>,
    {
        let mut keys = Vec::new();
        let mut values = Vec::new();
        for (key, value) in table {
            let c_key = std::ffi::CString::new(key)
                .map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
            keys.push(c_key);
            values.push(value);
        }
        let key_ptrs: Vec<*const std::os::raw::c_char> = keys.iter().map(|k| k.as_ptr()).collect();
        unsafe {
            ((*self.internal).push_result_table_boolean)(
                key_ptrs.as_ptr(),
                values.as_ptr(),
                key_ptrs.len() as i32,
            );
        }
        Ok(())
    }

    /// 関数の返り値に整数の配列を追加する。
    pub fn push_result_array_int(&mut self, values: &[i32]) -> ScriptModuleCallHandleResult<()> {
        if values.len() > i32::MAX as usize {
            return Err(ScriptModuleCallHandleError::TooManyElements);
        }
        unsafe {
            ((*self.internal).push_result_array_int)(values.as_ptr(), values.len() as i32);
        }
        Ok(())
    }

    /// 関数の返り値に浮動小数点数の配列を追加する。
    pub fn push_result_array_float(&mut self, values: &[f64]) -> ScriptModuleCallHandleResult<()> {
        if values.len() > i32::MAX as usize {
            return Err(ScriptModuleCallHandleError::TooManyElements);
        }
        unsafe {
            ((*self.internal).push_result_array_double)(values.as_ptr(), values.len() as i32);
        }
        Ok(())
    }

    /// 関数の返り値に文字列の配列を追加する。
    pub fn push_result_array_str(&mut self, values: &[&str]) -> ScriptModuleCallHandleResult<()> {
        let c_values: Vec<std::ffi::CString> = values
            .iter()
            .map(|s| std::ffi::CString::new(*s))
            .collect::<Result<_, _>>()
            .map_err(ScriptModuleCallHandleError::ValueContainsNullByte)?;
        if c_values.len() > i32::MAX as usize {
            return Err(ScriptModuleCallHandleError::TooManyElements);
        }
        let c_value_ptrs: Vec<*const std::os::raw::c_char> =
            c_values.iter().map(|s| s.as_ptr()).collect();
        unsafe {
            ((*self.internal).push_result_array_string)(
                c_value_ptrs.as_ptr(),
                c_value_ptrs.len() as i32,
            );
        }
        Ok(())
    }

    /// 関数の返り値にブール値の配列を追加する。
    pub fn push_result_array_boolean(
        &mut self,
        values: &[bool],
    ) -> ScriptModuleCallHandleResult<()> {
        if values.len() > i32::MAX as usize {
            return Err(ScriptModuleCallHandleError::TooManyElements);
        }
        unsafe {
            ((*self.internal).push_result_array_boolean)(values.as_ptr(), values.len() as i32);
        }
        Ok(())
    }

    /// 関数の返り値にブール値を追加する。
    pub fn push_result_boolean(&mut self, value: bool) {
        unsafe {
            ((*self.internal).push_result_boolean)(value);
        }
    }

    /// 読み取り専用の編集セクション。
    pub fn read_section(&self) -> &crate::generic::ReadSection {
        &self.read_section
    }
}

/// スクリプトモジュールの引数として受け取れる値。
///
/// # Note
///
/// このtraitはDeriveマクロを使用して実装することもできます。
/// 詳細は[`derive@FromScriptModuleParam`]のドキュメントを参照してください。
pub trait FromScriptModuleParam<'a>: Sized {
    type Error: std::error::Error;

    fn from_param(
        param: &'a crate::module::ScriptModuleCallHandle,
        index: usize,
    ) -> GetParamResult<Self, Self::Error>;
}

pub use aviutl2_macros::FromScriptModuleParam;

impl<'a> FromScriptModuleParam<'a> for &'a crate::generic::ReadSection {
    type Error = std::convert::Infallible;

    fn from_param(
        param: &'a crate::module::ScriptModuleCallHandle,
        _index: usize,
    ) -> GetParamResult<Self> {
        Ok(param.read_section())
    }
}

impl<'a> FromScriptModuleParam<'a> for i32 {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        param.get_param_int(index)
    }
}
#[duplicate::duplicate_item(
    Integer Failable;
    [i8]    [true];
    [i16]   [true];
    [i64]   [false];
    [i128]  [false];
    [isize] [false];
    [u8]    [true];
    [u16]   [true];
    [u32]   [true];
    [u64]   [false];
    [u128]  [false];
    [usize] [false];
)]
impl<'a> FromScriptModuleParam<'a> for Integer {
    type Error = std::num::TryFromIntError;

    fn from_param(
        param: &'a ScriptModuleCallHandle,
        index: usize,
    ) -> GetParamResult<Self, Self::Error> {
        let value = param
            .get_param_int(index)
            .map_err(GetParamError::into_conversion_error)?;
        comptime_if::comptime_if!(
            if failable where (failable = Failable) {
                value.try_into().map_err(GetParamError::ConversionError)
            } else {
                Ok(value as Integer)
            }
        )
    }
}
impl<'a> FromScriptModuleParam<'a> for f64 {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        param.get_param_float(index)
    }
}
impl<'a> FromScriptModuleParam<'a> for f32 {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        param.get_param_float(index).map(|value| value as f32)
    }
}
impl<'a> FromScriptModuleParam<'a> for bool {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        param.assert_param_type(index, ParamType::Boolean)?;
        Ok(param.get_param_boolean(index))
    }
}
impl<'a> FromScriptModuleParam<'a> for String {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        param.get_param_str(index)
    }
}
impl<'a, T> FromScriptModuleParam<'a> for NonNull<T> {
    type Error = ParamConversionError;

    fn from_param(
        param: &'a ScriptModuleCallHandle,
        index: usize,
    ) -> GetParamResult<Self, Self::Error> {
        param.get_param_data(index).ok_or_else(|| {
            GetParamError::ConversionError(ParamConversionError::new("param data is null"))
        })
    }
}
#[duplicate::duplicate_item(
    Integer     NonZero        Failable;
    [i8]        [NonZeroI8]    [true];
    [i16]       [NonZeroI16]   [true];
    [i64]       [NonZeroI64]   [false];
    [i128]      [NonZeroI128]  [false];
    [isize]     [NonZeroIsize] [false];
    [u8]        [NonZeroU8]    [true];
    [u16]       [NonZeroU16]   [true];
    [u32]       [NonZeroU32]   [true];
    [u64]       [NonZeroU64]   [false];
    [u128]      [NonZeroU128]  [false];
    [usize]     [NonZeroUsize] [false];
)]
impl<'a> FromScriptModuleParam<'a> for NonZero {
    type Error = ParamConversionError;

    fn from_param(
        param: &'a ScriptModuleCallHandle,
        index: usize,
    ) -> GetParamResult<Self, Self::Error> {
        let value = param
            .get_param_int(index)
            .map_err(GetParamError::into_conversion_error)?;
        let value: Integer = comptime_if::comptime_if!(
            if failable where (failable = Failable) {
                value
                    .try_into()
                    .map_err(|error: std::num::TryFromIntError| GetParamError::ConversionError(ParamConversionError::new(error.to_string())))?
            } else {
                value as Integer
            }
        );
        NonZero::new(value).ok_or_else(|| {
            GetParamError::ConversionError(ParamConversionError::new("value is zero"))
        })
    }
}
impl<'a> FromScriptModuleParam<'a> for NonZeroI32 {
    type Error = ParamConversionError;

    fn from_param(
        param: &'a ScriptModuleCallHandle,
        index: usize,
    ) -> GetParamResult<Self, Self::Error> {
        let value = param
            .get_param_int(index)
            .map_err(GetParamError::into_conversion_error)?;
        NonZeroI32::new(value).ok_or_else(|| {
            GetParamError::ConversionError(ParamConversionError::new("value is zero"))
        })
    }
}

impl<'a, T> FromScriptModuleParam<'a> for Option<T>
where
    T: FromScriptModuleParam<'a>,
{
    type Error = T::Error;

    fn from_param(
        param: &'a ScriptModuleCallHandle,
        index: usize,
    ) -> GetParamResult<Self, Self::Error> {
        match param.get_param_type(index) {
            None | Some(ParamType::Nil) => Ok(None),
            Some(_) => T::from_param(param, index).map(Some),
        }
    }
}
impl<'a> FromScriptModuleParam<'a> for () {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        param
            .get_param_type(index)
            .map(|_| ())
            .ok_or(GetParamError::IndexOutOfBounds {
                index,
                len: param.len(),
            })
    }
}

/// スクリプトモジュールの引数として渡される配列。
pub struct ScriptModuleParamArray<'a> {
    index: usize,
    ptr: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
    marker: std::marker::PhantomData<&'a ()>,
}

impl std::fmt::Debug for ScriptModuleParamArray<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ScriptModuleParamArray")
            .field("index", &self.index)
            .field("len", &self.len())
            .finish()
    }
}

impl<'a> ScriptModuleParamArray<'a> {
    /// 配列の長さを返す。
    pub fn len(&self) -> usize {
        unsafe { ((*self.ptr).get_param_array_num)(self.index as i32) as usize }
    }

    /// 配列が空かどうかを返す。
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// 配列の要素を整数として取得する。
    pub fn get_int(&self, array_index: usize) -> i32 {
        unsafe { ((*self.ptr).get_param_array_int)(self.index as i32, array_index as i32) }
    }

    /// 配列の要素を浮動小数点数として取得する。
    pub fn get_float(&self, array_index: usize) -> f64 {
        unsafe { ((*self.ptr).get_param_array_double)(self.index as i32, array_index as i32) }
    }

    /// 配列の要素を文字列として取得する。
    pub fn get_str(&self, array_index: usize) -> Option<String> {
        unsafe {
            let c_str = ((*self.ptr).get_param_array_string)(self.index as i32, array_index as i32);
            if c_str.is_null() {
                None
            } else {
                Some(
                    std::ffi::CStr::from_ptr(c_str)
                        .to_string_lossy()
                        .into_owned(),
                )
            }
        }
    }
}

impl<'a> FromScriptModuleParam<'a> for ScriptModuleParamArray<'a> {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        param.assert_param_type(index, ParamType::Table)?;
        Ok(ScriptModuleParamArray {
            index,
            ptr: param.internal,
            marker: std::marker::PhantomData,
        })
    }
}

/// スクリプトモジュールの引数として渡される連想配列。
#[derive(Debug)]
pub struct ScriptModuleParamTable<'a> {
    index: usize,
    ptr: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
    marker: std::marker::PhantomData<&'a ()>,
}

impl<'a> ScriptModuleParamTable<'a> {
    /// 連想配列の要素を整数として取得する。
    pub fn get_int(&self, key: &str) -> i32 {
        let c_key = std::ffi::CString::new(key).unwrap();
        unsafe { ((*self.ptr).get_param_table_int)(self.index as i32, c_key.as_ptr()) }
    }

    /// 連想配列の要素を浮動小数点数として取得する。
    pub fn get_float(&self, key: &str) -> f64 {
        let c_key = std::ffi::CString::new(key).unwrap();
        unsafe { ((*self.ptr).get_param_table_double)(self.index as i32, c_key.as_ptr()) }
    }

    /// 連想配列の要素を文字列として取得する。
    pub fn get_str(&self, key: &str) -> Option<String> {
        let c_key = std::ffi::CString::new(key).unwrap();
        unsafe {
            let c_str = ((*self.ptr).get_param_table_string)(self.index as i32, c_key.as_ptr());
            if c_str.is_null() {
                None
            } else {
                Some(
                    std::ffi::CStr::from_ptr(c_str)
                        .to_string_lossy()
                        .into_owned(),
                )
            }
        }
    }

    /// 連想配列の要素をブール値として取得する。
    pub fn get_boolean(&self, key: &str) -> bool {
        let c_key = std::ffi::CString::new(key).unwrap();
        unsafe { ((*self.ptr).get_param_table_boolean)(self.index as i32, c_key.as_ptr()) }
    }
}

impl<'a> FromScriptModuleParam<'a> for ScriptModuleParamTable<'a> {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        param.assert_param_type(index, ParamType::Table)?;
        Ok(ScriptModuleParamTable {
            index,
            ptr: param.internal,
            marker: std::marker::PhantomData,
        })
    }
}

impl<'a> FromScriptModuleParam<'a> for Vec<String> {
    type Error = ParamConversionError;

    fn from_param(
        param: &'a ScriptModuleCallHandle,
        index: usize,
    ) -> GetParamResult<Self, Self::Error> {
        let array = ScriptModuleParamArray::from_param(param, index)
            .map_err(GetParamError::into_conversion_error)?;
        let mut result = Vec::new();
        for i in 0..array.len() {
            let value = array.get_str(i).ok_or_else(|| {
                GetParamError::ConversionError(ParamConversionError::new(format!(
                    "array element #{i} is not a string"
                )))
            })?;
            result.push(value);
        }
        Ok(result)
    }
}
impl<'a> FromScriptModuleParam<'a> for Vec<i32> {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        let array = ScriptModuleParamArray::from_param(param, index)?;
        let mut result = Vec::new();
        for i in 0..array.len() {
            result.push(array.get_int(i));
        }
        Ok(result)
    }
}
impl<'a> FromScriptModuleParam<'a> for Vec<f64> {
    type Error = std::convert::Infallible;

    fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
        let array = ScriptModuleParamArray::from_param(param, index)?;
        let mut result = Vec::new();
        for i in 0..array.len() {
            result.push(array.get_float(i));
        }
        Ok(result)
    }
}

/// 連想配列の値として使える型。
pub trait FromScriptModuleParamTable<'a>: Sized {
    type Error: std::error::Error;

    fn from_param_table(
        param: &'a crate::module::ScriptModuleParamTable,
        key: &str,
    ) -> GetParamResult<Self, Self::Error>;
}

impl<'a> FromScriptModuleParamTable<'a> for i32 {
    type Error = std::convert::Infallible;

    fn from_param_table(param: &'a ScriptModuleParamTable, key: &str) -> GetParamResult<Self> {
        Ok(param.get_int(key))
    }
}
#[duplicate::duplicate_item(
    Integer Failable;
    [i8]    [true];
    [i16]   [true];
    [i64]   [false];
    [i128]  [false];
    [isize] [false];
    [u8]    [true];
    [u16]   [true];
    [u32]   [true];
    [u64]   [false];
    [u128]  [false];
    [usize] [false];
)]
impl<'a> FromScriptModuleParamTable<'a> for Integer {
    type Error = std::num::TryFromIntError;

    fn from_param_table(
        param: &'a ScriptModuleParamTable,
        key: &str,
    ) -> GetParamResult<Self, Self::Error> {
        let value = param.get_int(key);
        comptime_if::comptime_if!(
            if failable where (failable = Failable) {
                value.try_into().map_err(GetParamError::ConversionError)
            } else {
                Ok(value as Integer)
            }
        )
    }
}
impl<'a> FromScriptModuleParamTable<'a> for f64 {
    type Error = std::convert::Infallible;

    fn from_param_table(param: &'a ScriptModuleParamTable, key: &str) -> GetParamResult<Self> {
        Ok(param.get_float(key))
    }
}
impl<'a> FromScriptModuleParamTable<'a> for f32 {
    type Error = std::convert::Infallible;

    fn from_param_table(param: &'a ScriptModuleParamTable, key: &str) -> GetParamResult<Self> {
        Ok(param.get_float(key) as f32)
    }
}
impl<'a> FromScriptModuleParamTable<'a> for String {
    type Error = ParamConversionError;

    fn from_param_table(
        param: &'a ScriptModuleParamTable,
        key: &str,
    ) -> GetParamResult<Self, Self::Error> {
        param.get_str(key).ok_or_else(|| {
            GetParamError::ConversionError(ParamConversionError::new(format!(
                "key `{key}` is not a string"
            )))
        })
    }
}
impl<'a> FromScriptModuleParamTable<'a> for bool {
    type Error = std::convert::Infallible;

    fn from_param_table(param: &'a ScriptModuleParamTable, key: &str) -> GetParamResult<Self> {
        Ok(param.get_boolean(key))
    }
}
impl<'a, T: FromScriptModuleParamTable<'a>> FromScriptModuleParamTable<'a> for Option<T> {
    type Error = T::Error;

    fn from_param_table(
        param: &'a ScriptModuleParamTable,
        key: &str,
    ) -> GetParamResult<Self, Self::Error> {
        match T::from_param_table(param, key) {
            Ok(value) => Ok(Some(value)),
            Err(GetParamError::ConversionError(_)) => Ok(None),
            Err(error) => Err(error),
        }
    }
}

/// スクリプトモジュールの関数の戻り値の型を表す列挙型。
#[derive(Debug)]
pub enum ScriptModuleReturnValue {
    Int(i32),
    Float(f64),
    String(String),
    Boolean(bool),
    Data(*const std::ffi::c_void),
    StringArray(Vec<String>),
    IntArray(Vec<i32>),
    FloatArray(Vec<f64>),
    IntTable(std::collections::HashMap<String, i32>),
    FloatTable(std::collections::HashMap<String, f64>),
    StringTable(std::collections::HashMap<String, String>),
    Function(ScriptModuleFunctionCallback),
    MetaTable(ErasedScriptModuleUserData),
}

/// [`IntoScriptModuleReturnValue::push_into`]で使われるエラー。
#[derive(thiserror::Error, Debug)]
pub enum IntoScriptModuleReturnValueError<T> {
    #[error("failed to convert value: {0}")]
    ConversionFailed(#[source] T),
    #[error("failed to push return value: {0}")]
    PushFailed(#[from] ScriptModuleCallHandleError),
}

/// 関数の戻り値として使える型。
///
/// # Note
///
/// この関数はDeriveマクロを使用して実装することもできます。
/// 詳細は[`derive@IntoScriptModuleReturnValue`]のドキュメントを参照してください。
pub trait IntoScriptModuleReturnValue
where
    Self: Sized,
{
    type Err: Send + Sync + 'static + Into<Box<dyn std::error::Error + Send + Sync + 'static>>;

    fn into_return_values(self) -> Result<Vec<crate::module::ScriptModuleReturnValue>, Self::Err>;
    fn push_into(
        self,
        param: &mut crate::module::ScriptModuleCallHandle,
    ) -> Result<(), crate::module::IntoScriptModuleReturnValueError<Self::Err>> {
        for value in self
            .into_return_values()
            .map_err(IntoScriptModuleReturnValueError::ConversionFailed)?
        {
            match value {
                ScriptModuleReturnValue::Int(v) => {
                    param.push_result_int(v);
                }
                ScriptModuleReturnValue::Float(v) => {
                    param.push_result_float(v);
                }
                ScriptModuleReturnValue::String(v) => {
                    param.push_result_str(&v)?;
                }
                ScriptModuleReturnValue::Boolean(v) => {
                    param.push_result_boolean(v);
                }
                ScriptModuleReturnValue::Data(v) => {
                    param.push_result_data(v);
                }
                ScriptModuleReturnValue::Function(v) => {
                    param.push_result_function(v);
                }
                ScriptModuleReturnValue::MetaTable(v) => {
                    param.push_result_meta_table(v);
                }
                ScriptModuleReturnValue::StringArray(v) => {
                    let strs: Vec<&str> = v.iter().map(|s| s.as_str()).collect();
                    param.push_result_array_str(&strs)?
                }
                ScriptModuleReturnValue::IntArray(v) => param.push_result_array_int(&v)?,
                ScriptModuleReturnValue::FloatArray(v) => param.push_result_array_float(&v)?,
                ScriptModuleReturnValue::IntTable(v) => {
                    let table = v.iter().map(|(k, v)| (k.as_str(), *v));
                    param.push_result_table_int(table)?;
                }
                ScriptModuleReturnValue::FloatTable(v) => {
                    let table = v.iter().map(|(k, v)| (k.as_str(), *v));
                    param.push_result_table_float(table)?;
                }
                ScriptModuleReturnValue::StringTable(v) => {
                    let table = v.iter().map(|(k, v)| (k.as_str(), v.as_str()));
                    param.push_result_table_str(table)?;
                }
            };
        }
        Ok(())
    }
}
pub use aviutl2_macros::IntoScriptModuleReturnValue;

impl<T> IntoScriptModuleReturnValue for *const T {
    type Err = std::convert::Infallible;

    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::Data(
            self as *const std::ffi::c_void,
        )])
    }
}

impl IntoScriptModuleReturnValue for ScriptModuleFunctionCallback {
    type Err = std::convert::Infallible;

    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::Function(self)])
    }
}

impl<T: Into<ErasedScriptModuleUserData>> IntoScriptModuleReturnValue for T {
    type Err = std::convert::Infallible;

    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::MetaTable(self.into())])
    }
}

impl IntoScriptModuleReturnValue for i32 {
    type Err = std::convert::Infallible;

    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::Int(self)])
    }
}
#[duplicate::duplicate_item(
    Integer;
    [i8];
    [i16];
    [u8];
    [u16];
)]
impl IntoScriptModuleReturnValue for Integer {
    type Err = std::convert::Infallible;

    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::Int(self as i32)])
    }
}
#[duplicate::duplicate_item(
    Integer;
    [i64];
    [i128];
    [isize];
    [u32];
    [u64];
    [u128];
    [usize];
)]
impl IntoScriptModuleReturnValue for Integer {
    type Err = std::num::TryFromIntError;

    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::Int(self.try_into()?)])
    }
}
impl IntoScriptModuleReturnValue for f64 {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::Float(self)])
    }
}
impl IntoScriptModuleReturnValue for f32 {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::Float(self as f64)])
    }
}
impl IntoScriptModuleReturnValue for bool {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::Boolean(self)])
    }
}
impl IntoScriptModuleReturnValue for &str {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::String(self.to_string())])
    }
}
impl IntoScriptModuleReturnValue for String {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::String(self)])
    }
}

impl IntoScriptModuleReturnValue for ScriptModuleReturnValue {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![self])
    }
}

impl IntoScriptModuleReturnValue for Vec<ScriptModuleReturnValue> {
    type Err = std::convert::Infallible;

    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(self)
    }
}

impl<T: IntoScriptModuleReturnValue> IntoScriptModuleReturnValue for Option<T> {
    type Err = T::Err;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        if let Some(value) = self {
            value.into_return_values()
        } else {
            Ok(Vec::new())
        }
    }
}
impl<T, const N: usize> IntoScriptModuleReturnValue for [T; N]
where
    Vec<T>: IntoScriptModuleReturnValue,
{
    type Err = <Vec<T> as IntoScriptModuleReturnValue>::Err;

    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        let vec: Vec<T> = self.into();
        vec.into_return_values()
    }
}
impl<T: IntoScriptModuleReturnValue, E> IntoScriptModuleReturnValue for Result<T, E>
where
    E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
{
    type Err = T::Err;

    fn into_return_values(
        self,
    ) -> Result<
        Vec<ScriptModuleReturnValue>,
        <std::result::Result<T, E> as IntoScriptModuleReturnValue>::Err,
    > {
        match self {
            Ok(value) => value.into_return_values(),
            Err(_) => Ok(Vec::new()),
        }
    }
    fn push_into(
        self,
        param: &mut ScriptModuleCallHandle,
    ) -> Result<
        (),
        IntoScriptModuleReturnValueError<
            <std::result::Result<T, E> as IntoScriptModuleReturnValue>::Err,
        >,
    > {
        match self {
            Ok(value) => value.push_into(param)?,
            Err(err) => {
                let e: Box<dyn std::error::Error + 'static> = err.into();
                let e = e.to_string();
                param.set_error(&e)?
            }
        }
        Ok(())
    }
}

impl IntoScriptModuleReturnValue for () {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(Vec::new())
    }
}

macro_rules! impl_into_script_module_return_value_for_tuple {
    ($($name:ident),+) => {
        impl<$($name),+> IntoScriptModuleReturnValue for ($($name,)+)
        where
            $($name: IntoScriptModuleReturnValue),+
        {
            type Err = anyhow::Error;

            fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
                let mut vec = Vec::new();
                #[allow(non_snake_case)]
                let ($($name,)+) = self;
                $(
                    vec.extend(
                        $name.into_return_values()
                            .map_err(|e| anyhow::Error::from_boxed(e.into()))?
                    );
                )+
                Ok(vec)
            }
        }
    };
}
impl_into_script_module_return_value_for_tuple!(T1);
impl_into_script_module_return_value_for_tuple!(T1, T2);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6, T7);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);

impl IntoScriptModuleReturnValue for Vec<String> {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::StringArray(self)])
    }
}
impl IntoScriptModuleReturnValue for Vec<&str> {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::StringArray(
            self.iter().map(|s| s.to_string()).collect(),
        )])
    }
}
impl IntoScriptModuleReturnValue for Vec<i32> {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::IntArray(self)])
    }
}
impl IntoScriptModuleReturnValue for Vec<f64> {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::FloatArray(self)])
    }
}
impl<T> IntoScriptModuleReturnValue for &[T]
where
    Vec<T>: IntoScriptModuleReturnValue,
    T: Clone,
{
    type Err = <Vec<T> as IntoScriptModuleReturnValue>::Err;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        let vec: Vec<T> = self.to_vec();
        vec.into_return_values()
    }
}

impl IntoScriptModuleReturnValue for std::collections::HashMap<String, i32> {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::IntTable(self)])
    }
}
impl IntoScriptModuleReturnValue for std::collections::HashMap<String, f64> {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::FloatTable(self)])
    }
}
impl IntoScriptModuleReturnValue for std::collections::HashMap<String, String> {
    type Err = std::convert::Infallible;
    fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
        Ok(vec![ScriptModuleReturnValue::StringTable(self)])
    }
}

#[doc(hidden)]
pub mod __table_converter {
    pub trait ToOptionalTableEntry {
        type Value;
        fn to_optional(&self) -> Option<Self::Value>;
    }

    impl<T: Clone> ToOptionalTableEntry for Option<T> {
        type Value = T;
        fn to_optional(&self) -> Option<Self::Value> {
            self.clone()
        }
    }
    impl ToOptionalTableEntry for i32 {
        type Value = i32;
        fn to_optional(&self) -> Option<Self::Value> {
            Some(*self)
        }
    }
    impl ToOptionalTableEntry for f64 {
        type Value = f64;
        fn to_optional(&self) -> Option<Self::Value> {
            Some(*self)
        }
    }
    impl ToOptionalTableEntry for String {
        type Value = String;
        fn to_optional(&self) -> Option<Self::Value> {
            Some(self.clone())
        }
    }
}

#[doc(hidden)]
pub fn __push_return_value<T>(param: &mut crate::module::ScriptModuleCallHandle, value: T)
where
    T: crate::module::IntoScriptModuleReturnValue,
{
    let res = value.push_into(param);
    let _ = res
        .map_err(|e| -> Box<dyn std::error::Error + Send + Sync + 'static> {
            match e {
                crate::module::IntoScriptModuleReturnValueError::PushFailed(e) => Box::new(e),
                crate::module::IntoScriptModuleReturnValueError::ConversionFailed(e) => e.into(),
            }
        })
        .push_into(param);
}
