//! CiA 401 I/O 模块协议辅助类
//!
//! 实现 CiA 401 (IEC 61131-9) 标准的通用 I/O 设备访问，
//! 包括数字输入/输出、模拟输入/输出的标准对象字典访问。
//! 通过 slave.cia401() 获取实例。

use crate::slave::core::Slave;
use crate::data::error::Result;

// ===================== 错误模式枚举 =====================

/// 通信丢失时的输出错误模式
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CiA401ErrorMode {
    /// 保持当前值
    Hold = 0,
    /// 使用安全值
    SafeValue = 1,
}

impl CiA401ErrorMode {
    /// 从原始值创建
    pub fn from_raw(val: u8) -> Self {
        match val {
            1 => Self::SafeValue,
            _ => Self::Hold,
        }
    }
}

// ===================== CiA 401 标准对象索引 =====================

/// 数字输入 (0x6000)
pub const OD_DI: u16 = 0x6000;
/// 16位数字输入 (0x6001)
pub const OD_DI_16BIT: u16 = 0x6001;
/// 数字输入极性 (0x6002)
pub const OD_DI_POLARITY: u16 = 0x6002;
/// 数字输入滤波使能 (0x6003)
pub const OD_DI_FILTER: u16 = 0x6003;
/// 数字输入中断触发 (0x6005)
pub const OD_DI_INTERRUPT: u16 = 0x6005;
/// 数字输入中断边沿 (0x6006)
pub const OD_DI_INTERRUPT_EDGE: u16 = 0x6006;
/// 32位数字输入 (0x6020)
pub const OD_DI_32BIT: u16 = 0x6020;

/// 数字输出 (0x6200)
pub const OD_DO: u16 = 0x6200;
/// 16位数字输出 (0x6201)
pub const OD_DO_16BIT: u16 = 0x6201;
/// 数字输出极性 (0x6202)
pub const OD_DO_POLARITY: u16 = 0x6202;
/// 数字输出错误模式 (0x6206)
pub const OD_DO_ERROR_MODE: u16 = 0x6206;
/// 数字输出错误值 (0x6207)
pub const OD_DO_ERROR_VALUE: u16 = 0x6207;
/// 32位数字输出 (0x6220)
pub const OD_DO_32BIT: u16 = 0x6220;

/// 模拟输入 (0x6400)
pub const OD_AI: u16 = 0x6400;
/// 模拟输入量程 (0x6420)
pub const OD_AI_SI_UNIT: u16 = 0x6420;
/// 模拟输入全局中断使能 (0x6423)
pub const OD_AI_GLOBAL_INTERRUPT: u16 = 0x6423;
/// 模拟输入中断上限 (0x6424)
pub const OD_AI_UPPER_LIMIT: u16 = 0x6424;
/// 模拟输入中断下限 (0x6425)
pub const OD_AI_LOWER_LIMIT: u16 = 0x6425;

/// 模拟输出 (0x6411)
pub const OD_AO: u16 = 0x6411;
/// 32位模拟输出 (0x6412)
pub const OD_AO_32BIT: u16 = 0x6412;
/// 模拟输出量程 (0x6430)
pub const OD_AO_SI_UNIT: u16 = 0x6430;
/// 模拟输出错误模式 (0x6443)
pub const OD_AO_ERROR_MODE: u16 = 0x6443;
/// 模拟输出错误值 (0x6444)
pub const OD_AO_ERROR_VALUE: u16 = 0x6444;

// ===================== CiA401 主结构体 =====================

/// CiA 401 I/O 模块协议辅助类
///
/// 封装数字/模拟输入输出的标准对象字典访问。
/// 通过 `slave.cia401()` 获取实例。
///
/// # 示例
/// ```no_run
/// let slave = master.slave(1);
/// let io = slave.cia401();
/// // 读取数字输入通道 0
/// let di_state = io.read_di(0);
/// // 写入数字输出通道 0
/// io.write_do(0, true);
/// // 读取模拟输入通道 0
/// let ai_value = io.read_ai(0);
/// ```
pub struct CiA401 {
    slave: Slave,
}

impl CiA401 {
    /// 创建 CiA 401 实例 (内部使用, 通过 slave.cia401() 获取)
    pub(crate) fn new(slave: Slave) -> Self {
        Self { slave }
    }

    // ===================== 数字输入 =====================

    /// 读取指定通道的数字输入状态
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    ///
    /// # 返回
    /// 通道状态（true = 高电平），读取失败时返回 false
    pub fn read_di(&self, channel: i32) -> bool {
        let group = (channel / 8 + 1) as u8;
        let bit = channel % 8;
        match self.slave.sdo_read(OD_DI, group, false) {
            Ok(data) if !data.is_empty() => (data[0] & (1 << bit)) != 0,
            _ => false,
        }
    }

    /// 读取 16 位数字输入组 (0x6001)
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    pub fn read_di16(&self, group: i32) -> u16 {
        match self.slave.sdo_read(OD_DI_16BIT, group as u8, false) {
            Ok(data) if data.len() >= 2 => u16::from_le_bytes([data[0], data[1]]),
            _ => 0,
        }
    }

    /// 读取 32 位数字输入组 (0x6020)
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    pub fn read_di32(&self, group: i32) -> u32 {
        match self.slave.sdo_read(OD_DI_32BIT, group as u8, false) {
            Ok(data) if data.len() >= 4 => u32::from_le_bytes([data[0], data[1], data[2], data[3]]),
            _ => 0,
        }
    }

    /// 设置数字输入极性 (0x6002)，每位对应一个通道，1=反转
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    /// - `polarity`: 极性掩码
    pub fn set_di_polarity(&self, group: i32, polarity: u8) -> Result<()> {
        self.slave.sdo_write(OD_DI_POLARITY, group as u8, false, &[polarity])
    }

    /// 读取数字输入极性 (0x6002)
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    pub fn di_polarity(&self, group: i32) -> u8 {
        match self.slave.sdo_read(OD_DI_POLARITY, group as u8, false) {
            Ok(data) if !data.is_empty() => data[0],
            _ => 0,
        }
    }

    /// 设置数字输入滤波使能 (0x6003)
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    /// - `filter_enable`: 滤波使能掩码
    pub fn set_di_filter(&self, group: i32, filter_enable: u8) -> Result<()> {
        self.slave.sdo_write(OD_DI_FILTER, group as u8, false, &[filter_enable])
    }

    /// 读取数字输入滤波使能 (0x6003)
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    pub fn di_filter(&self, group: i32) -> u8 {
        match self.slave.sdo_read(OD_DI_FILTER, group as u8, false) {
            Ok(data) if !data.is_empty() => data[0],
            _ => 0,
        }
    }

    // ===================== 数字输出 =====================

    /// 读取指定通道的数字输出状态
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    pub fn read_do(&self, channel: i32) -> bool {
        let group = (channel / 8 + 1) as u8;
        let bit = channel % 8;
        match self.slave.sdo_read(OD_DO, group, false) {
            Ok(data) if !data.is_empty() => (data[0] & (1 << bit)) != 0,
            _ => false,
        }
    }

    /// 设置指定通道的数字输出，不影响同组其他通道
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    /// - `state`: 目标状态（true = 高电平）
    pub fn write_do(&self, channel: i32, state: bool) -> Result<()> {
        let group = (channel / 8 + 1) as u8;
        let bit = channel % 8;
        // 先读取当前值，再修改目标位
        let current = match self.slave.sdo_read(OD_DO, group, false) {
            Ok(data) if !data.is_empty() => data[0],
            _ => 0,
        };
        let new_val = if state {
            current | (1 << bit)
        } else {
            current & !(1 << bit)
        };
        self.slave.sdo_write(OD_DO, group, false, &[new_val])
    }

    /// 读取 16 位数字输出组 (0x6201)
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    pub fn read_do16(&self, group: i32) -> u16 {
        match self.slave.sdo_read(OD_DO_16BIT, group as u8, false) {
            Ok(data) if data.len() >= 2 => u16::from_le_bytes([data[0], data[1]]),
            _ => 0,
        }
    }

    /// 写入 16 位数字输出组 (0x6201)
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    /// - `value`: 16 位输出值
    pub fn write_do16(&self, group: i32, value: u16) -> Result<()> {
        self.slave.sdo_write(OD_DO_16BIT, group as u8, false, &value.to_le_bytes())
    }

    /// 读取 32 位数字输出组 (0x6220)
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    pub fn read_do32(&self, group: i32) -> u32 {
        match self.slave.sdo_read(OD_DO_32BIT, group as u8, false) {
            Ok(data) if data.len() >= 4 => u32::from_le_bytes([data[0], data[1], data[2], data[3]]),
            _ => 0,
        }
    }

    /// 写入 32 位数字输出组 (0x6220)
    ///
    /// # 参数
    /// - `group`: 组号（子索引，从 1 开始）
    /// - `value`: 32 位输出值
    pub fn write_do32(&self, group: i32, value: u32) -> Result<()> {
        self.slave.sdo_write(OD_DO_32BIT, group as u8, false, &value.to_le_bytes())
    }

    // ===================== 模拟输入 =====================

    /// 读取模拟输入值，自动适应 16 位和 32 位设备
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    pub fn read_ai(&self, channel: i32) -> i32 {
        let sub = (channel + 1) as u8;
        match self.slave.sdo_read(OD_AI, sub, false) {
            Ok(data) if data.len() >= 4 => {
                i32::from_le_bytes([data[0], data[1], data[2], data[3]])
            }
            Ok(data) if data.len() >= 2 => {
                i16::from_le_bytes([data[0], data[1]]) as i32
            }
            _ => 0,
        }
    }

    /// 读取无符号模拟输入值 (对应 C# ReadAIUnsigned)
    ///
    /// 与 read_ai 不同, 此方法将数据解释为无符号值。
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    pub fn read_ai_unsigned(&self, channel: i32) -> u16 {
        let sub = (channel + 1) as u8;
        match self.slave.sdo_read(OD_AI, sub, false) {
            Ok(data) if data.len() >= 2 => u16::from_le_bytes([data[0], data[1]]),
            _ => 0,
        }
    }

    /// 读取32位无符号模拟输入值 (0x6401)
    pub fn read_ai32_unsigned(&self, channel: i32) -> u32 {
        let sub = (channel + 1) as u8;
        match self.slave.sdo_read(OD_AI, sub, false) {
            Ok(data) if data.len() >= 4 => u32::from_le_bytes([data[0], data[1], data[2], data[3]]),
            _ => 0,
        }
    }

    /// 获取模拟输入全局中断使能状态 (0x6423)
    ///
    /// 对应 C# ReadGlobalInterruptEnable
    pub fn global_interrupt_enable(&self) -> bool {
        match self.slave.sdo_read(OD_AI_GLOBAL_INTERRUPT, 0, false) {
            Ok(data) if !data.is_empty() => data[0] != 0,
            _ => false,
        }
    }

    /// 设置模拟输入全局中断使能 (0x6423)
    ///
    /// 对应 C# WriteGlobalInterruptEnable
    pub fn set_global_interrupt_enable(&self, enabled: bool) -> Result<()> {
        self.slave.sdo_write(OD_AI_GLOBAL_INTERRUPT, 0, false, &[if enabled { 1 } else { 0 }])
    }

    /// 读取模拟输入中断上限 (0x6424)
    ///
    /// 对应 C# ReadAIUpperLimit
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    pub fn ai_upper_limit(&self, channel: i32) -> i32 {
        let sub = (channel + 1) as u8;
        match self.slave.sdo_read(OD_AI_UPPER_LIMIT, sub, false) {
            Ok(data) if data.len() >= 4 => {
                i32::from_le_bytes([data[0], data[1], data[2], data[3]])
            }
            Ok(data) if data.len() >= 2 => {
                i16::from_le_bytes([data[0], data[1]]) as i32
            }
            _ => 0,
        }
    }

    /// 读取模拟输入中断下限 (0x6425)
    ///
    /// 对应 C# ReadAILowerLimit
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    pub fn ai_lower_limit(&self, channel: i32) -> i32 {
        let sub = (channel + 1) as u8;
        match self.slave.sdo_read(OD_AI_LOWER_LIMIT, sub, false) {
            Ok(data) if data.len() >= 4 => {
                i32::from_le_bytes([data[0], data[1], data[2], data[3]])
            }
            Ok(data) if data.len() >= 2 => {
                i16::from_le_bytes([data[0], data[1]]) as i32
            }
            _ => 0,
        }
    }

    /// 设置模拟输入中断上限 (0x6424)
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    /// - `value`: 上限值
    pub fn set_ai_upper_limit(&self, channel: i32, value: i16) -> Result<()> {
        let sub = (channel + 1) as u8;
        self.slave.sdo_write(OD_AI_UPPER_LIMIT, sub, false, &value.to_le_bytes())
    }

    /// 设置模拟输入中断下限 (0x6425)
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    /// - `value`: 下限值
    pub fn set_ai_lower_limit(&self, channel: i32, value: i16) -> Result<()> {
        let sub = (channel + 1) as u8;
        self.slave.sdo_write(OD_AI_LOWER_LIMIT, sub, false, &value.to_le_bytes())
    }

    // ===================== 模拟输出 =====================

    /// 读取 16 位模拟输出值
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    pub fn read_ao(&self, channel: i32) -> i16 {
        let sub = (channel + 1) as u8;
        match self.slave.sdo_read(OD_AO, sub, false) {
            Ok(data) if data.len() >= 2 => i16::from_le_bytes([data[0], data[1]]),
            _ => 0,
        }
    }

    /// 写入 16 位模拟输出值
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    /// - `value`: 16 位输出值
    pub fn write_ao(&self, channel: i32, value: i16) -> Result<()> {
        let sub = (channel + 1) as u8;
        self.slave.sdo_write(OD_AO, sub, false, &value.to_le_bytes())
    }

    /// 读取 32 位模拟输出值 (0x6412)
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    pub fn read_ao32(&self, channel: i32) -> i32 {
        let sub = (channel + 1) as u8;
        match self.slave.sdo_read(OD_AO_32BIT, sub, false) {
            Ok(data) if data.len() >= 4 => {
                i32::from_le_bytes([data[0], data[1], data[2], data[3]])
            }
            _ => 0,
        }
    }

    /// 写入 32 位模拟输出值 (0x6412)
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    /// - `value`: 32 位输出值
    pub fn write_ao32(&self, channel: i32, value: i32) -> Result<()> {
        let sub = (channel + 1) as u8;
        self.slave.sdo_write(OD_AO_32BIT, sub, false, &value.to_le_bytes())
    }

    // ===================== 错误处理 =====================

    /// 设置数字输出错误模式，通信丢失时的输出行为
    ///
    /// # 参数
    /// - `group`: 组号（从 0 开始，对应 DO 通道 0-7, 8-15, ...）
    /// - `mode`: 错误模式
    pub fn set_do_error_mode(&self, group: i32, mode: CiA401ErrorMode) -> Result<()> {
        self.slave.sdo_write(OD_DO_ERROR_MODE, (group + 1) as u8, false, &[mode as u8])
    }

    /// 读取数字输出错误模式 (0x6206)
    ///
    /// # 参数
    /// - `group`: 组号（从 0 开始）
    pub fn do_error_mode(&self, group: i32) -> CiA401ErrorMode {
        match self.slave.sdo_read(OD_DO_ERROR_MODE, (group + 1) as u8, false) {
            Ok(data) if !data.is_empty() => CiA401ErrorMode::from_raw(data[0]),
            _ => CiA401ErrorMode::Hold,
        }
    }

    /// 设置数字输出安全值，通信丢失且错误模式启用时使用
    ///
    /// # 参数
    /// - `group`: 组号（从 0 开始）
    /// - `value`: 安全输出值
    pub fn set_do_error_value(&self, group: i32, value: u8) -> Result<()> {
        self.slave.sdo_write(OD_DO_ERROR_VALUE, (group + 1) as u8, false, &[value])
    }

    /// 读取数字输出安全值 (0x6207)
    ///
    /// # 参数
    /// - `group`: 组号（从 0 开始）
    pub fn do_error_value(&self, group: i32) -> u8 {
        match self.slave.sdo_read(OD_DO_ERROR_VALUE, (group + 1) as u8, false) {
            Ok(data) if !data.is_empty() => data[0],
            _ => 0,
        }
    }

    /// 设置模拟输出错误模式
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    /// - `mode`: 错误模式
    pub fn set_ao_error_mode(&self, channel: i32, mode: CiA401ErrorMode) -> Result<()> {
        self.slave.sdo_write(OD_AO_ERROR_MODE, (channel + 1) as u8, false, &[mode as u8])
    }

    /// 设置模拟输出安全值
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    /// - `value`: 安全输出值
    pub fn set_ao_error_value(&self, channel: i32, value: i16) -> Result<()> {
        self.slave.sdo_write(OD_AO_ERROR_VALUE, (channel + 1) as u8, false, &value.to_le_bytes())
    }

    /// 读取模拟输出错误模式 (0x6443)
    ///
    /// 对应 C# AOErrorMode (读取)
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    pub fn get_ao_error_mode(&self, channel: i32) -> CiA401ErrorMode {
        match self.slave.sdo_read(OD_AO_ERROR_MODE, (channel + 1) as u8, false) {
            Ok(data) if !data.is_empty() => CiA401ErrorMode::from_raw(data[0]),
            _ => CiA401ErrorMode::Hold,
        }
    }

    /// 读取模拟输出安全值 (0x6444)
    ///
    /// 对应 C# AOErrorValue (读取)
    ///
    /// # 参数
    /// - `channel`: 通道号（从 0 开始）
    pub fn get_ao_error_value(&self, channel: i32) -> i16 {
        match self.slave.sdo_read(OD_AO_ERROR_VALUE, (channel + 1) as u8, false) {
            Ok(data) if data.len() >= 2 => i16::from_le_bytes([data[0], data[1]]),
            _ => 0,
        }
    }
}

impl std::fmt::Display for CiA401 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "从站 {}: CiA401 I/O", self.slave.index())
    }
}
