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

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

impl<'a> MasterConfig<'a> {

    pub(crate) fn new(master: &'a EtherCATMaster) -> Self {
        Self { master }
    }

    pub fn loop_cycle(&self) -> u32 {

        unsafe { ffi::GetTimingMode(self.master.index()) }
    }

    pub fn set_loop_cycle(&self, time_ns: u32) {
        unsafe { ffi::SetMasterLoopCycleTime(self.master.index(), time_ns) };
    }

    pub fn frame_high_priority(&self) -> bool {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return false; }

        unsafe {
            let byte_ptr = ptr as *const u8;

            *byte_ptr.add(OFFSET_FRAME_HIGH_PRIORITY) != 0
        }
    }

    pub fn set_frame_high_priority(&self, value: bool) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            *byte_ptr.add(OFFSET_FRAME_HIGH_PRIORITY) = if value { 1 } else { 0 };
        }
    }

    pub fn use_udp(&self) -> bool {
        (unsafe { ffi::GetUdpMode(self.master.index()) }) != 0
    }

    pub fn set_use_udp(&self, value: bool) {
        unsafe { ffi::SetUdpMode(self.master.index(), if value { 1 } else { 0 }) };
    }

    pub fn is_udp_available(&self) -> bool {
        (unsafe { ffi::IsUdpAvailable(self.master.index()) }) != 0
    }

    pub fn vlan_id(&self) -> u16 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 0; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            u16::from_le_bytes([
                *byte_ptr.add(OFFSET_VLAN_ID),
                *byte_ptr.add(OFFSET_VLAN_ID + 1),
            ])
        }
    }

    pub fn set_vlan_id(&self, value: u16) {
        if value > 4095 { return; }
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            let bytes = value.to_le_bytes();
            *byte_ptr.add(OFFSET_VLAN_ID) = bytes[0];
            *byte_ptr.add(OFFSET_VLAN_ID + 1) = bytes[1];
        }
    }

    pub fn vlan_priority(&self) -> u8 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 0; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            *byte_ptr.add(OFFSET_VLAN_PRIORITY)
        }
    }

    pub fn set_vlan_priority(&self, value: u8) {
        if value > 7 { return; }
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            *byte_ptr.add(OFFSET_VLAN_PRIORITY) = value;
        }
    }

    pub fn overlapping_groups(&self) -> bool {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return false; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            *byte_ptr.add(OFFSET_OVERLAPPING_GROUPS) != 0
        }
    }

    pub fn set_overlapping_groups(&self, value: bool) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            *byte_ptr.add(OFFSET_OVERLAPPING_GROUPS) = if value { 1 } else { 0 };
        }
    }

    pub fn packed_mode(&self) -> bool {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return false; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            *byte_ptr.add(OFFSET_PACKED_MODE) != 0
        }
    }

    pub fn set_packed_mode(&self, value: bool) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            *byte_ptr.add(OFFSET_PACKED_MODE) = if value { 1 } else { 0 };
        }
    }

    pub fn filter_threshold(&self) -> u32 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 3; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            u32::from_le_bytes([
                *byte_ptr.add(OFFSET_FILTER_THRESHOLD),
                *byte_ptr.add(OFFSET_FILTER_THRESHOLD + 1),
                *byte_ptr.add(OFFSET_FILTER_THRESHOLD + 2),
                *byte_ptr.add(OFFSET_FILTER_THRESHOLD + 3),
            ])
        }
    }

    pub fn set_filter_threshold(&self, value: u32) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            let bytes = value.to_le_bytes();
            for i in 0..4 {
                *byte_ptr.add(OFFSET_FILTER_THRESHOLD + i) = bytes[i];
            }
        }
    }

    pub fn frame_repeat_count(&self) -> u8 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 1; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            *byte_ptr.add(OFFSET_FRAME_REPEAT_COUNT)
        }
    }

    pub fn set_frame_repeat_count(&self, value: u8) {
        if value < 1 || value > 3 { return; }
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            *byte_ptr.add(OFFSET_FRAME_REPEAT_COUNT) = value;
        }
    }

    pub fn mutex_protection(&self) -> bool {
        (unsafe { ffi::GetMutexProtection(self.master.index()) }) != 0
    }

    pub fn set_mutex_protection(&self, value: bool) {
        unsafe { ffi::SetMutexProtection(self.master.index(), if value { 1 } else { 0 }) };
    }

    pub fn process_data_watchdog_ms(&self) -> u32 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 0; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            u16::from_le_bytes([
                *byte_ptr.add(OFFSET_WD_PD_TIMEOUT_MS),
                *byte_ptr.add(OFFSET_WD_PD_TIMEOUT_MS + 1),
            ]) as u32
        }
    }

    pub fn set_process_data_watchdog_ms(&self, value: u32) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        let clamped = value.min(6553) as u16;
        unsafe {
            let byte_ptr = ptr as *mut u8;
            let bytes = clamped.to_le_bytes();
            *byte_ptr.add(OFFSET_WD_PD_TIMEOUT_MS) = bytes[0];
            *byte_ptr.add(OFFSET_WD_PD_TIMEOUT_MS + 1) = bytes[1];
        }
    }

    pub fn pdi_watchdog_ms(&self) -> u32 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 0; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            u16::from_le_bytes([
                *byte_ptr.add(OFFSET_WD_PDI_TIMEOUT_MS),
                *byte_ptr.add(OFFSET_WD_PDI_TIMEOUT_MS + 1),
            ]) as u32
        }
    }

    pub fn set_pdi_watchdog_ms(&self, value: u32) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        let clamped = value.min(6553) as u16;
        unsafe {
            let byte_ptr = ptr as *mut u8;
            let bytes = clamped.to_le_bytes();
            *byte_ptr.add(OFFSET_WD_PDI_TIMEOUT_MS) = bytes[0];
            *byte_ptr.add(OFFSET_WD_PDI_TIMEOUT_MS + 1) = bytes[1];
        }
    }

    pub fn adaptive_timeout_enabled(&self) -> bool {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return false; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            *byte_ptr.add(OFFSET_ADAPTIVE_TIMEOUT_ENABLED) != 0
        }
    }

    pub fn set_adaptive_timeout_enabled(&self, value: bool) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            *byte_ptr.add(OFFSET_ADAPTIVE_TIMEOUT_ENABLED) = if value { 1 } else { 0 };
        }
    }

    pub fn timeout_init_to_preop(&self) -> u32 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 3000; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            u32::from_le_bytes([
                *byte_ptr.add(OFFSET_TIMEOUT_INIT_TO_PREOP),
                *byte_ptr.add(OFFSET_TIMEOUT_INIT_TO_PREOP + 1),
                *byte_ptr.add(OFFSET_TIMEOUT_INIT_TO_PREOP + 2),
                *byte_ptr.add(OFFSET_TIMEOUT_INIT_TO_PREOP + 3),
            ])
        }
    }

    pub fn set_timeout_init_to_preop(&self, value: u32) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            let bytes = value.to_le_bytes();
            for i in 0..4 {
                *byte_ptr.add(OFFSET_TIMEOUT_INIT_TO_PREOP + i) = bytes[i];
            }
        }
    }

    pub fn timeout_preop_to_safeop(&self) -> u32 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 10000; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            u32::from_le_bytes([
                *byte_ptr.add(OFFSET_TIMEOUT_PREOP_TO_SAFEOP),
                *byte_ptr.add(OFFSET_TIMEOUT_PREOP_TO_SAFEOP + 1),
                *byte_ptr.add(OFFSET_TIMEOUT_PREOP_TO_SAFEOP + 2),
                *byte_ptr.add(OFFSET_TIMEOUT_PREOP_TO_SAFEOP + 3),
            ])
        }
    }

    pub fn set_timeout_preop_to_safeop(&self, value: u32) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            let bytes = value.to_le_bytes();
            for i in 0..4 {
                *byte_ptr.add(OFFSET_TIMEOUT_PREOP_TO_SAFEOP + i) = bytes[i];
            }
        }
    }

    pub fn timeout_safeop_to_op(&self) -> u32 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 5000; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            u32::from_le_bytes([
                *byte_ptr.add(OFFSET_TIMEOUT_SAFEOP_TO_OP),
                *byte_ptr.add(OFFSET_TIMEOUT_SAFEOP_TO_OP + 1),
                *byte_ptr.add(OFFSET_TIMEOUT_SAFEOP_TO_OP + 2),
                *byte_ptr.add(OFFSET_TIMEOUT_SAFEOP_TO_OP + 3),
            ])
        }
    }

    pub fn set_timeout_safeop_to_op(&self, value: u32) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            let bytes = value.to_le_bytes();
            for i in 0..4 {
                *byte_ptr.add(OFFSET_TIMEOUT_SAFEOP_TO_OP + i) = bytes[i];
            }
        }
    }

    pub fn scan_revision_match(&self) -> u8 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 0; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            *byte_ptr.add(OFFSET_SCAN_REVISION_MATCH)
        }
    }

    pub fn set_scan_revision_match(&self, mode: u8) {
        if mode > 2 { return; }
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            *byte_ptr.add(OFFSET_SCAN_REVISION_MATCH) = mode;
        }
    }

    pub fn drift_compensation(&self) -> bool {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return false; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            *byte_ptr.add(OFFSET_DRIFT_COMPENSATION) != 0
        }
    }

    pub fn set_drift_compensation(&self, value: bool) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            *byte_ptr.add(OFFSET_DRIFT_COMPENSATION) = if value { 1 } else { 0 };
        }
    }

    pub fn mailbox_timeout(&self) -> u32 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 5000; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            u32::from_le_bytes([
                *byte_ptr.add(OFFSET_MAILBOX_TIMEOUT),
                *byte_ptr.add(OFFSET_MAILBOX_TIMEOUT + 1),
                *byte_ptr.add(OFFSET_MAILBOX_TIMEOUT + 2),
                *byte_ptr.add(OFFSET_MAILBOX_TIMEOUT + 3),
            ])
        }
    }

    pub fn set_mailbox_timeout(&self, ms: u32) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            let bytes = ms.to_le_bytes();
            for i in 0..4 {
                *byte_ptr.add(OFFSET_MAILBOX_TIMEOUT + i) = bytes[i];
            }
        }
    }

    pub fn mailbox_retries(&self) -> u32 {
        let ptr = unsafe { ffi::GetMasterStateCache(self.master.index()) };
        if ptr.is_null() { return 3; }
        unsafe {
            let byte_ptr = ptr as *const u8;
            u32::from_le_bytes([
                *byte_ptr.add(OFFSET_MAILBOX_RETRIES),
                *byte_ptr.add(OFFSET_MAILBOX_RETRIES + 1),
                *byte_ptr.add(OFFSET_MAILBOX_RETRIES + 2),
                *byte_ptr.add(OFFSET_MAILBOX_RETRIES + 3),
            ])
        }
    }

    pub fn set_mailbox_retries(&self, count: u32) {
        let ptr = unsafe { ffi::GetMasterState(self.master.index()) };
        if ptr.is_null() { return; }
        unsafe {
            let byte_ptr = ptr as *mut u8;
            let bytes = count.to_le_bytes();
            for i in 0..4 {
                *byte_ptr.add(OFFSET_MAILBOX_RETRIES + i) = bytes[i];
            }
        }
    }

    pub fn save_config_xml(&self, path: &str) -> bool {
        let loop_cycle_us = self.loop_cycle() / 1000;
        let xml = format!(
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\
             <DarraEtherCATConfiguration version=\"1.0\">\n\
             \x20 <Master>\n\
             \x20   <CycleTime unit=\"us\">{}</CycleTime>\n\
             \x20   <DcCycleTime unit=\"us\">{}</DcCycleTime>\n\
             \x20   <FrameHighPriority>{}</FrameHighPriority>\n\
             \x20   <UseUdp>{}</UseUdp>\n\
             \x20   <VlanId>{}</VlanId>\n\
             \x20   <VlanPriority>{}</VlanPriority>\n\
             \x20   <OverlappingGroups>{}</OverlappingGroups>\n\
             \x20   <PackedMode>{}</PackedMode>\n\
             \x20   <MutexProtection>{}</MutexProtection>\n\
             \x20   <FilterThreshold>{}</FilterThreshold>\n\
             \x20   <FrameRepeatCount>{}</FrameRepeatCount>\n\
             \x20   <ProcessDataWatchdogMs>{}</ProcessDataWatchdogMs>\n\
             \x20   <PdiWatchdogMs>{}</PdiWatchdogMs>\n\
             \x20   <AdaptiveTimeout>{}</AdaptiveTimeout>\n\
             \x20   <DriftCompensation>{}</DriftCompensation>\n\
             \x20   <ScanRevisionMatch>{}</ScanRevisionMatch>\n\
             \x20   <MailboxTimeout>{}</MailboxTimeout>\n\
             \x20   <MailboxRetries>{}</MailboxRetries>\n\
             \x20 </Master>\n\
             </DarraEtherCATConfiguration>",
            loop_cycle_us,
            loop_cycle_us,
            self.frame_high_priority(),
            self.use_udp(),
            self.vlan_id(),
            self.vlan_priority(),
            self.overlapping_groups(),
            self.packed_mode(),
            self.mutex_protection(),
            self.filter_threshold(),
            self.frame_repeat_count(),
            self.process_data_watchdog_ms(),
            self.pdi_watchdog_ms(),
            self.adaptive_timeout_enabled(),
            self.drift_compensation(),
            self.scan_revision_match(),
            self.mailbox_timeout(),
            self.mailbox_retries(),
        );

        std::fs::write(path, xml.as_bytes()).is_ok()
    }

    pub fn export_config_string(&self) -> Option<String> {

        let json = format!(
            "{{\n\
             \x20 \"loop_cycle_ns\": {},\n\
             \x20 \"frame_high_priority\": {},\n\
             \x20 \"use_udp\": {},\n\
             \x20 \"udp_available\": {},\n\
             \x20 \"vlan_id\": {},\n\
             \x20 \"vlan_priority\": {},\n\
             \x20 \"overlapping_groups\": {},\n\
             \x20 \"packed_mode\": {},\n\
             \x20 \"mutex_protection\": {},\n\
             \x20 \"filter_threshold\": {},\n\
             \x20 \"frame_repeat_count\": {},\n\
             \x20 \"process_data_watchdog_ms\": {},\n\
             \x20 \"pdi_watchdog_ms\": {},\n\
             \x20 \"adaptive_timeout_enabled\": {},\n\
             \x20 \"timeout_init_to_preop\": {},\n\
             \x20 \"timeout_preop_to_safeop\": {},\n\
             \x20 \"timeout_safeop_to_op\": {},\n\
             \x20 \"drift_compensation\": {},\n\
             \x20 \"scan_revision_match\": {},\n\
             \x20 \"mailbox_timeout\": {},\n\
             \x20 \"mailbox_retries\": {}\n\
             }}",
            self.loop_cycle(),
            self.frame_high_priority(),
            self.use_udp(),
            self.is_udp_available(),
            self.vlan_id(),
            self.vlan_priority(),
            self.overlapping_groups(),
            self.packed_mode(),
            self.mutex_protection(),
            self.filter_threshold(),
            self.frame_repeat_count(),
            self.process_data_watchdog_ms(),
            self.pdi_watchdog_ms(),
            self.adaptive_timeout_enabled(),
            self.timeout_init_to_preop(),
            self.timeout_preop_to_safeop(),
            self.timeout_safeop_to_op(),
            self.drift_compensation(),
            self.scan_revision_match(),
            self.mailbox_timeout(),
            self.mailbox_retries(),
        );

        Some(json)
    }
}

const OFFSET_FRAME_HIGH_PRIORITY: usize = 0x100;

const OFFSET_VLAN_ID: usize = 0x102;

const OFFSET_VLAN_PRIORITY: usize = 0x104;

const OFFSET_OVERLAPPING_GROUPS: usize = 0x105;

const OFFSET_PACKED_MODE: usize = 0x106;

const OFFSET_FILTER_THRESHOLD: usize = 0x108;

const OFFSET_FRAME_REPEAT_COUNT: usize = 0x10C;

const OFFSET_WD_PD_TIMEOUT_MS: usize = 0x110;

const OFFSET_WD_PDI_TIMEOUT_MS: usize = 0x112;

const OFFSET_ADAPTIVE_TIMEOUT_ENABLED: usize = 0x114;

const OFFSET_TIMEOUT_INIT_TO_PREOP: usize = 0x118;

const OFFSET_TIMEOUT_PREOP_TO_SAFEOP: usize = 0x11C;

const OFFSET_TIMEOUT_SAFEOP_TO_OP: usize = 0x120;

const OFFSET_DRIFT_COMPENSATION: usize = 0x124;

const OFFSET_SCAN_REVISION_MATCH: usize = 0x125;

const OFFSET_MAILBOX_TIMEOUT: usize = 0x128;

const OFFSET_MAILBOX_RETRIES: usize = 0x12C;

pub fn save_master_settings(path: &str, loop_cycle_us: u16, dc_cycle_us: u16, expected_slave_count: i32) -> bool {
    let xml = format!(
        "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\
         <DarraEtherCATConfiguration version=\"1.0\">\n\
         \x20 <Master>\n\
         \x20   <CycleTime unit=\"us\">{}</CycleTime>\n\
         \x20   <DcCycleTime unit=\"us\">{}</DcCycleTime>\n\
         \x20   <ExpectedSlaveCount>{}</ExpectedSlaveCount>\n\
         \x20 </Master>\n\
         </DarraEtherCATConfiguration>",
        loop_cycle_us, dc_cycle_us, expected_slave_count
    );

    std::fs::write(path, xml.as_bytes()).is_ok()
}

pub fn load_master_settings(path: &str) -> (bool, u16, u16) {

    let mut loop_cycle: u16 = 1000;
    let mut dc_cycle: u16 = 1000;

    let content = match std::fs::read_to_string(path) {
        Ok(c) => c,
        Err(_) => return (false, loop_cycle, dc_cycle),
    };

    if !content.contains("DarraEtherCATConfiguration") {
        return (false, loop_cycle, dc_cycle);
    }

    if let Some(val) = extract_xml_value(&content, "CycleTime") {
        if let Ok(v) = val.parse::<u16>() {
            if v > 0 { loop_cycle = v; }
        }
    }
    if let Some(val) = extract_xml_value(&content, "DcCycleTime") {
        if let Ok(v) = val.parse::<u16>() {
            if v > 0 { dc_cycle = v; }
        }
    }

    (true, loop_cycle, dc_cycle)
}

fn extract_xml_value(xml: &str, tag: &str) -> Option<String> {
    let open_tag = format!("<{}", tag);
    let close_tag = format!("</{}>", tag);

    let start_pos = xml.find(&open_tag)?;
    let after_open = &xml[start_pos..];

    let content_start = after_open.find('>')? + 1;
    let remaining = &after_open[content_start..];
    let end_pos = remaining.find(&close_tag)?;
    let value = remaining[..end_pos].trim();
    if value.is_empty() { None } else { Some(value.to_string()) }
}

impl<'a> std::fmt::Display for MasterConfig<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let cycle_us = self.loop_cycle() as f64 / 1000.0;
        write!(f, "MasterConfig(cycle={:.0}us, mutex={}, mbx_timeout={}ms, mbx_retries={})",
               cycle_us,
               if self.mutex_protection() { "on" } else { "off" },
               self.mailbox_timeout(),
               self.mailbox_retries())
    }
}
