use super::*;
use nyash_rust::{
    box_trait::{NyashBox, StringBox},
    runtime::{host_handles as handles, plugin_loader_v2::make_plugin_box_v2},
};
use std::sync::Arc;

unsafe extern "C" fn fake_i32(
    _t: u32,
    _m: u32,
    _i: u32,
    _a: *const u8,
    _al: usize,
    res: *mut u8,
    len: *mut usize,
) -> i32 {
    let mut buf = Vec::new();
    buf.extend_from_slice(&1u16.to_le_bytes());
    buf.extend_from_slice(&1u16.to_le_bytes());
    buf.push(2);
    buf.push(0);
    buf.extend_from_slice(&4u16.to_le_bytes());
    buf.extend_from_slice(&123i32.to_le_bytes());
    if res.is_null() || len.is_null() || unsafe { *len } < buf.len() {
        unsafe {
            if !len.is_null() {
                *len = buf.len();
            }
        }
        return -1;
    }
    unsafe {
        std::ptr::copy_nonoverlapping(buf.as_ptr(), res, buf.len());
        *len = buf.len();
    }
    0
}

unsafe extern "C" fn fake_str(
    _t: u32,
    _m: u32,
    _i: u32,
    _a: *const u8,
    _al: usize,
    res: *mut u8,
    len: *mut usize,
) -> i32 {
    let s = b"hi";
    let mut buf = Vec::new();
    buf.extend_from_slice(&1u16.to_le_bytes());
    buf.extend_from_slice(&1u16.to_le_bytes());
    buf.push(7);
    buf.push(0);
    buf.extend_from_slice(&(s.len() as u16).to_le_bytes());
    buf.extend_from_slice(s);
    if res.is_null() || len.is_null() || unsafe { *len } < buf.len() {
        unsafe {
            if !len.is_null() {
                *len = buf.len();
            }
        }
        return -1;
    }
    unsafe {
        std::ptr::copy_nonoverlapping(buf.as_ptr(), res, buf.len());
        *len = buf.len();
    }
    0
}

#[test]
fn decode_i32_and_string_returns() {
    let pb = make_plugin_box_v2("Dummy".into(), 1, 1, fake_i32);
    let arc: Arc<dyn NyashBox> = Arc::new(pb);
    let handle = handles::to_handle_arc(arc) as i64;
    let val = nyash_plugin_invoke3_tagged_i64(1, 0, 0, handle, 0, 0, 0, 0, 0, 0, 0, 0);
    assert_eq!(val, 123);

    let pb = make_plugin_box_v2("Dummy".into(), 1, 2, fake_str);
    let arc: Arc<dyn NyashBox> = Arc::new(pb);
    let handle = handles::to_handle_arc(arc) as i64;
    let h = nyash_plugin_invoke3_tagged_i64(1, 0, 0, handle, 0, 0, 0, 0, 0, 0, 0, 0);
    assert!(h > 0);
    let obj = handles::get(h as u64).unwrap();
    let sb = obj.as_any().downcast_ref::<StringBox>().unwrap();
    assert_eq!(sb.value, "hi");
}
