
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,
        }
    }
}

pub const OD_DI: u16 = 0x6000;

pub const OD_DI_16BIT: u16 = 0x6001;

pub const OD_DI_POLARITY: u16 = 0x6002;

pub const OD_DI_FILTER: u16 = 0x6003;

pub const OD_DI_INTERRUPT: u16 = 0x6005;

pub const OD_DI_INTERRUPT_EDGE: u16 = 0x6006;

pub const OD_DI_32BIT: u16 = 0x6020;

pub const OD_DO: u16 = 0x6200;

pub const OD_DO_16BIT: u16 = 0x6201;

pub const OD_DO_POLARITY: u16 = 0x6202;

pub const OD_DO_ERROR_MODE: u16 = 0x6206;

pub const OD_DO_ERROR_VALUE: u16 = 0x6207;

pub const OD_DO_32BIT: u16 = 0x6220;

pub const OD_AI: u16 = 0x6400;

pub const OD_AI_SI_UNIT: u16 = 0x6420;

pub const OD_AI_GLOBAL_INTERRUPT: u16 = 0x6423;

pub const OD_AI_UPPER_LIMIT: u16 = 0x6424;

pub const OD_AI_LOWER_LIMIT: u16 = 0x6425;

pub const OD_AO: u16 = 0x6411;

pub const OD_AO_32BIT: u16 = 0x6412;

pub const OD_AO_SI_UNIT: u16 = 0x6430;

pub const OD_AO_ERROR_MODE: u16 = 0x6443;

pub const OD_AO_ERROR_VALUE: u16 = 0x6444;

pub struct CiA401 {
    slave: Slave,
}

impl CiA401 {

    pub(crate) fn new(slave: Slave) -> Self {
        Self { slave }
    }

    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,
        }
    }

    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,
        }
    }

    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,
        }
    }

    pub fn set_di_polarity(&self, group: i32, polarity: u8) -> Result<()> {
        self.slave.sdo_write(OD_DI_POLARITY, group as u8, false, &[polarity])
    }

    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,
        }
    }

    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])
    }

    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,
        }
    }

    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,
        }
    }

    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])
    }

    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,
        }
    }

    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())
    }

    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,
        }
    }

    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())
    }

    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,
        }
    }

    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,
        }
    }

    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,
        }
    }

    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,
        }
    }

    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 }])
    }

    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,
        }
    }

    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,
        }
    }

    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())
    }

    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())
    }

    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,
        }
    }

    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())
    }

    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,
        }
    }

    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())
    }

    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])
    }

    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,
        }
    }

    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])
    }

    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,
        }
    }

    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])
    }

    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())
    }

    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,
        }
    }

    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())
    }
}
