
use crate::utils::ffi;
#[allow(unused_imports)]
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

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

    pub name: String,

    pub description: String,

    pub mac: [u8; 6],
}

impl NetworkInfo {

    pub fn mac_string(&self) -> String {
        format!("{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
            self.mac[0], self.mac[1], self.mac[2],
            self.mac[3], self.mac[4], self.mac[5])
    }
}

pub fn get_network_adapters() -> Vec<NetworkInfo> {

    Vec::new()
}

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

    pub name: String,

    pub description: String,

    pub slave_num: i16,

    pub redundant_slave_num: i16,
}

pub fn enumerate_network_info(is_redundant: bool, need_slaves_num: bool) -> Vec<EnumeratedNetworkInfo> {
    let count = unsafe { ffi::GetNetworkInfo(is_redundant as i32, need_slaves_num as i32) };
    if count <= 0 {
        return Vec::new();
    }
    let ptr = unsafe { ffi::GetNetworksPointer() } as *const u8;
    if ptr.is_null() {
        return Vec::new();
    }

    const STRIDE: usize = 128 + 128 + 2 + 2;
    let mut result = Vec::with_capacity(count as usize);
    for i in 0..count as usize {
        unsafe {
            let base = ptr.add(i * STRIDE);
            let name_slice = std::slice::from_raw_parts(base, 128);
            let desc_slice = std::slice::from_raw_parts(base.add(128), 128);
            let slave_num = std::ptr::read_unaligned(base.add(256) as *const i16);
            let red_num = std::ptr::read_unaligned(base.add(258) as *const i16);
            let name_end = name_slice.iter().position(|&b| b == 0).unwrap_or(128);
            let desc_end = desc_slice.iter().position(|&b| b == 0).unwrap_or(128);
            let name = String::from_utf8_lossy(&name_slice[..name_end]).to_string();
            let desc = String::from_utf8_lossy(&desc_slice[..desc_end]).to_string();
            result.push(EnumeratedNetworkInfo {
                name,
                description: desc,
                slave_num,
                redundant_slave_num: red_num,
            });
        }
    }
    result
}

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

    pub index: u16,

    pub vendor_id: u32,

    pub product_code: u32,

    pub revision: u32,

    pub serial: u32,

    pub name: String,

    pub config_addr: u16,

    pub alias_addr: u16,

    pub parent_index: u16,

    pub topology: u8,

    pub active_ports: u8,

    pub entry_port: u8,

    pub parent_port: u8,

    pub physical_type: u8,
}

impl ScannedSlaveInfo {

    pub fn alias_addr_hex(&self) -> String {
        format!("0x{:04X}", self.alias_addr)
    }

    pub fn vendor_id_hex(&self) -> String {
        format!("0x{:08X}", self.vendor_id)
    }

    pub fn is_coupler(&self) -> bool {

        self.physical_type == 1
    }

    pub fn device_type(&self) -> u8 {
        self.physical_type
    }

    pub fn depth(&self) -> u16 {

        if self.parent_index == 0 || self.parent_index == self.index {
            0
        } else {
            1
        }
    }
}

pub fn get_scanned_slaves() -> Vec<ScannedSlaveInfo> {
    let count = unsafe { ffi::GetScannedSlaveCount() };
    if count <= 0 {
        return Vec::new();
    }

    let mut slaves = Vec::with_capacity(count as usize);
    for i in 0..count {
        let mut vendor_id: u32 = 0;
        let mut product_code: u32 = 0;
        let mut revision: u32 = 0;
        let mut serial: u32 = 0;
        let mut name_buf = vec![0u8; 256];
        let mut config_addr: u16 = 0;
        let mut alias_addr: u16 = 0;
        let mut parent: u16 = 0;
        let mut topology: u8 = 0;
        let mut active_ports: u8 = 0;
        let mut entry_port: u8 = 0;
        let mut parent_port: u8 = 0;
        let mut ptype: u8 = 0;

        let ret = unsafe {
            ffi::GetScannedSlaveInfo(
                i,
                &mut vendor_id, &mut product_code, &mut revision, &mut serial,
                name_buf.as_mut_ptr() as *mut c_char, name_buf.len() as i32,
                &mut config_addr, &mut alias_addr,
                &mut parent, &mut topology, &mut active_ports,
                &mut entry_port, &mut parent_port, &mut ptype,
            )
        };

        if ret != 0 {
            let null_pos = name_buf.iter().position(|&b| b == 0).unwrap_or(name_buf.len());
            let name = String::from_utf8_lossy(&name_buf[..null_pos]).to_string();
            slaves.push(ScannedSlaveInfo {
                index: (i + 1) as u16,
                vendor_id,
                product_code,
                revision,
                serial,
                name,
                config_addr,
                alias_addr,
                parent_index: parent,
                topology,
                active_ports,
                entry_port,
                parent_port,
                physical_type: ptype,
            });
        }
    }
    slaves
}

pub fn ring_slave_count() -> i32 {
    unsafe { ffi::GetRingSlaveCount() }
}

pub fn scanned_slave_count() -> i32 {
    unsafe { ffi::GetScannedSlaveCount() }
}

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

    pub index: u16,

    pub name: String,

    pub child_count: usize,
}

pub fn determine_coupler_status(slaves: &[ScannedSlaveInfo]) -> Vec<CouplerStatus> {
    let mut results = Vec::new();
    for s in slaves {
        if !s.is_coupler() {
            continue;
        }
        let child_count = slaves.iter().filter(|c| c.parent_index == s.index).count();
        results.push(CouplerStatus {
            index: s.index,
            name: s.name.clone(),
            child_count,
        });
    }
    results
}
