
use crate::utils::ffi;

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

    pub slave_index: u16,

    pub port0_crc_errors: u8,

    pub port1_crc_errors: u8,

    pub port2_crc_errors: u8,

    pub port3_crc_errors: u8,

    pub frame_errors: u8,

    pub lost_frames: u8,
}

pub fn read_slave_error_counters(master_index: u16, slave_index: u16) -> SlaveErrorCounters {
    let mut counters = SlaveErrorCounters {
        slave_index,
        ..Default::default()
    };

    let mut crc_data = [0u8; 8];
    unsafe {
        if ffi::ReadSlaveRegister(
            master_index, slave_index, 0x0300,
            crc_data.as_mut_ptr(), 8,
        ) != 0 {
            counters.port0_crc_errors = crc_data[0];
            counters.port1_crc_errors = crc_data[2];
            counters.port2_crc_errors = crc_data[4];
            counters.port3_crc_errors = crc_data[6];
        }

        let mut frame_data = [0u8; 4];
        if ffi::ReadSlaveRegister(
            master_index, slave_index, 0x0308,
            frame_data.as_mut_ptr(), 4,
        ) != 0 {
            counters.frame_errors = frame_data[0];
            counters.lost_frames = frame_data[2];
        }
    }

    counters
}

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

    pub total_lost: u32,

    pub consecutive_lost: u32,

    pub max_consecutive_lost: u32,
}

pub fn get_pdo_frame_loss_stats(master_index: u16, group: u8) -> PdoFrameLossInfo {
    let mut total = 0u32;
    let mut consecutive = 0u32;
    let mut max_consecutive = 0u32;
    unsafe {
        ffi::GetPDOFrameLossStats(
            master_index, group,
            &mut total, &mut consecutive, &mut max_consecutive,
        );
    }
    PdoFrameLossInfo {
        total_lost: total,
        consecutive_lost: consecutive,
        max_consecutive_lost: max_consecutive,
    }
}

pub fn get_all_pdo_frame_loss_stats(master_index: u16) -> PdoFrameLossInfo {
    let mut total_sum = 0u32;
    let mut consecutive_max = 0u32;
    let mut max_consecutive_max = 0u32;

    for g in 0..8u8 {
        let stats = get_pdo_frame_loss_stats(master_index, g);
        total_sum += stats.total_lost;
        if stats.consecutive_lost > consecutive_max {
            consecutive_max = stats.consecutive_lost;
        }
        if stats.max_consecutive_lost > max_consecutive_max {
            max_consecutive_max = stats.max_consecutive_lost;
        }
    }

    PdoFrameLossInfo {
        total_lost: total_sum,
        consecutive_lost: consecutive_max,
        max_consecutive_lost: max_consecutive_max,
    }
}

pub fn reset_pdo_frame_loss_stats(master_index: u16, group: u8) {
    unsafe { ffi::ResetPDOFrameLossStats(master_index, group); }
}

pub fn reset_all_pdo_frame_loss_stats(master_index: u16) {
    for g in 0..8u8 {
        reset_pdo_frame_loss_stats(master_index, g);
    }
}

#[derive(Clone, Copy, Debug, Default)]
pub struct WdkRtCounters {

    pub preempt_count: u64,

    pub preempt_peak_ns: u32,

    pub big_gap_unknown_count: u64,

    pub smi_msr_available: bool,

    pub rt_cpu_index: i32,

    pub rx_cpu_index: i32,

    pub jitter_instrument_ver: u32,

    pub jitter_total_samples: u64,

    pub jitter_min_ns: u32,

    pub jitter_last_wake_delta_ns: i32,

    pub path_count_fast: u32,

    pub path_count_med: u32,

    pub path_count_slow: u32,

    pub ktimer_wake_normal: u32,

    pub ktimer_wake_overrun: u32,

    pub dispatch_cycle_max_jitter_ns: u32,

    pub drain_cycle_max_jitter_ns: u32,
}

pub fn get_wdk_rt_counters(master_index: u16) -> WdkRtCounters {
    unsafe {
        WdkRtCounters {
            preempt_count: ffi::GetRtPreemptCount(master_index),
            preempt_peak_ns: ffi::GetRtPreemptPeakNs(master_index),
            big_gap_unknown_count: ffi::GetBigGapUnknownCount(master_index),
            smi_msr_available: ffi::GetSmiMsrAvailable(master_index) != 0,
            rt_cpu_index: ffi::GetWdkRtCpuIndex(master_index),
            rx_cpu_index: ffi::GetWdkRxCpuIndex(master_index),
            jitter_instrument_ver: ffi::GetWdkJitterInstrumentVer(master_index),
            jitter_total_samples: ffi::GetWdkJitterTotalSamples(master_index),
            jitter_min_ns: ffi::GetWdkJitterMinNs(master_index),
            jitter_last_wake_delta_ns: ffi::GetWdkJitterLastWakeDeltaNs(master_index),
            path_count_fast: ffi::GetWdkPathCountFast(master_index),
            path_count_med: ffi::GetWdkPathCountMed(master_index),
            path_count_slow: ffi::GetWdkPathCountSlow(master_index),
            ktimer_wake_normal: ffi::GetWdkKtimerWakeNormalCnt(master_index),
            ktimer_wake_overrun: ffi::GetWdkKtimerWakeOverrunCnt(master_index),
            dispatch_cycle_max_jitter_ns: ffi::GetWdkDispatchCycleMaxJitterNs(master_index),
            drain_cycle_max_jitter_ns: ffi::GetWdkDrainCycleMaxJitterNs(master_index),
        }
    }
}

pub fn get_wdk_jitter_hist_bin(master_index: u16, bin_index: u32) -> u32 {
    unsafe { ffi::GetWdkJitterHistBin(master_index, bin_index) }
}

pub fn get_wdk_diag_counter(master_index: u16, counter_id: u32) -> u32 {
    unsafe { ffi::GetWdkDiagCounter(master_index, counter_id) }
}

#[derive(Clone, Copy, Debug, Default)]
pub struct WdkFrameStats {

    pub primary_tx: u32,

    pub primary_rx: u32,

    pub secondary_tx: u32,

    pub secondary_rx: u32,

    pub pdo_idx_drop: u32,
}

pub fn get_wdk_frame_stats(master_index: u16) -> WdkFrameStats {
    unsafe {
        WdkFrameStats {
            primary_tx: ffi::GetWdkPrimaryFrameTx(master_index),
            primary_rx: ffi::GetWdkPrimaryFrameRx(master_index),
            secondary_tx: ffi::GetWdkSecondaryFrameTx(master_index),
            secondary_rx: ffi::GetWdkSecondaryFrameRx(master_index),
            pdo_idx_drop: ffi::GetWdkPdoIdxDropCount(master_index),
        }
    }
}
