
use std::collections::HashMap;
use std::time::{Duration, Instant};
use crate::data::error::{DarraError, Result};
use crate::utils::ffi;
use std::os::raw::c_int;

pub fn internet_checksum(data: &[u8]) -> u16 {
    let mut sum: u32 = 0;
    let mut i = 0;
    while i + 1 < data.len() {
        sum += u16::from_be_bytes([data[i], data[i + 1]]) as u32;
        i += 2;
    }

    if i < data.len() {
        sum += (data[i] as u32) << 8;
    }

    while (sum >> 16) != 0 {
        sum = (sum & 0xFFFF) + (sum >> 16);
    }
    !(sum as u16)
}

fn build_ethernet_frame(dst_mac: &[u8; 6], src_mac: &[u8; 6], ethertype: u16, payload: &[u8]) -> Vec<u8> {
    let mut frame = Vec::with_capacity(14 + payload.len());
    frame.extend_from_slice(dst_mac);
    frame.extend_from_slice(src_mac);
    frame.extend_from_slice(&ethertype.to_be_bytes());
    frame.extend_from_slice(payload);

    while frame.len() < 60 {
        frame.push(0);
    }
    frame
}

fn build_ip_header(src_ip: u32, dst_ip: u32, protocol: u8, payload_len: u16) -> Vec<u8> {
    let total_len = 20u16 + payload_len;
    let mut header = vec![
        0x45,
        0x00,
        (total_len >> 8) as u8,
        (total_len & 0xFF) as u8,
        0x00, 0x01,
        0x40, 0x00,
        0x40,
        protocol,
        0x00, 0x00,

        ((src_ip >> 24) & 0xFF) as u8,
        ((src_ip >> 16) & 0xFF) as u8,
        ((src_ip >>  8) & 0xFF) as u8,
        ( src_ip        & 0xFF) as u8,

        ((dst_ip >> 24) & 0xFF) as u8,
        ((dst_ip >> 16) & 0xFF) as u8,
        ((dst_ip >>  8) & 0xFF) as u8,
        ( dst_ip        & 0xFF) as u8,
    ];

    let checksum = internet_checksum(&header);
    header[10] = (checksum >> 8) as u8;
    header[11] = (checksum & 0xFF) as u8;
    header
}

fn build_icmp_echo(id: u16, seq: u16, data: &[u8]) -> Vec<u8> {
    let mut icmp = Vec::with_capacity(8 + data.len());
    icmp.push(8);
    icmp.push(0);
    icmp.extend_from_slice(&[0, 0]);
    icmp.extend_from_slice(&id.to_be_bytes());
    icmp.extend_from_slice(&seq.to_be_bytes());
    icmp.extend_from_slice(data);

    let checksum = internet_checksum(&icmp);
    icmp[2] = (checksum >> 8) as u8;
    icmp[3] = (checksum & 0xFF) as u8;
    icmp
}

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

    pub success: bool,

    pub rtt: Option<Duration>,

    pub seq: u16,
}

pub fn eoe_ping(
    master_index: u16,
    slave_index: u16,
    port: u8,
    src_mac: &[u8; 6],
    dst_mac: &[u8; 6],
    src_ip: u32,
    dst_ip: u32,
    seq: u16,
    payload: &[u8],
    timeout_us: i32,
) -> Result<PingResult> {

    let icmp = build_icmp_echo(0xDA12, seq, payload);

    let mut ip = build_ip_header(src_ip, dst_ip, 1, icmp.len() as u16);
    ip.extend_from_slice(&icmp);

    let frame = build_ethernet_frame(dst_mac, src_mac, 0x0800, &ip);

    let start = Instant::now();

    let ok = unsafe {
        ffi::EOESendFrame(master_index, slave_index, port,
                          frame.as_ptr(), frame.len() as c_int, timeout_us)
    };
    if ok == 0 {
        return Ok(PingResult { success: false, rtt: None, seq });
    }

    let mut resp_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
    let mut resp_size: c_int = 0;
    let recv_ok = unsafe {
        ffi::EOEReceiveFrame(master_index, slave_index, port,
                             &mut resp_ptr, &mut resp_size, timeout_us)
    };
    let rtt = start.elapsed();

    if recv_ok == 0 || resp_ptr.is_null() || resp_size < 42 {
        if !resp_ptr.is_null() { unsafe { ffi::FreeMemory(resp_ptr) }; }
        return Ok(PingResult { success: false, rtt: None, seq });
    }

    let resp = unsafe {
        std::slice::from_raw_parts(resp_ptr as *const u8, resp_size as usize).to_vec()
    };
    unsafe { ffi::FreeMemory(resp_ptr) };

    if resp.len() < 36 {
        return Ok(PingResult { success: false, rtt: None, seq });
    }

    let ethertype = u16::from_be_bytes([resp[12], resp[13]]);
    if ethertype != 0x0800 {
        return Ok(PingResult { success: false, rtt: None, seq });
    }

    let protocol = resp[23];
    if protocol != 1 {
        return Ok(PingResult { success: false, rtt: None, seq });
    }

    let icmp_type = resp[34];

    let resp_seq = u16::from_be_bytes([resp[38], resp[39]]);
    let success = icmp_type == 0 && resp_seq == seq;

    Ok(PingResult { success, rtt: Some(rtt), seq })
}

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

    pub ip: u32,

    pub mac: [u8; 6],

    pub last_seen: Instant,

    pub dynamic: bool,
}

impl ArpEntry {

    pub fn is_expired(&self, ttl: Duration) -> bool {
        self.last_seen.elapsed() > ttl
    }
}

pub struct ArpCache {

    entries: HashMap<u32, ArpEntry>,

    ttl: Duration,
}

impl ArpCache {

    pub fn new() -> Self {
        Self {
            entries: HashMap::new(),
            ttl: Duration::from_secs(300),
        }
    }

    pub fn set_ttl(&mut self, ttl: Duration) {
        self.ttl = ttl;
    }

    pub fn add_static(&mut self, ip: u32, mac: [u8; 6]) {
        self.entries.insert(ip, ArpEntry {
            ip, mac,
            last_seen: Instant::now(),
            dynamic: false,
        });
    }

    pub fn lookup(&self, ip: u32) -> Option<[u8; 6]> {
        self.entries.get(&ip).and_then(|e| {
            if e.dynamic && e.is_expired(self.ttl) {
                None
            } else {
                Some(e.mac)
            }
        })
    }

    pub fn learn_from_frame(&mut self, frame: &[u8]) {
        if frame.len() < 42 { return; }

        if frame[12] != 0x08 || frame[13] != 0x06 { return; }

        let opcode = u16::from_be_bytes([frame[20], frame[21]]);
        if opcode != 2 { return; }

        let mut mac = [0u8; 6];
        mac.copy_from_slice(&frame[22..28]);

        let ip = u32::from_be_bytes([frame[28], frame[29], frame[30], frame[31]]);
        self.entries.insert(ip, ArpEntry {
            ip, mac,
            last_seen: Instant::now(),
            dynamic: true,
        });
    }

    pub fn resolve(
        &mut self,
        master_index: u16,
        slave_index: u16,
        port: u8,
        src_mac: &[u8; 6],
        src_ip: u32,
        target_ip: u32,
        timeout_us: i32,
    ) -> Result<Option<[u8; 6]>> {

        if let Some(mac) = self.lookup(target_ip) {
            return Ok(Some(mac));
        }

        let arp_frame = build_arp_request(src_mac, src_ip, target_ip);
        let broadcast_mac = [0xFF; 6];
        let frame = build_ethernet_frame(&broadcast_mac, src_mac, 0x0806, &arp_frame);

        let send_ok = unsafe {
            ffi::EOESendFrame(master_index, slave_index, port,
                              frame.as_ptr(), frame.len() as c_int, timeout_us)
        };
        if send_ok == 0 {
            return Err(DarraError::EoeFailed);
        }

        let mut resp_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
        let mut resp_size: c_int = 0;
        let recv_ok = unsafe {
            ffi::EOEReceiveFrame(master_index, slave_index, port,
                                 &mut resp_ptr, &mut resp_size, timeout_us)
        };
        if recv_ok == 0 || resp_ptr.is_null() {
            if !resp_ptr.is_null() { unsafe { ffi::FreeMemory(resp_ptr) }; }
            return Ok(None);
        }

        let resp = unsafe {
            std::slice::from_raw_parts(resp_ptr as *const u8, resp_size as usize).to_vec()
        };
        unsafe { ffi::FreeMemory(resp_ptr) };

        self.learn_from_frame(&resp);

        Ok(self.lookup(target_ip))
    }

    pub fn purge_expired(&mut self) {
        let ttl = self.ttl;
        self.entries.retain(|_, e| !e.dynamic || !e.is_expired(ttl));
    }

    pub fn remove(&mut self, ip: u32) {
        self.entries.remove(&ip);
    }

    pub fn len(&self) -> usize {
        self.entries.len()
    }

    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }
}

impl Default for ArpCache {
    fn default() -> Self {
        Self::new()
    }
}

pub fn foe_estimate_packet_count(file_size: u32, max_data_size: u32) -> u32 {
    if max_data_size == 0 {
        return 0;
    }

    (file_size + max_data_size - 1) / max_data_size
}

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

    pub success: bool,

    pub round_trip_time_ms: f64,

    pub target_address: String,

    pub ttl: u8,

    pub error_message: String,
}

impl std::fmt::Display for EoEPingResult {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.success {
            write!(f, "来自 {} 的回复: 时间={:.2}ms TTL={}", self.target_address, self.round_trip_time_ms, self.ttl)
        } else {
            write!(f, "Ping {} 失败: {}", self.target_address, self.error_message)
        }
    }
}

pub struct EoEInstance {
    master_index: u16,
    slave_index: u16,
    port: u8,

    pub arp_cache: ArpCache,
}

impl EoEInstance {

    pub(crate) fn new(master_index: u16, slave_index: u16, port: u8) -> Self {
        Self { master_index, slave_index, port, arp_cache: ArpCache::new() }
    }

    pub fn master_index(&self) -> u16 { self.master_index }

    pub fn slave_index(&self) -> u16 { self.slave_index }

    pub fn is_supported(&self) -> bool {
        let proto = unsafe { ffi::GetSlaveMailboxProto(self.master_index, self.slave_index) };
        (proto & 0x02) != 0
    }

    pub fn ip(&self, timeout_ms: i32) -> Result<u32> {
        let mut ip: u32 = 0;
        let mut subnet: u32 = 0;
        let mut gateway: u32 = 0;
        let ret = unsafe {
            ffi::EOEGetIP(self.master_index, self.slave_index, self.port,
                         &mut ip, &mut subnet, &mut gateway, timeout_ms * 1000)
        };
        if ret != 0 { Ok(ip) } else { Err(DarraError::EoeFailed) }
    }

    pub fn subnet(&self, timeout_ms: i32) -> Result<u32> {
        let mut ip: u32 = 0;
        let mut subnet: u32 = 0;
        let mut gateway: u32 = 0;
        let ret = unsafe {
            ffi::EOEGetIP(self.master_index, self.slave_index, self.port,
                         &mut ip, &mut subnet, &mut gateway, timeout_ms * 1000)
        };
        if ret != 0 { Ok(subnet) } else { Err(DarraError::EoeFailed) }
    }

    pub fn gateway(&self, timeout_ms: i32) -> Result<u32> {
        let mut ip: u32 = 0;
        let mut subnet: u32 = 0;
        let mut gateway: u32 = 0;
        let ret = unsafe {
            ffi::EOEGetIP(self.master_index, self.slave_index, self.port,
                         &mut ip, &mut subnet, &mut gateway, timeout_ms * 1000)
        };
        if ret != 0 { Ok(gateway) } else { Err(DarraError::EoeFailed) }
    }

    pub fn set_ip(&self, ip: u32, subnet: u32, gateway: u32, timeout_ms: i32) -> Result<()> {
        let ret = unsafe {
            ffi::EOESetIP(self.master_index, self.slave_index, self.port,
                         ip, subnet, gateway, timeout_ms * 1000)
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
    }

    pub fn mac(&self, timeout_ms: i32) -> Result<[u8; 6]> {
        let mut mac = [0u8; 6];
        let ret = unsafe {
            ffi::EOEGetMAC(self.master_index, self.slave_index, self.port,
                          mac.as_mut_ptr(), timeout_ms * 1000)
        };
        if ret != 0 { Ok(mac) } else { Err(DarraError::EoeFailed) }
    }

    pub fn mac_string(&self, timeout_ms: i32) -> Result<String> {
        let mac = self.mac(timeout_ms)?;
        Ok(format!("{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
                   mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]))
    }

    pub fn set_mac(&self, mac: &[u8; 6], timeout_ms: i32) -> Result<()> {
        let ret = unsafe {
            ffi::EOESetMAC(self.master_index, self.slave_index, self.port,
                          mac.as_ptr(), timeout_ms * 1000)
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
    }

    pub fn dns(&self, timeout_ms: i32) -> Result<u32> {
        let mut dns_ip: u32 = 0;
        let mut dns_name = vec![0i8; 33];
        let ret = unsafe {
            ffi::EOEGetDNS(self.master_index, self.slave_index, self.port,
                          &mut dns_ip, dns_name.as_mut_ptr(), timeout_ms * 1000)
        };
        if ret != 0 { Ok(dns_ip) } else { Err(DarraError::EoeFailed) }
    }

    pub fn set_dns(&self, dns_ip: u32, dns_name: &str, timeout_ms: i32) -> Result<()> {
        let c_name = std::ffi::CString::new(dns_name).unwrap_or_default();
        let ret = unsafe {
            ffi::EOESetDNS(self.master_index, self.slave_index, self.port,
                          dns_ip, c_name.as_ptr(), timeout_ms * 1000)
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
    }

    pub fn send_frame(&self, frame: &[u8], timeout_ms: i32) -> Result<()> {
        let ret = unsafe {
            ffi::EOESendFrame(self.master_index, self.slave_index, self.port,
                             frame.as_ptr(), frame.len() as c_int, timeout_ms * 1000)
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
    }

    pub fn receive_frame(&self, timeout_ms: i32) -> Result<Vec<u8>> {
        let mut frame_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
        let mut frame_size: c_int = 0;
        let ret = unsafe {
            ffi::EOEReceiveFrame(self.master_index, self.slave_index, self.port,
                                &mut frame_ptr, &mut frame_size, timeout_ms * 1000)
        };
        if ret != 0 && !frame_ptr.is_null() && frame_size > 0 {
            let data = unsafe {
                std::slice::from_raw_parts(frame_ptr as *const u8, frame_size as usize).to_vec()
            };
            unsafe { ffi::FreeMemory(frame_ptr) };
            Ok(data)
        } else {
            Err(DarraError::EoeFailed)
        }
    }

    pub fn send_frame_with_timestamp(&self, frame: &[u8], timestamp: u64, timeout_ms: i32) -> Result<()> {

        let ts32 = timestamp as u32;
        let mut frame_with_ts = Vec::with_capacity(frame.len() + 4);
        frame_with_ts.extend_from_slice(frame);
        frame_with_ts.extend_from_slice(&ts32.to_le_bytes());

        let ret = unsafe {
            ffi::EOESendFrame(
                self.master_index, self.slave_index, self.port,
                frame_with_ts.as_ptr(), frame_with_ts.len() as c_int,
                timeout_ms * 1000,
            )
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
    }

    pub fn get_address_filters(&self, timeout_ms: i32) -> Result<Vec<[u8; 6]>> {
        let mut filter_count: u8 = 0;

        let mut mac_buffer = vec![0u8; 16 * 6];
        let ret = unsafe {
            ffi::EOEGetAddressFilter(
                self.master_index, self.slave_index, self.port,
                &mut filter_count, mac_buffer.as_mut_ptr(), 16, timeout_ms * 1000,
            )
        };
        if ret == 0 {
            return Err(DarraError::EoeFailed);
        }
        let count = filter_count as usize;
        let mut filters = Vec::with_capacity(count);
        for i in 0..count {
            let mut mac = [0u8; 6];
            mac.copy_from_slice(&mac_buffer[i * 6..(i + 1) * 6]);
            filters.push(mac);
        }
        Ok(filters)
    }

    pub fn set_address_filters_batch(&self, filters: &[[u8; 6]], timeout_ms: i32) -> Result<()> {

        let mac_data: Vec<u8> = filters.iter().flat_map(|m| m.iter().copied()).collect();
        let ret = unsafe {
            ffi::EOESetAddressFilter(
                self.master_index, self.slave_index, self.port,
                filters.len() as u8, mac_data.as_ptr(), timeout_ms * 1000,
            )
        };
        if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
    }

    pub fn clear_arp_cache(&mut self) {
        self.arp_cache = ArpCache::new();
    }

    pub fn ping(&self, target_ip: u32, timeout_ms: i32) -> EoEPingResult {

        let ip = match self.ip(timeout_ms) {
            Ok(ip) => ip,
            Err(_) => return EoEPingResult {
                success: false, round_trip_time_ms: 0.0,
                target_address: format_ip(target_ip), ttl: 0,
                error_message: "本地网络配置无效".to_string(),
            },
        };
        let mac = match self.mac(timeout_ms) {
            Ok(mac) => mac,
            Err(_) => return EoEPingResult {
                success: false, round_trip_time_ms: 0.0,
                target_address: format_ip(target_ip), ttl: 0,
                error_message: "无法获取MAC地址".to_string(),
            },
        };

        let dst_mac = self.arp_cache.lookup(target_ip).unwrap_or([0xFF; 6]);
        match eoe_ping(self.master_index, self.slave_index, self.port,
                       &mac, &dst_mac, ip, target_ip, 1, &[0u8; 32], timeout_ms * 1000) {
            Ok(result) => EoEPingResult {
                success: result.success,
                round_trip_time_ms: result.rtt.map(|r| r.as_secs_f64() * 1000.0).unwrap_or(0.0),
                target_address: format_ip(target_ip),
                ttl: 64,
                error_message: if result.success { String::new() } else { "超时".to_string() },
            },
            Err(e) => EoEPingResult {
                success: false, round_trip_time_ms: 0.0,
                target_address: format_ip(target_ip), ttl: 0,
                error_message: format!("{:?}", e),
            },
        }
    }
}

fn format_ip(ip: u32) -> String {
    format!("{}.{}.{}.{}",
            (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF)
}

fn build_arp_request(src_mac: &[u8; 6], src_ip: u32, target_ip: u32) -> Vec<u8> {
    let mut arp = Vec::with_capacity(28);

    arp.extend_from_slice(&1u16.to_be_bytes());

    arp.extend_from_slice(&0x0800u16.to_be_bytes());

    arp.push(6);

    arp.push(4);

    arp.extend_from_slice(&1u16.to_be_bytes());

    arp.extend_from_slice(src_mac);

    arp.extend_from_slice(&src_ip.to_be_bytes());

    arp.extend_from_slice(&[0u8; 6]);

    arp.extend_from_slice(&target_ip.to_be_bytes());
    arp
}

impl EoEInstance {

    pub fn send_frame_blocking(
        master_index: u16,
        slave_index: u16,
        port: u8,
        frame: Vec<u8>,
        timeout_ms: i32,
    ) -> std::thread::JoinHandle<Result<()>> {
        std::thread::spawn(move || {
            let eoe = EoEInstance::new(master_index, slave_index, port);
            eoe.send_frame(&frame, timeout_ms)
        })
    }

    pub fn receive_frame_blocking(
        master_index: u16,
        slave_index: u16,
        port: u8,
        timeout_ms: i32,
    ) -> std::thread::JoinHandle<Result<Vec<u8>>> {
        std::thread::spawn(move || {
            let eoe = EoEInstance::new(master_index, slave_index, port);
            eoe.receive_frame(timeout_ms)
        })
    }

    pub fn ping_blocking(
        master_index: u16,
        slave_index: u16,
        port: u8,
        target_ip: u32,
        timeout_ms: i32,
    ) -> std::thread::JoinHandle<EoEPingResult> {
        std::thread::spawn(move || {
            let eoe = EoEInstance::new(master_index, slave_index, port);
            eoe.ping(target_ip, timeout_ms)
        })
    }
}

use std::sync::{Mutex as StdMutex, OnceLock as StdOnceLock, Arc};

type EoEReceiveHook = Arc<dyn Fn(u16, u16, &[u8]) + Send + Sync + 'static>;

fn eoe_hook_registry() -> &'static StdMutex<HashMap<u16, EoEReceiveHook>> {
    static REG: StdOnceLock<StdMutex<HashMap<u16, EoEReceiveHook>>> = StdOnceLock::new();
    REG.get_or_init(|| StdMutex::new(HashMap::new()))
}

unsafe extern "C" fn eoe_receive_trampoline(
    master_index: u16,
    slave: u16,
    frame_data: *const u8,
    frame_size: c_int,
) {
    if frame_data.is_null() || frame_size <= 0 {
        return;
    }
    let slice = unsafe { std::slice::from_raw_parts(frame_data, frame_size as usize) };
    let cb_opt = {
        match eoe_hook_registry().lock() {
            Ok(g) => g.get(&master_index).cloned(),
            Err(p) => p.into_inner().get(&master_index).cloned(),
        }
    };
    if let Some(cb) = cb_opt {
        cb(master_index, slave, slice);
    }
}

impl EoEInstance {

    pub fn set_receive_hook<F>(&self, callback: F) -> Result<()>
    where
        F: Fn(u16, u16, &[u8]) + Send + Sync + 'static,
    {

        match eoe_hook_registry().lock() {
            Ok(mut g) => { g.insert(self.master_index, Arc::new(callback)); }
            Err(p) => { p.into_inner().insert(self.master_index, Arc::new(callback)); }
        }

        let ok = unsafe {
            ffi::EOESetReceiveHook(self.master_index, Some(eoe_receive_trampoline))
        };
        if ok == 0 {

            if let Ok(mut g) = eoe_hook_registry().lock() {
                g.remove(&self.master_index);
            }
            return Err(DarraError::Other(
                obfstr::obfstr!("EOESetReceiveHook 返回 false (DLL 拒绝注册)").to_string()
            ));
        }
        Ok(())
    }

    pub fn clear_receive_hook(&self) -> Result<()> {

        let _ok = unsafe { ffi::EOEClearReceiveHook(self.master_index) };
        if let Ok(mut g) = eoe_hook_registry().lock() {
            g.remove(&self.master_index);
        }
        Ok(())
    }
}

#[cfg(feature = "async-tokio")]
impl EoEInstance {

    pub async fn send_frame_async(&self, frame: Vec<u8>, timeout_ms: i32) -> Result<()> {
        let master = self.master_index;
        let slave = self.slave_index;
        let port = self.port;
        tokio::task::spawn_blocking(move || {
            let eoe = EoEInstance::new(master, slave, port);
            eoe.send_frame(&frame, timeout_ms)
        })
        .await
        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
    }

    pub async fn receive_frame_async(&self, timeout_ms: i32) -> Result<Vec<u8>> {
        let master = self.master_index;
        let slave = self.slave_index;
        let port = self.port;
        tokio::task::spawn_blocking(move || {
            let eoe = EoEInstance::new(master, slave, port);
            eoe.receive_frame(timeout_ms)
        })
        .await
        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
    }

    pub async fn ping_async(&self, target_ip: u32, timeout_ms: i32) -> EoEPingResult {
        let master = self.master_index;
        let slave = self.slave_index;
        let port = self.port;
        match tokio::task::spawn_blocking(move || {
            let eoe = EoEInstance::new(master, slave, port);
            eoe.ping(target_ip, timeout_ms)
        })
        .await
        {
            Ok(r) => r,
            Err(e) => EoEPingResult {
                success: false,
                round_trip_time_ms: 0.0,
                target_address: format_ip(target_ip),
                ttl: 0,
                error_message: format!("tokio join error: {}", e),
            },
        }
    }
}

impl crate::abstractions::MailboxProtocol for EoEInstance {
    fn protocol_type(&self) -> u8 { 0x02 }
    fn protocol_name(&self) -> &'static str { "EoE" }

    fn is_supported(&self) -> bool {
        EoEInstance::is_supported(self)
    }

    fn statistics(&self) -> crate::abstractions::MailboxStatistics {
        let mut stats = ffi::EcMbxStatsC::default();
        let rc = unsafe {
            ffi::mbx_get_stats_by_master(
                self.master_index, self.slave_index, 0x02, &mut stats,
            )
        };
        if rc == 1 {
            stats.into()
        } else {
            crate::abstractions::MailboxStatistics::empty()
        }
    }

    fn reset_statistics(&self) {
        unsafe {
            ffi::mbx_reset_stats_by_master(self.master_index, self.slave_index, 0x02);
        }
    }
}
