
use crate::utils::ffi;
use crate::master::core::EtherCATMaster;

use std::sync::atomic::{AtomicU32, Ordering};

static INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(0);

pub const VENDOR_ID: u32 = 0x00001164;

pub const PRODUCT_CODE: u32 = 0x00000001;

pub struct MasterObjectDictionary<'a> {
    master: &'a EtherCATMaster,

    serial_number: u32,
}

impl<'a> MasterObjectDictionary<'a> {

    pub(crate) fn new(master: &'a EtherCATMaster) -> Self {
        let serial_number = INSTANCE_COUNTER.fetch_add(1, Ordering::SeqCst);
        Self { master, serial_number }
    }

    pub fn vendor_id(&self) -> u32 {
        VENDOR_ID
    }

    pub fn product_code(&self) -> u32 {
        PRODUCT_CODE
    }

    pub fn revision_number(&self) -> u32 {
        match EtherCATMaster::dll_version() {
            Some((major, minor, _, _)) => ((major as u32) << 16) | (minor as u32),
            None => 0,
        }
    }

    pub fn serial_number(&self) -> u32 {
        self.serial_number
    }

    pub fn read_object(&self, index: u16, subindex: u8) -> Option<Vec<u8>> {
        match index {

            0x1000 => {
                Some(0x000011A4u32.to_le_bytes().to_vec())
            }

            0x1008 => {
                Some(b"Darra EtherCAT Master".to_vec())
            }

            0x1009 => {
                Some(b"1.0".to_vec())
            }

            0x100A => {
                match EtherCATMaster::dll_version() {
                    Some((major, minor, patch, build)) => {
                        let ver = format!("{}.{}.{}.{}", major, minor, patch, build);
                        Some(ver.into_bytes())
                    }
                    None => Some(b"0.0.0.0".to_vec()),
                }
            }

            0x1018 => {
                self.read_identity_object(subindex)
            }

            0x8000..=0x8FFF => {
                let slave_idx = index & 0x0FFF;
                self.read_slave_config_data(slave_idx, subindex)
            }

            0x9000..=0x9FFF => {
                let slave_idx = index & 0x0FFF;
                self.read_slave_info_data(slave_idx, subindex)
            }

            0xA000..=0xAFFF => {
                let slave_idx = index & 0x0FFF;
                self.read_slave_diag_data(slave_idx, subindex)
            }

            0xF002 => {
                self.read_detect_modules(subindex)
            }

            0xF120 => {
                self.read_master_diag_data(subindex)
            }

            0xF200 => {

                Some(vec![0u8; 4])
            }
            _ => None,
        }
    }

    pub fn write_object(&self, index: u16, subindex: u8, data: &[u8]) -> bool {
        match index {

            0xF002 => {

                subindex == 1 && !data.is_empty()
            }

            0xF200 => {
                !data.is_empty()
            }

            0xA000..=0xAFFF => {

                subindex == 0 && !data.is_empty()
            }

            _ => false,
        }
    }

    pub fn object_name(&self, index: u16) -> &str {
        match index {
            0x1000 => "Device Type",
            0x1008 => "Manufacturer Device Name",
            0x1009 => "Manufacturer Hardware Version",
            0x100A => "Manufacturer Software Version",
            0x1018 => "Identity Object",
            0x8000..=0x8FFF => "Configuration Data",
            0x9000..=0x9FFF => "Information Data",
            0xA000..=0xAFFF => "Diagnosis Data",
            0xF002 => "Detect Modules Command",
            0xF120 => "Master Diag Data",
            0xF200 => "Diag Interface Control",
            _ => "Unknown",
        }
    }

    pub fn subindex_count(&self, index: u16) -> u8 {
        match index {
            0x1000 | 0x1008 | 0x1009 | 0x100A => 0,
            0x1018 => 4,
            0x8000..=0x8FFF => 40,
            0x9000..=0x9FFF => 32,
            0xA000..=0xAFFF => 19,
            0xF002 => 3,
            0xF120 | 0xF200 => 16,
            _ => 0,
        }
    }

    pub fn supported_object_indices(&self) -> Vec<u16> {
        let mut indices = vec![
            0x1000, 0x1008, 0x1009, 0x100A, 0x1018,
            0xF002, 0xF120, 0xF200,
        ];

        let slave_count = self.slave_count();
        for i in 1..=slave_count {
            indices.push(0x8000 | i);
            indices.push(0x9000 | i);
            indices.push(0xA000 | i);
        }

        indices
    }

    fn slave_count(&self) -> u16 {

        unsafe { ffi::GetGroupSlaveCount(self.master.index(), 0) }
    }

    fn read_identity_object(&self, subindex: u8) -> Option<Vec<u8>> {
        match subindex {

            0 => Some(vec![4]),

            1 => Some(VENDOR_ID.to_le_bytes().to_vec()),

            2 => Some(PRODUCT_CODE.to_le_bytes().to_vec()),

            3 => Some(self.revision_number().to_le_bytes().to_vec()),

            4 => Some(self.serial_number.to_le_bytes().to_vec()),
            _ => None,
        }
    }

    fn read_slave_config_data(&self, slave_idx: u16, subindex: u8) -> Option<Vec<u8>> {
        if slave_idx == 0 { return None; }

        let slave = crate::slave::Slave::new(self.master.index(), slave_idx);

        match subindex {

            0 => Some(vec![40]),

            1 => {
                match slave.identity() {
                    Ok(id) => Some(id.vendor_id.to_le_bytes().to_vec()),
                    Err(_) => Some(vec![0; 4]),
                }
            }
            2 => {
                match slave.identity() {
                    Ok(id) => Some(id.product_code.to_le_bytes().to_vec()),
                    Err(_) => Some(vec![0; 4]),
                }
            }
            3 => {
                match slave.identity() {
                    Ok(id) => Some(id.revision_no.to_le_bytes().to_vec()),
                    Err(_) => Some(vec![0; 4]),
                }
            }
            4 => {
                match slave.identity() {
                    Ok(id) => Some(id.serial_no.to_le_bytes().to_vec()),
                    Err(_) => Some(vec![0; 4]),
                }
            }
            _ => Some(vec![0; 4]),
        }
    }

    fn read_slave_info_data(&self, slave_idx: u16, subindex: u8) -> Option<Vec<u8>> {
        if slave_idx == 0 { return None; }

        let slave = crate::slave::Slave::new(self.master.index(), slave_idx);

        match subindex {

            0 => Some(vec![32]),

            1 => {
                let state = slave.state().map(|s| s as u8).unwrap_or(0);
                Some(vec![state])
            }

            2 => Some(slave.error_code_raw().to_le_bytes().to_vec()),
            _ => Some(vec![0; 4]),
        }
    }

    fn read_slave_diag_data(&self, slave_idx: u16, subindex: u8) -> Option<Vec<u8>> {
        if slave_idx == 0 { return None; }

        let slave = crate::slave::Slave::new(self.master.index(), slave_idx);

        match subindex {

            0 => Some(vec![19]),

            1 => {
                let state = slave.state().map(|s| s as u8).unwrap_or(0);
                Some(vec![state])
            }

            2 => Some(slave.error_code_raw().to_le_bytes().to_vec()),

            3 => {
                let quality = unsafe {
                    ffi::GetSlaveLinkQuality(self.master.index(), slave_idx)
                };
                Some(quality.to_le_bytes().to_vec())
            }
            _ => Some(vec![0; 4]),
        }
    }

    fn read_detect_modules(&self, subindex: u8) -> Option<Vec<u8>> {
        match subindex {

            0 => Some(vec![3]),

            1 => Some(vec![1]),

            2 => {
                let count = self.slave_count();
                Some(count.to_le_bytes().to_vec())
            }

            3 => Some(vec![0]),
            _ => None,
        }
    }

    fn read_master_diag_data(&self, subindex: u8) -> Option<Vec<u8>> {
        match subindex {

            0 => Some(vec![16]),
            _ => {

                let mut diag = ffi::MasterDiagData {
                    cyclic_lost_frames: 0,
                    acyclic_lost_frames: 0,
                    cyclic_frames_per_sec: 0,
                    acyclic_frames_per_sec: 0,
                    master_state: 0,
                };
                let ok = unsafe {
                    ffi::GetMasterDiagData(self.master.index(), &mut diag)
                };
                if ok == 0 { return Some(vec![0; 4]); }

                match subindex {

                    1 => Some(diag.cyclic_lost_frames.to_le_bytes().to_vec()),

                    2 => Some(diag.acyclic_lost_frames.to_le_bytes().to_vec()),

                    3 => Some(diag.cyclic_frames_per_sec.to_le_bytes().to_vec()),

                    4 => Some(diag.acyclic_frames_per_sec.to_le_bytes().to_vec()),

                    5 => Some(diag.master_state.to_le_bytes().to_vec()),
                    _ => Some(vec![0; 4]),
                }
            }
        }
    }
}

impl<'a> std::fmt::Display for MasterObjectDictionary<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "ETG.1510 主站对象字典 (VendorID=0x{:08X}, Serial={})",
            self.vendor_id(),
            self.serial_number
        )
    }
}
