//! 从站 PDO 实例封装
//!
//! 对应 C# Slave/SlavePdo.cs
//! 将 PDO 相关功能集中到从站 PDO 实例中。

use crate::utils::ffi;

/// 从站 PDO 封装类
///
/// 对应 C# SlavePdo
/// 提供输入/输出数据的字节数组读写。
pub struct SlavePdo {
    master_index: u16,
    slave_index: u16,
}

impl SlavePdo {
    /// 创建新的 PDO 实例
    pub fn new(master_index: u16, slave_index: u16) -> Self {
        Self {
            master_index,
            slave_index,
        }
    }

    /// 读取输入 PDO 数据 (TxPDO, 从站到主站) - 新接口
    pub fn read_input_u8(&self, offset: u32) -> u8 {
        unsafe { ffi::PDOReadInputU8(self.master_index, self.slave_index, offset) }
    }

    /// 读取输入 i16
    pub fn read_input_i16(&self, offset: u32) -> i16 {
        unsafe { ffi::PDOReadInputI16(self.master_index, self.slave_index, offset) }
    }

    /// 读取输入 u16
    pub fn read_input_u16(&self, offset: u32) -> u16 {
        unsafe { ffi::PDOReadInputU16(self.master_index, self.slave_index, offset) }
    }

    /// 读取输入 i32
    pub fn read_input_i32(&self, offset: u32) -> i32 {
        unsafe { ffi::PDOReadInputI32(self.master_index, self.slave_index, offset) }
    }

    /// 读取输入 u32
    pub fn read_input_u32(&self, offset: u32) -> u32 {
        unsafe { ffi::PDOReadInputU32(self.master_index, self.slave_index, offset) }
    }

    /// 读取输入 f32
    pub fn read_input_f32(&self, offset: u32) -> f32 {
        unsafe { ffi::PDOReadInputF32(self.master_index, self.slave_index, offset) }
    }

    /// 写入输出 u8
    pub fn write_output_u8(&self, offset: u32, value: u8) -> bool {
        unsafe { ffi::PDOWriteOutputU8(self.master_index, self.slave_index, offset, value) != 0 }
    }

    /// 写入输出 i16
    pub fn write_output_i16(&self, offset: u32, value: i16) -> bool {
        unsafe { ffi::PDOWriteOutputI16(self.master_index, self.slave_index, offset, value) != 0 }
    }

    /// 写入输出 u16
    pub fn write_output_u16(&self, offset: u32, value: u16) -> bool {
        unsafe { ffi::PDOWriteOutputU16(self.master_index, self.slave_index, offset, value) != 0 }
    }

    /// 写入输出 i32
    pub fn write_output_i32(&self, offset: u32, value: i32) -> bool {
        unsafe { ffi::PDOWriteOutputI32(self.master_index, self.slave_index, offset, value) != 0 }
    }

    /// 写入输出 u32
    pub fn write_output_u32(&self, offset: u32, value: u32) -> bool {
        unsafe { ffi::PDOWriteOutputU32(self.master_index, self.slave_index, offset, value) != 0 }
    }

    /// 写入输出 f32
    pub fn write_output_f32(&self, offset: u32, value: f32) -> bool {
        unsafe { ffi::PDOWriteOutputF32(self.master_index, self.slave_index, offset, value) != 0 }
    }

    /// PDO 统计信息
    pub fn stats(&self) -> Option<*const std::os::raw::c_void> {
        unsafe {
            let ptr = ffi::GetPDOStats(self.master_index, self.slave_index);
            if ptr.is_null() { None } else { Some(ptr) }
        }
    }

    /// 重置 PDO 统计信息
    pub fn reset_stats(&self) {
        unsafe { ffi::ResetPDOStats(self.master_index, self.slave_index); }
    }

    // ===================== 零拷贝指针访问 =====================

    /// 获取输入数据的直接指针（零拷贝访问）
    ///
    /// 警告: 仅在 PDO 循环中使用, 指针在 IOmap 重新映射后会失效
    pub fn get_input_data_pointer(&self) -> *mut u8 {
        super::pdo::get_input_data_pointer(self.master_index, self.slave_index)
    }

    /// 获取输出数据的直接指针（零拷贝访问）
    ///
    /// 警告: 仅在 PDO 循环中使用, 指针在 IOmap 重新映射后会失效
    pub fn get_output_data_pointer(&self) -> *mut u8 {
        super::pdo::get_output_data_pointer(self.master_index, self.slave_index)
    }

    // ===================== 便捷拷贝访问 =====================

    /// 获取输入 PDO 数据的拷贝 (便捷方法)
    ///
    /// 对应 C# SlavePdo.Inputs / Python inputs 属性
    /// 注意: 此方法会进行 memcpy, 零拷贝请使用 input_slice()
    pub fn inputs(&self) -> Vec<u8> {
        let (ptr, size) = self.inputs_mapping_info();
        if ptr.is_null() || size == 0 {
            return Vec::new();
        }
        let mut buf = vec![0u8; size];
        unsafe { std::ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), size); }
        buf
    }

    /// 获取输出 PDO 数据的拷贝 (便捷方法)
    ///
    /// 对应 C# SlavePdo.Outputs / Python outputs 属性
    /// 注意: 此方法会进行 memcpy, 零拷贝请使用 output_slice()
    pub fn outputs(&self) -> Vec<u8> {
        let (ptr, size) = self.outputs_mapping_info();
        if ptr.is_null() || size == 0 {
            return Vec::new();
        }
        let mut buf = vec![0u8; size];
        unsafe { std::ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), size); }
        buf
    }

    /// 写入输出 PDO 数据 (便捷方法, 整体覆盖)
    ///
    /// 对应 C# SlavePdo.WriteOutputs(byte[])
    pub fn write_outputs(&self, data: &[u8]) {
        let (ptr, size) = self.outputs_mapping_info();
        if ptr.is_null() || size == 0 { return; }
        let len = data.len().min(size);
        unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), ptr, len); }
    }

    // ===================== 直接读写 =====================

    /// 读取输入数据（零拷贝，直接从 IOmap）
    ///
    /// 返回实际读取的字节数
    pub fn read_input_data_direct(&self, buffer: &mut [u8], offset: usize, length: usize) -> usize {
        super::pdo::read_input_data_direct(self.master_index, self.slave_index, buffer, offset, length)
    }

    /// 写入输出数据（零拷贝，直接到 IOmap）
    ///
    /// 返回实际写入的字节数
    pub fn write_output_data_direct(&self, data: &[u8], offset: usize, length: usize) -> usize {
        super::pdo::write_output_data_direct(self.master_index, self.slave_index, data, offset, length)
    }

    // ===================== 零拷贝切片访问 =====================

    /// 获取输入数据切片（零拷贝）
    ///
    /// 对应 C# InputsMapping / Python get_input_memoryview
    ///
    /// # 安全性
    /// 返回的切片直接指向 IOmap 内存
    pub unsafe fn input_slice(&self) -> &[u8] {
        super::pdo::input_slice(self.master_index, self.slave_index)
    }

    /// 获取输出数据切片（零拷贝）
    ///
    /// 对应 C# OutputsMapping / Python get_output_memoryview
    ///
    /// # 安全性
    /// 返回的切片直接指向 IOmap 内存
    pub unsafe fn output_slice(&self) -> &mut [u8] {
        super::pdo::output_slice(self.master_index, self.slave_index)
    }

    // ===================== PDO 映射信息查询 =====================

    /// 获取输入 PDO 映射信息
    ///
    /// 返回 (指针地址, 数据大小) 元组
    pub fn inputs_mapping_info(&self) -> (*mut u8, usize) {
        let mut out_size: i32 = 0;
        let mut out_ptr: *mut u8 = std::ptr::null_mut();
        let mut in_size: i32 = 0;
        let mut in_ptr: *mut u8 = std::ptr::null_mut();
        unsafe {
            ffi::GetIO(self.master_index, self.slave_index,
                       &mut out_size, &mut out_ptr,
                       &mut in_size, &mut in_ptr);
        }
        (in_ptr, in_size.max(0) as usize)
    }

    /// 获取输出 PDO 映射信息
    ///
    /// 返回 (指针地址, 数据大小) 元组
    pub fn outputs_mapping_info(&self) -> (*mut u8, usize) {
        let mut out_size: i32 = 0;
        let mut out_ptr: *mut u8 = std::ptr::null_mut();
        let mut in_size: i32 = 0;
        let mut in_ptr: *mut u8 = std::ptr::null_mut();
        unsafe {
            ffi::GetIO(self.master_index, self.slave_index,
                       &mut out_size, &mut out_ptr,
                       &mut in_size, &mut in_ptr);
        }
        (out_ptr, out_size.max(0) as usize)
    }

    /// 获取输入切片映射（偏移访问）
    ///
    /// 对应 C# InputsSliceMapping
    pub unsafe fn inputs_slice_mapping(&self, offset: usize, size: usize) -> &[u8] {
        let (ptr, total) = self.inputs_mapping_info();
        if ptr.is_null() || offset + size > total {
            return &[];
        }
        std::slice::from_raw_parts(ptr.add(offset), size)
    }

    /// 获取输出切片映射（偏移访问）
    ///
    /// 对应 C# OutputsSliceMapping
    pub unsafe fn outputs_slice_mapping(&self, offset: usize, size: usize) -> &mut [u8] {
        let (ptr, total) = self.outputs_mapping_info();
        if ptr.is_null() || offset + size > total {
            return &mut [];
        }
        std::slice::from_raw_parts_mut(ptr.add(offset), size)
    }

    // ===================== 结构体绑定 =====================

    /// 将 PDO 映射绑定到用户结构体指针 (对应 C# BindPdoStruct)
    ///
    /// 直接将 IOmap 中的 PDO 数据区视为指定的 T 类型,
    /// 实现零拷贝的结构化访问。
    ///
    /// # 安全性
    /// - T 必须是 #[repr(C)] 或 #[repr(packed)] 结构体
    /// - IOmap 重新映射后指针会失效
    /// - 调用者必须确保 T 的大小不超过 PDO 数据区
    pub unsafe fn bind_pdo_struct<T>(&self, is_input: bool) -> Option<*mut T> {
        let (ptr, size) = if is_input {
            self.inputs_mapping_info()
        } else {
            self.outputs_mapping_info()
        };
        if ptr.is_null() || size < std::mem::size_of::<T>() {
            return None;
        }
        Some(ptr as *mut T)
    }

    // ===================== PDO 监控控制 =====================

    /// 启动 PDO 监控
    ///
    /// 对应 C# PDOManager.StartMonitoring
    pub fn start_monitoring(&self) {
        unsafe { ffi::EnablePDOMonitoring(1); }
    }

    /// 停止 PDO 监控
    ///
    /// 对应 C# PDOManager.StopMonitoring
    pub fn stop_monitoring(&self) {
        unsafe { ffi::EnablePDOMonitoring(0); }
    }

    /// 查询 PDO 监控是否启用
    pub fn is_monitoring_enabled(&self) -> bool {
        unsafe { ffi::IsPDOMonitoringEnabled() != 0 }
    }

    // ===================== PDO 映射查询 =====================

    /// 查询指定 SM 索引的 PDO 映射信息
    ///
    /// 对应 C# GetPDOMapping
    ///
    /// # 参数
    /// - `sm_index`: SyncManager 索引 (通常 0x1A00 为输入, 0x1600 为输出)
    ///
    /// # 返回
    /// 映射数据的原始字节和条目数量
    pub fn get_pdo_mapping(&self, sm_index: u16) -> Option<(Vec<u8>, u32)> {
        let mut buffer = vec![0u8; 512];
        let mut mapping_count: u32 = 0;
        let ret = unsafe {
            ffi::GetPDOMapping(
                self.master_index, self.slave_index, sm_index,
                buffer.as_mut_ptr() as *mut std::os::raw::c_void,
                buffer.len() as u32, &mut mapping_count,
            )
        };
        if ret != 0 && mapping_count > 0 {
            buffer.truncate((mapping_count * 8) as usize); // 每条映射 8 字节
            Some((buffer, mapping_count))
        } else {
            None
        }
    }

    // ===================== IOmap 互斥锁 =====================

    /// 锁定 IOmap (对应 C# LockIOmap)
    ///
    /// 在多线程访问 PDO 数据时使用，防止数据竞争。
    /// 必须与 unlock_iomap 配对使用。
    pub fn lock_iomap(&self) {
        unsafe { ffi::LockIOmap(self.master_index); }
    }

    /// 解锁 IOmap (对应 C# UnlockIOmap)
    pub fn unlock_iomap(&self) {
        unsafe { ffi::UnlockIOmap(self.master_index); }
    }

    /// 设置互斥保护模式 (对应 C# SetMutexProtection)
    ///
    /// 启用后，所有 PDO 读写操作自动加锁。
    pub fn set_mutex_protection(&self, enabled: bool) {
        unsafe { ffi::SetMutexProtection(self.master_index, if enabled { 1 } else { 0 }); }
    }

    /// 获取互斥保护模式状态
    pub fn get_mutex_protection(&self) -> bool {
        unsafe { ffi::GetMutexProtection(self.master_index) != 0 }
    }

    // ===================== 直接 PDO 读写 (按索引) =====================

    /// 按 PDO 索引直接读取数据 (对应 C# PDOReadDirect)
    ///
    /// # 参数
    /// - `pdo_index`: PDO 对象索引
    /// - `size`: 要读取的字节数
    ///
    /// # 返回
    /// 读取的数据, 失败返回 None
    pub fn read_direct(&self, pdo_index: u16, size: u32) -> Option<Vec<u8>> {
        let mut buffer = vec![0u8; size as usize];
        let mut bytes_read: u32 = 0;
        let ret = unsafe {
            ffi::PDOReadDirect(
                self.master_index, self.slave_index, pdo_index,
                buffer.as_mut_ptr() as *mut std::os::raw::c_void,
                size, &mut bytes_read,
            )
        };
        if ret != 0 && bytes_read > 0 {
            buffer.truncate(bytes_read as usize);
            Some(buffer)
        } else {
            None
        }
    }

    /// 按 PDO 索引直接写入数据 (对应 C# PDOWriteDirect)
    ///
    /// # 参数
    /// - `pdo_index`: PDO 对象索引
    /// - `data`: 要写入的数据
    ///
    /// # 返回
    /// 是否成功
    pub fn write_direct(&self, pdo_index: u16, data: &[u8]) -> bool {
        let ret = unsafe {
            ffi::PDOWriteDirect(
                self.master_index, self.slave_index, pdo_index,
                data.as_ptr() as *const std::os::raw::c_void,
                data.len() as u32,
            )
        };
        ret != 0
    }

    // ===================== 字符串 PDO 读写 =====================

    /// 读取字符串型 PDO 输入
    ///
    /// 通过逐字节读取 PDO 输入数据区实现字符串读取。
    ///
    /// # 参数
    /// - `offset`: 在输入数据区的字节偏移
    /// - `max_len`: 最大字符串长度
    pub fn read_string(&self, offset: u32, max_len: u32) -> String {
        // 逐字节读取, 遇到 0 终止
        let mut buf = Vec::with_capacity(max_len as usize);
        for i in 0..max_len {
            let b = unsafe { ffi::PDOReadInputU8(self.master_index, self.slave_index, offset + i) };
            if b == 0 { break; }
            buf.push(b);
        }
        // [2026-05-08 修复乱码] PDO 输入字符串走多编码兜底 (UTF-8/ASCII/Latin-1)
        crate::utils::help::decode_ethercat_string(&buf)
    }

    /// 写入字符串型 PDO 输出
    ///
    /// 通过逐字节写入 PDO 输出数据区实现字符串写入。
    ///
    /// # 参数
    /// - `offset`: 在输出数据区的字节偏移
    /// - `value`: 要写入的字符串
    /// - `max_len`: 最大字符串长度
    pub fn write_string(&self, offset: u32, value: &str, max_len: u32) -> bool {
        let bytes = value.as_bytes();
        let write_len = bytes.len().min(max_len as usize);
        for i in 0..write_len {
            let ret = unsafe {
                ffi::PDOWriteOutputU8(self.master_index, self.slave_index, offset + i as u32, bytes[i])
            };
            if ret == 0 { return false; }
        }
        // 写入终止符
        if write_len < max_len as usize {
            unsafe {
                ffi::PDOWriteOutputU8(self.master_index, self.slave_index, offset + write_len as u32, 0);
            };
        }
        true
    }

    // ===================== I64 PDO 读写 =====================

    /// 读取输入 i64 值
    ///
    /// 通过两次 i32 读取组合为 i64。
    pub fn read_input_i64(&self, offset: u32) -> i64 {
        let low = unsafe { ffi::PDOReadInputU32(self.master_index, self.slave_index, offset) };
        let high = unsafe { ffi::PDOReadInputU32(self.master_index, self.slave_index, offset + 4) };
        ((high as i64) << 32) | (low as i64)
    }

    /// 写入输出 i64 值
    ///
    /// 通过两次 i32 写入实现 i64 写入。
    pub fn write_output_i64(&self, offset: u32, value: i64) -> bool {
        let low = value as u32;
        let high = (value >> 32) as u32;
        unsafe {
            ffi::PDOWriteOutputU32(self.master_index, self.slave_index, offset, low);
            ffi::PDOWriteOutputU32(self.master_index, self.slave_index, offset + 4, high);
        }
        true
    }

    // ===================== 组周期分频器 =====================

    /// 获取组的周期分频器 (对应 C# GroupCycleDivider)
    ///
    /// 分频器值表示该组每 N 个主周期执行一次 PDO 交换。
    pub fn group_cycle_divider(&self, group: u8) -> u8 {
        unsafe { ffi::GetGroupCycleDivider(self.master_index, group) }
    }

    /// 设置组的周期分频器 (对应 C# SetGroupCycleDivider)
    ///
    /// # 参数
    /// - `group`: 组号
    /// - `divider`: 分频值 (1=每个周期, 2=每两个周期, ...)
    pub fn set_group_cycle_divider(&self, group: u8, divider: u8) -> bool {
        unsafe { ffi::SetGroupCycleDivider(self.master_index, group, divider) != 0 }
    }

    // ===================== PDO 丢帧统计 =====================

    /// 获取 PDO 丢帧统计 (对应 C# FrameLossStats)
    ///
    /// # 参数
    /// - `group`: 组号
    ///
    /// # 返回
    /// (总丢帧数, 连续丢帧数, 最大连续丢帧数)
    pub fn frame_loss_stats(&self, group: u8) -> (u32, u32, u32) {
        let mut total_lost: u32 = 0;
        let mut consecutive_lost: u32 = 0;
        let mut max_consecutive_lost: u32 = 0;
        unsafe {
            ffi::GetPDOFrameLossStats(
                self.master_index, group,
                &mut total_lost, &mut consecutive_lost, &mut max_consecutive_lost,
            );
        }
        (total_lost, consecutive_lost, max_consecutive_lost)
    }

    /// 重置 PDO 丢帧统计
    pub fn reset_frame_loss_stats(&self, group: u8) {
        unsafe { ffi::ResetPDOFrameLossStats(self.master_index, group); }
    }
}
