
use crate::data::error::{SyncWindowStatus, Result};
use crate::utils::ffi;
use crate::data::types::DcSyncMode;

pub struct SlaveDC {
    master_index: u16,
    slave_index: u16,
}

impl SlaveDC {

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

    pub fn configure(&self, sync0_ns: u32, sync1_ns: u32, shift_ns: Option<i32>) -> Result<()> {
        let actual_shift = match shift_ns {
            Some(s) => s,
            None => {

                unsafe { ffi::UpdatePropagationDelays(self.master_index) };
                let max_delay = unsafe { ffi::GetMaxPropagationDelay(self.master_index) };
                let slave_delay = unsafe { ffi::GetSlavePropagationDelay(self.master_index, self.slave_index) };
                if max_delay > 0 { max_delay - slave_delay } else { 0 }
            }
        };
        unsafe {
            ffi::SetSyncBySlaveIndex(self.master_index, self.slave_index, sync0_ns, sync1_ns, actual_shift);
        }
        Ok(())
    }

    pub fn configure_mode(&self, mode: DcSyncMode, sync0_ns: u32, sync1_ns: u32, shift_ns: Option<i32>) -> Result<()> {
        match mode {
            DcSyncMode::FreeRun => {
                unsafe {
                    ffi::SetSyncBySlaveIndex(self.master_index, self.slave_index, 0, 0, 0);
                }
                self.configure_sm_sync_type(DcSyncMode::FreeRun);
                Ok(())
            }
            DcSyncMode::SmSynchron => {
                unsafe {
                    ffi::SetSyncBySlaveIndex(self.master_index, self.slave_index, 0, 0, 0);
                }
                self.configure_sm_sync_type(DcSyncMode::SmSynchron);
                Ok(())
            }
            DcSyncMode::DcSynchron => {

                self.configure(sync0_ns, 0, shift_ns)?;
                self.configure_sm_sync_type(DcSyncMode::DcSynchron);
                Ok(())
            }
            DcSyncMode::DcSynchron01 => {

                self.configure(sync0_ns, sync1_ns, shift_ns)?;
                self.configure_sm_sync_type(DcSyncMode::DcSynchron01);
                Ok(())
            }
        }
    }

    fn configure_sm_sync_type(&self, mode: DcSyncMode) {
        let sync_type: u8 = match mode {
            DcSyncMode::FreeRun => 0,
            DcSyncMode::SmSynchron => 1,
            DcSyncMode::DcSynchron => 2,
            DcSyncMode::DcSynchron01 => 3,
        };
        unsafe {

            let _ = ffi::SetDcSyncMode(self.master_index, self.slave_index, sync_type);
        }
    }

    pub fn disable(&self) {
        unsafe {
            ffi::SetSyncBySlaveIndex(self.master_index, self.slave_index, 0, 0, 0);
        }
    }

    pub fn propagation_delay(&self) -> i32 {
        unsafe { ffi::GetSlavePropagationDelay(self.master_index, self.slave_index) }
    }

    pub fn has_dc(&self) -> bool {
        let ptr = unsafe { ffi::GetSlave(self.master_index, self.slave_index) };
        if ptr.is_null() { return false; }
        let s = unsafe { std::ptr::read_unaligned(ptr as *const ffi::EcSlaveBlittable) };
        s.topo.has_dc != 0
    }

    pub fn dc_sync_mode_from_esi(&self) -> i32 {
        unsafe { ffi::EcEsi_GetDeviceDcSyncMode(self.master_index, self.slave_index) }
    }

    pub fn has_esi_dc_sync(&self) -> bool {
        let mode = self.dc_sync_mode_from_esi();
        if mode >= 0 {
            return mode >= 2;
        }
        if mode == -1 {
            return false;
        }

        self.has_dc()
    }

    pub fn sync_window_status(&self) -> Option<SyncWindowStatus> {
        let mut diff_ns: i32 = 0;
        let mut max_diff_ns: i32 = 0;
        let mut min_diff_ns: i32 = 0;
        let mut in_sync: i32 = 0;
        let mut out_of_sync_count: u32 = 0;
        let ret = unsafe {
            ffi::GetSlaveSyncWindowStatus(
                self.master_index, self.slave_index,
                &mut diff_ns, &mut max_diff_ns, &mut min_diff_ns,
                &mut in_sync, &mut out_of_sync_count,
            )
        };
        if ret != 0 {
            Some(SyncWindowStatus {
                diff_ns,
                max_diff_ns,
                min_diff_ns,
                in_sync: in_sync != 0,
                out_of_sync_count,
            })
        } else {
            None
        }
    }

    pub fn reset_sync_window_stats(&self) {
        unsafe { ffi::ResetSlaveSyncWindowStats(self.master_index, self.slave_index) };
    }

    pub fn current_dc_sync_mode(&self) -> DcSyncMode {
        let mode = unsafe { ffi::GetDcSyncMode(self.master_index, self.slave_index) };
        match mode {
            1 => DcSyncMode::SmSynchron,
            2 => DcSyncMode::DcSynchron,
            3 => DcSyncMode::DcSynchron01,
            _ => DcSyncMode::FreeRun,
        }
    }
}

pub struct MasterDC {
    master_index: u16,
}

impl MasterDC {

    pub fn new(master_index: u16) -> Self {
        Self { master_index }
    }

    pub fn enable_continuous_measurement(&self, enabled: bool, interval_sec: u32) {
        unsafe {
            ffi::EnableContinuousMeasurement(
                self.master_index,
                if enabled { 1 } else { 0 },
                interval_sec,
            );
        }
    }

    pub fn enable_drift_compensation(&self, enabled: bool, threshold_ns: i32, gain: i32) {
        unsafe {
            ffi::EnableDriftCompensation(
                self.master_index,
                if enabled { 1 } else { 0 },
                threshold_ns,
                gain,
            );
        }
    }

    pub fn get_max_sync_difference(&self) -> i32 {
        unsafe { ffi::GetMaxSyncDifference(self.master_index) }
    }

    pub fn is_all_slaves_in_sync(&self) -> bool {
        unsafe { ffi::IsAllSlavesInSync(self.master_index) != 0 }
    }

    pub fn reset_all_sync_window_stats(&self) {
        unsafe { ffi::ResetSlaveSyncWindowStats(self.master_index, 0); }
    }

    pub fn set_sync_window_threshold(&self, threshold_ns: i32) {
        unsafe { ffi::SetSyncWindowThreshold(self.master_index, threshold_ns); }
    }

    pub fn get_sync_window_threshold(&self) -> i32 {
        unsafe { ffi::GetSyncWindowThreshold(self.master_index) }
    }

    pub fn set_auto_shift_enabled(&self, enabled: bool) {
        unsafe { ffi::SetDCAutoShiftEnabled(self.master_index, if enabled { 1 } else { 0 }); }
    }

    pub fn get_auto_shift_enabled(&self) -> bool {
        unsafe { ffi::GetDCAutoShiftEnabled(self.master_index) != 0 }
    }

    pub fn master_dc_time(&self) -> u64 {
        let t = unsafe { ffi::GetMasterDCTime(self.master_index) };
        if t < 0 { 0 } else { t as u64 }
    }

    pub fn reference_clock_slave_index(&self) -> u16 {
        unsafe { ffi::GetReferenceClockSlaveIndex(self.master_index) }
    }
}
