
use crate::data::error::{DarraError, Result};
use crate::utils::ffi;
use std::os::raw::c_void;

pub const VOE_HEADER_SIZE: usize = 6;

#[derive(Debug, Clone)]
pub struct VoEResponse {

    pub vendor_id: u32,

    pub vendor_type: u16,

    pub data: Vec<u8>,
}

impl VoEResponse {

    pub fn to_hex_string(&self) -> String {
        self.data.iter()
            .map(|b| format!("{:02X}", b))
            .collect::<Vec<_>>()
            .join(" ")
    }

    pub fn data_length(&self) -> usize {
        self.data.len()
    }
}

impl std::fmt::Display for VoEResponse {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "VoE Response: VendorID=0x{:08X}, VendorType=0x{:04X}, DataLength={}",
            self.vendor_id, self.vendor_type, self.data.len())
    }
}

pub struct VoEInstance {
    master_index: u16,
    slave_index: u16,

    pub default_timeout_ms: i32,
}

impl VoEInstance {

    pub(crate) fn new(master_index: u16, slave_index: u16) -> Self {
        Self {
            master_index,
            slave_index,
            default_timeout_ms: 500,
        }
    }

    pub fn is_supported(&self) -> bool {
        let proto = unsafe { ffi::GetSlaveMailboxProto(self.master_index, self.slave_index) };
        (proto & 0x20) != 0
    }

    pub fn send(&self, vendor_id: u32, vendor_type: u16, data: &[u8]) -> Result<()> {
        self.send_with_timeout(vendor_id, vendor_type, data, self.default_timeout_ms)
    }

    pub fn send_with_timeout(&self, vendor_id: u32, vendor_type: u16, data: &[u8], timeout_ms: i32) -> Result<()> {
        let ret = unsafe {
            ffi::VOESend(
                self.master_index, self.slave_index,
                vendor_id, vendor_type,
                data.as_ptr(), data.len() as i32,
                timeout_ms * 1000,
            )
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::VoeFailed) }
    }

    pub fn receive(&self) -> Result<VoEResponse> {
        self.receive_with_timeout(self.default_timeout_ms)
    }

    pub fn receive_with_timeout(&self, timeout_ms: i32) -> Result<VoEResponse> {
        let mut vendor_id: u32 = 0;
        let mut vendor_type: u16 = 0;
        let mut data_ptr: *mut c_void = std::ptr::null_mut();
        let mut data_size: i32 = 0;

        let ret = unsafe {
            ffi::VOEReceive(
                self.master_index, self.slave_index,
                &mut vendor_id, &mut vendor_type,
                &mut data_ptr, &mut data_size,
                timeout_ms * 1000,
            )
        };

        if ret != 0 {
            let data = if !data_ptr.is_null() && data_size > 0 {
                let mut buf = vec![0u8; data_size as usize];
                unsafe {
                    std::ptr::copy_nonoverlapping(data_ptr as *const u8, buf.as_mut_ptr(), data_size as usize);
                    ffi::FreeMemory(data_ptr);
                }
                buf
            } else {
                if !data_ptr.is_null() {
                    unsafe { ffi::FreeMemory(data_ptr) };
                }
                Vec::new()
            };

            Ok(VoEResponse { vendor_id, vendor_type, data })
        } else {
            Err(DarraError::VoeFailed)
        }
    }

    pub fn send_and_receive(&self, vendor_id: u32, vendor_type: u16, data: &[u8]) -> Result<VoEResponse> {
        self.send(vendor_id, vendor_type, data)?;
        self.receive()
    }

    pub fn send_raw(&self, frame_data: &[u8]) -> Result<()> {
        self.send_raw_with_timeout(frame_data, self.default_timeout_ms)
    }

    pub fn send_raw_with_timeout(&self, frame_data: &[u8], timeout_ms: i32) -> Result<()> {
        if frame_data.is_empty() {
            return Err(DarraError::InvalidParameter("帧数据不能为空".into()));
        }
        let ret = unsafe {
            ffi::VOESendRaw(
                self.master_index, self.slave_index,
                frame_data.as_ptr(), frame_data.len() as i32,
                timeout_ms * 1000,
            )
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::VoeFailed) }
    }

    pub fn receive_raw(&self) -> Result<Vec<u8>> {
        self.receive_raw_with_timeout(self.default_timeout_ms)
    }

    pub fn receive_raw_with_timeout(&self, timeout_ms: i32) -> Result<Vec<u8>> {
        let mut frame_ptr: *mut c_void = std::ptr::null_mut();
        let mut frame_size: i32 = 0;

        let ret = unsafe {
            ffi::VOEReceiveRaw(
                self.master_index, self.slave_index,
                &mut frame_ptr, &mut frame_size,
                timeout_ms * 1000,
            )
        };

        if ret != 0 && !frame_ptr.is_null() && frame_size > 0 {
            let mut data = vec![0u8; frame_size as usize];
            unsafe {
                std::ptr::copy_nonoverlapping(frame_ptr as *const u8, data.as_mut_ptr(), frame_size as usize);
                ffi::FreeMemory(frame_ptr);
            }
            Ok(data)
        } else {
            if !frame_ptr.is_null() {
                unsafe { ffi::FreeMemory(frame_ptr) };
            }
            Err(DarraError::VoeFailed)
        }
    }

    pub fn send_raw_and_receive(&self, frame_data: &[u8]) -> Result<Vec<u8>> {
        self.send_raw(frame_data)?;
        self.receive_raw()
    }

    pub fn build_frame(vendor_id: u32, vendor_type: u16, data: &[u8]) -> Vec<u8> {
        let mut frame = Vec::with_capacity(VOE_HEADER_SIZE + data.len());

        frame.push((vendor_id & 0xFF) as u8);
        frame.push(((vendor_id >> 8) & 0xFF) as u8);
        frame.push(((vendor_id >> 16) & 0xFF) as u8);
        frame.push(((vendor_id >> 24) & 0xFF) as u8);

        frame.push((vendor_type & 0xFF) as u8);
        frame.push(((vendor_type >> 8) & 0xFF) as u8);

        frame.extend_from_slice(data);
        frame
    }

    pub fn parse_frame(frame: &[u8]) -> Option<VoEResponse> {
        if frame.len() < VOE_HEADER_SIZE {
            return None;
        }
        let vendor_id = u32::from_le_bytes([frame[0], frame[1], frame[2], frame[3]]);
        let vendor_type = u16::from_le_bytes([frame[4], frame[5]]);
        let data = frame[VOE_HEADER_SIZE..].to_vec();
        Some(VoEResponse { vendor_id, vendor_type, data })
    }
}

impl VoEInstance {

    pub fn send_blocking(
        master_index: u16,
        slave_index: u16,
        vendor_id: u32,
        vendor_type: u16,
        data: Vec<u8>,
        timeout_ms: i32,
    ) -> std::thread::JoinHandle<Result<()>> {
        std::thread::spawn(move || {
            let voe = VoEInstance::new(master_index, slave_index);
            voe.send_with_timeout(vendor_id, vendor_type, &data, timeout_ms)
        })
    }

    pub fn receive_blocking(
        master_index: u16,
        slave_index: u16,
        timeout_ms: i32,
    ) -> std::thread::JoinHandle<Result<VoEResponse>> {
        std::thread::spawn(move || {
            let voe = VoEInstance::new(master_index, slave_index);
            voe.receive_with_timeout(timeout_ms)
        })
    }

    pub fn send_raw_blocking(
        master_index: u16,
        slave_index: u16,
        frame_data: Vec<u8>,
        timeout_ms: i32,
    ) -> std::thread::JoinHandle<Result<()>> {
        std::thread::spawn(move || {
            let voe = VoEInstance::new(master_index, slave_index);
            voe.send_raw_with_timeout(&frame_data, timeout_ms)
        })
    }
}

#[cfg(feature = "async-tokio")]
impl VoEInstance {

    pub async fn send_async(&self, vendor_id: u32, vendor_type: u16, data: Vec<u8>) -> Result<()> {
        let master = self.master_index;
        let slave = self.slave_index;
        let timeout_ms = self.default_timeout_ms;
        tokio::task::spawn_blocking(move || {
            let voe = VoEInstance::new(master, slave);
            voe.send_with_timeout(vendor_id, vendor_type, &data, timeout_ms)
        })
        .await
        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
    }

    pub async fn receive_async(&self) -> Result<VoEResponse> {
        let master = self.master_index;
        let slave = self.slave_index;
        let timeout_ms = self.default_timeout_ms;
        tokio::task::spawn_blocking(move || {
            let voe = VoEInstance::new(master, slave);
            voe.receive_with_timeout(timeout_ms)
        })
        .await
        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
    }

    pub async fn send_raw_async(&self, frame_data: Vec<u8>) -> Result<()> {
        let master = self.master_index;
        let slave = self.slave_index;
        let timeout_ms = self.default_timeout_ms;
        tokio::task::spawn_blocking(move || {
            let voe = VoEInstance::new(master, slave);
            voe.send_raw_with_timeout(&frame_data, timeout_ms)
        })
        .await
        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
    }
}

#[derive(Debug, Clone)]
pub struct VoENotificationEventArgs {
    pub slave_index: u16,
    pub vendor_id: u32,
    pub vendor_type: u16,
    pub data: Vec<u8>,

    pub timestamp_us: u64,
}

impl VoENotificationEventArgs {
    pub fn new(slave_index: u16, vendor_id: u32, vendor_type: u16, data: Vec<u8>) -> Self {
        let ts = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map(|d| d.as_micros() as u64)
            .unwrap_or(0);
        Self { slave_index, vendor_id, vendor_type, data, timestamp_us: ts }
    }
}

pub trait VoENotificationListener: Send + Sync + 'static {
    fn on_notification(&self, args: &VoENotificationEventArgs);
}

impl<F> VoENotificationListener for F
where
    F: Fn(&VoENotificationEventArgs) + Send + Sync + 'static,
{
    fn on_notification(&self, args: &VoENotificationEventArgs) { self(args); }
}

type VoENotificationHook =
    std::sync::Arc<dyn Fn(&VoENotificationEventArgs) + Send + Sync + 'static>;

fn voe_listener_registry()
    -> &'static std::sync::Mutex<std::collections::HashMap<u16, VoENotificationHook>>
{
    static REG: std::sync::OnceLock<
        std::sync::Mutex<std::collections::HashMap<u16, VoENotificationHook>>
    > = std::sync::OnceLock::new();
    REG.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))
}

fn voe_subscription_registry()
    -> &'static std::sync::Mutex<std::collections::HashMap<u16, i32>>
{
    static SUB: std::sync::OnceLock<
        std::sync::Mutex<std::collections::HashMap<u16, i32>>
    > = std::sync::OnceLock::new();
    SUB.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))
}

unsafe extern "C" fn voe_notification_trampoline(
    slave: u16,
    vendor_id: u32,
    vendor_type: u16,
    data: *const u8,
    data_size: u32,
    user_data: *mut std::ffi::c_void,
) {
    let master_index = user_data as usize as u16;
    let payload: Vec<u8> = if data.is_null() || data_size == 0 {
        Vec::new()
    } else {
        unsafe { std::slice::from_raw_parts(data, data_size as usize) }.to_vec()
    };
    let cb_opt = match voe_listener_registry().lock() {
        Ok(g) => g.get(&master_index).cloned(),
        Err(p) => p.into_inner().get(&master_index).cloned(),
    };
    if let Some(cb) = cb_opt {
        let args = VoENotificationEventArgs::new(slave, vendor_id, vendor_type, payload);
        cb(&args);
    }
}

impl VoEInstance {

    pub fn start_notification_listener<F>(&self, listener: F) -> bool
    where
        F: Fn(&VoENotificationEventArgs) + Send + Sync + 'static,
    {

        match voe_listener_registry().lock() {
            Ok(mut g) => { g.insert(self.master_index, std::sync::Arc::new(listener)); }
            Err(p) => { p.into_inner().insert(self.master_index, std::sync::Arc::new(listener)); }
        }

        let started = unsafe { ffi::VOEStartNotificationListener(self.master_index) } != 0;
        if !started {
            if let Ok(mut g) = voe_listener_registry().lock() {
                g.remove(&self.master_index);
            }
            return false;
        }

        let idx = unsafe {
            ffi::VOERegisterNotification(
                self.slave_index, 0, 0,
                Some(voe_notification_trampoline),
                self.master_index as usize as *mut std::ffi::c_void,
            )
        };
        if idx >= 0 {
            if let Ok(mut s) = voe_subscription_registry().lock() {
                s.insert(self.master_index, idx);
            }
        }
        true
    }

    pub fn stop_notification_listener(&self) {
        let idx = match voe_subscription_registry().lock() {
            Ok(mut s) => s.remove(&self.master_index),
            Err(p) => p.into_inner().remove(&self.master_index),
        };
        if let Some(i) = idx {

            unsafe { let _ = ffi::VOEUnregisterNotification(i); }
        }
        if let Ok(mut g) = voe_listener_registry().lock() {
            g.remove(&self.master_index);
        }
    }

    pub fn stop_all_listeners() {
        unsafe { let _ = ffi::VOEStopNotificationListener(); }
        if let Ok(mut g) = voe_listener_registry().lock() { g.clear(); }
        if let Ok(mut s) = voe_subscription_registry().lock() { s.clear(); }
    }

    pub fn is_listener_running() -> bool {
        unsafe { ffi::VOEIsNotificationListening() != 0 }
    }
}

impl crate::abstractions::MailboxProtocol for VoEInstance {

    fn protocol_type(&self) -> u8 { 0x0F }
    fn protocol_name(&self) -> &'static str { "VoE" }

    fn is_supported(&self) -> bool {
        VoEInstance::is_supported(self)
    }

    fn statistics(&self) -> crate::abstractions::MailboxStatistics {
        let mut stats = ffi::EcMbxStatsC::default();
        let rc = unsafe {
            ffi::mbx_get_stats_by_master(
                self.master_index, self.slave_index, 0x0F, &mut stats,
            )
        };
        if rc == 1 {
            stats.into()
        } else {
            crate::abstractions::MailboxStatistics::empty()
        }
    }

    fn reset_statistics(&self) {
        unsafe {
            ffi::mbx_reset_stats_by_master(self.master_index, self.slave_index, 0x0F);
        }
    }
}
