
pub mod constants {

    pub const ECT_MBXPROT_AOE: u16 = 0x0001;

    pub const ECT_MBXPROT_EOE: u16 = 0x0002;

    pub const ECT_MBXPROT_COE: u16 = 0x0004;

    pub const ECT_MBXPROT_FOE: u16 = 0x0008;

    pub const ECT_MBXPROT_SOE: u16 = 0x0010;

    pub const ECT_MBXPROT_VOE: u16 = 0x0020;

    pub const ECT_COEDET_SDO: u8 = 0x01;

    pub const ECT_COEDET_SDOINFO: u8 = 0x02;

    pub const ECT_COEDET_PDOASSIGN: u8 = 0x04;

    pub const ECT_COEDET_PDOCONFIG: u8 = 0x08;

    pub const ECT_COEDET_UPLOAD: u8 = 0x10;

    pub const ECT_COEDET_SDOCA: u8 = 0x20;
}

#[derive(Clone, Debug, Default)]
#[repr(C)]
pub struct PdoStats {
    pub read_count: u32,
    pub write_count: u32,
    pub error_count: u32,
    pub total_bytes_read: u32,
    pub total_bytes_written: u32,
    pub last_cycle_time_ns: u64,
    pub min_cycle_time_ns: u64,
    pub max_cycle_time_ns: u64,
    pub avg_cycle_time_ns: u64,
}

#[derive(Clone, Debug)]
#[repr(C, packed)]
pub struct PdoMappingEntry {
    pub index: u16,
    pub sub_index: u8,
    pub bit_length: u8,
    pub bit_offset: u16,
    pub name: [u8; 64],
    pub unit: [u8; 16],
    pub readable: u8,
    pub writable: u8,
}

#[derive(Clone, Debug, Default)]
#[repr(C, packed)]
pub struct CommunicationStatsLocal {
    pub total_cycles: u32,
    pub successful_cycles: u32,
    pub failed_cycles: u32,
    pub timeout_cycles: u32,
    pub average_cycle_time_us: f64,
    pub max_cycle_time_us: f64,
    pub min_cycle_time_us: f64,
}

#[derive(Clone, Debug, Default)]
pub struct SlaveInfo {
    pub vendor_id: u32,
    pub product_code: u32,
    pub revision_number: u32,
    pub serial_number: u32,
    pub device_name: String,
    pub vendor_name: String,
    pub sync_manager_count: u16,
    pub fmmu_count: u16,
    pub supports_coe: bool,
    pub supports_foe: bool,
    pub supports_eoe: bool,
    pub supports_soe: bool,
}

#[derive(Clone, Debug, Default)]
#[repr(C, packed)]
pub struct RealtimeStats {
    pub current_cycle_time_us: f64,
    pub jitter_us: f64,
    pub lost_frames: u32,
    pub corrupted_frames: u32,
    pub bus_utilization: f64,
}

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

    pub name: [u8; 128],

    pub desc: [u8; 128],

    pub slave_num: i16,

    pub redundant_slave_num: i16,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct EcSmt {

    pub start_addr: u16,

    pub sm_length: u16,

    pub sm_flags: u32,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
pub struct EcFmmut {

    pub log_start: u32,

    pub log_length: u16,

    pub log_start_bit: u8,

    pub log_end_bit: u8,

    pub phys_start: u16,

    pub phys_start_bit: u8,

    pub fmmu_type: u8,

    pub fmmu_active: u8,

    pub unused1: u8,

    pub unused2: u16,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveIoDesc {

    pub bits: u16,

    pub bytes: u32,

    pub ptr: usize,

    pub offset: u32,

    pub startbit: u8,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveFsoe {

    pub capable: u8,

    pub connection: usize,

    pub sm_context: usize,

    pub connection_id: u16,

    pub safety_address: u16,

    pub safe_input_size: u16,

    pub safe_output_size: u16,

    pub pdo_input_offset: u32,

    pub pdo_output_offset: u32,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveMailbox {

    pub length: u16,

    pub write_offset: u16,

    pub read_length: u16,

    pub read_offset: u16,

    pub supported_proto: u16,

    pub cnt: u8,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveEepromConfig {

    pub _reserved_sii: u16,

    pub read_8byte: u8,

    pub pdi: u8,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveHandler {

    pub state: i32,

    pub rmp_state: i32,

    pub instate_ex: u16,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveIdentity {

    pub vendor_id: u32,

    pub product_id: u32,

    pub revision: u32,

    pub serial: u32,

    pub itype: u16,

    pub dtype: u16,
}

#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct SlaveMetadata {

    pub identity: SlaveIdentity,

    pub group_name: [u8; 41],

    pub device_name: [u8; 41],

    pub sm_count: u16,
}

impl Default for SlaveMetadata {
    fn default() -> Self {
        Self {
            identity: SlaveIdentity::default(),
            group_name: [0; 41],
            device_name: [0; 41],
            sm_count: 0,
        }
    }
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveCapabilities {

    pub is_optional: u8,

    pub supports_frame_repeat: u8,

    pub mailbox_side: u8,

    pub coe_details: u8,

    pub foe_details: u8,

    pub eoe_details: u8,

    pub soe_details: u8,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveRuntime {

    pub ebus_current: i16,

    pub block_lrw: u8,

    pub group: u8,

    pub is_lost: u8,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveSmFmmu {

    pub sm_type: [u8; 8],

    pub sm_app_length: [u16; 8],

    pub fmmu_func: [u8; 4],

    pub fmmu_unused: u8,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveProtoMbx {

    pub in_ptr: usize,

    pub in_full: u8,

    pub overrun: i32,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlavePdoConfig {

    pub assignment_enabled: u8,

    pub configuration_enabled: u8,

    pub config_initialized: u8,

    pub supports_complete_access: u8,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveTopology {

    pub has_dc: u8,

    pub phy_type: u8,

    pub link_count: u8,

    pub active_ports: u8,

    pub consumed_ports: u8,

    pub parent: u16,

    pub parent_port: u8,

    pub entry_port: u8,
}

#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct SlaveDc {

    pub recvtime: [i32; 4],

    pub propagation_delay: i32,

    pub next: u16,

    pub prev: u16,

    pub cycle0: i32,

    pub cycle1: i32,

    pub shift: i32,

    pub active: u16,
}

#[derive(Clone, Debug)]
#[repr(C)]
pub struct EcSlave {

    pub state: u16,

    pub al_status_code: u16,

    pub config_addr: u16,

    pub alias_addr: u16,

    pub fsoe: SlaveFsoe,

    pub output: SlaveIoDesc,

    pub input: SlaveIoDesc,

    pub sm: [EcSmt; 8],

    pub fmmu: [EcFmmut; 4],

    pub sm_fmmu: SlaveSmFmmu,

    pub mbx: SlaveMailbox,

    pub topo: SlaveTopology,

    pub dc: SlaveDc,

    pub eeprom_config: SlaveEepromConfig,

    pub runtime: SlaveRuntime,

    pub po2so_config: usize,

    pub handler: SlaveHandler,

    pub coe: SlaveProtoMbx,

    pub soe: SlaveProtoMbx,

    pub foe: SlaveProtoMbx,

    pub eoe: SlaveProtoMbx,

    pub voe: SlaveProtoMbx,

    pub aoe: SlaveProtoMbx,

    pub mbx_status: usize,

    pub metadata: SlaveMetadata,

    pub pdo_config: SlavePdoConfig,

    pub capabilities: SlaveCapabilities,
}

#[derive(Clone, Debug)]
#[repr(C)]
pub struct EcState {

    pub state: u16,

    pub al_status_code: u16,

    pub slave_count: i32,

    pub loop_cycle: u32,

    pub dc_cycle: u32,

    pub dc_time: i64,

    pub iomap: [u8; 65536],

    pub iomap_mutex: usize,

    pub iomap_buffer: [u8; 65536],

    pub buffer_version: u32,

    pub buffer_dirty: u8,

    pub mutex_protection: u8,

    pub pdo_cpu_affinity: i8,

    pub dc_auto_shift_enabled: u8,

    pub use_udp: u8,

    pub adaptive_timeout_enabled: u8,

    pub overlapping_groups: u8,

    pub packed_mode: u8,

    pub frame_high_priority: u8,

    pub vlan_id: u16,

    pub vlan_priority: u8,

    pub timeout_init_to_preop: u32,

    pub timeout_preop_to_safeop: u32,

    pub timeout_safeop_to_op: u32,

    pub wd_pd_timeout_ms: u16,

    pub wd_pdi_timeout_ms: u16,

    pub filter_threshold: u32,

    pub frame_repeat_count: u8,

    pub active_group_count: u8,

    pub _group_pad: [u8; 2],

    pub group_config_raw: [u8; 160],
}

#[derive(Clone, Debug)]
#[repr(C)]
pub struct InternalDiagnostics {

    pub cache_cnt: u32,

    pub rt_cnt: u32,

    pub cycle_count: u32,

    pub error_cache_cnt: u32,

    pub error_cnt: u32,

    pub wkc: u16,

    pub expected_wkc: u16,

    pub cycle_time_span: u32,

    pub frame_errors: u32,

    pub lost_frames: u32,

    pub checksum_errors: u32,

    pub timeout_frames: u32,

    pub rx_error_count: [u32; 512],

    pub tx_error_count: [u32; 512],

    pub lost_link_count: [u16; 512],

    pub invalid_frame_count: [u16; 512],

    pub working_counter_errors: u16,

    pub consecutive_wkc_errors: u16,

    pub total_wkc_mismatches: u32,

    pub cycle_start_time: u64,

    pub cycle_time_accumulator: f64,

    pub last_snapshot_time: u64,

    pub snapshot_cycle_count: u32,

    pub last_cycle_time_us: f64,

    pub jitter_accumulator: f64,

    pub max_jitter_in_second: f64,

    pub jitter_sample_count: u32,

}

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

    pub enable_crc: bool,

    pub strict_mode: bool,

    pub auto_append_crc: bool,

    pub expected_crc: u32,
}

impl Default for FoEOptionsLocal {
    fn default() -> Self {
        Self {
            enable_crc: false,
            strict_mode: true,
            auto_append_crc: true,
            expected_crc: 0,
        }
    }
}

pub struct EcGroupConfigHelper;

impl EcGroupConfigHelper {

    const GROUP_CONFIG_SIZE: usize = 20;

    pub fn get_group_enabled(raw: &[u8], group: u8) -> bool {
        if group >= 8 { return false; }
        raw[group as usize * Self::GROUP_CONFIG_SIZE] != 0
    }

    pub fn set_group_enabled(raw: &mut [u8], group: u8, enabled: bool) {
        if group >= 8 { return; }
        raw[group as usize * Self::GROUP_CONFIG_SIZE] = if enabled { 1 } else { 0 };
    }

    pub fn get_group_cycle_divider(raw: &[u8], group: u8) -> u8 {
        if group >= 8 { return 0; }
        raw[group as usize * Self::GROUP_CONFIG_SIZE + 1]
    }

    pub fn set_group_cycle_divider(raw: &mut [u8], group: u8, divider: u8) {
        if group >= 8 { return; }
        raw[group as usize * Self::GROUP_CONFIG_SIZE + 1] = divider;
    }

    pub fn get_group_expected_wkc(raw: &[u8], group: u8) -> u16 {
        if group >= 8 { return 0; }
        let off = group as usize * Self::GROUP_CONFIG_SIZE + 2;
        u16::from_le_bytes([raw[off], raw[off + 1]])
    }

    pub fn get_group_slave_count(raw: &[u8], group: u8) -> u16 {
        if group >= 8 { return 0; }
        let off = group as usize * Self::GROUP_CONFIG_SIZE + 4;
        u16::from_le_bytes([raw[off], raw[off + 1]])
    }

    pub fn get_group_frame_repeat_eligible(raw: &[u8], group: u8) -> bool {
        if group >= 8 { return false; }
        raw[group as usize * Self::GROUP_CONFIG_SIZE + 6] != 0
    }

    pub fn set_group_frame_repeat_eligible(raw: &mut [u8], group: u8, eligible: bool) {
        if group >= 8 { return; }
        raw[group as usize * Self::GROUP_CONFIG_SIZE + 6] = if eligible { 1 } else { 0 };
    }

    pub fn get_group_actual_wkc(raw: &[u8], group: u8) -> u16 {
        if group >= 8 { return 0; }
        let off = group as usize * Self::GROUP_CONFIG_SIZE + 8;
        u16::from_le_bytes([raw[off], raw[off + 1]])
    }

    pub fn get_group_consecutive_miss(raw: &[u8], group: u8) -> u16 {
        if group >= 8 { return 0; }
        let off = group as usize * Self::GROUP_CONFIG_SIZE + 10;
        u16::from_le_bytes([raw[off], raw[off + 1]])
    }

    pub fn get_group_total_frames(raw: &[u8], group: u8) -> u32 {
        if group >= 8 { return 0; }
        let off = group as usize * Self::GROUP_CONFIG_SIZE + 12;
        u32::from_le_bytes([raw[off], raw[off + 1], raw[off + 2], raw[off + 3]])
    }

    pub fn get_group_mismatch_frames(raw: &[u8], group: u8) -> u32 {
        if group >= 8 { return 0; }
        let off = group as usize * Self::GROUP_CONFIG_SIZE + 16;
        u32::from_le_bytes([raw[off], raw[off + 1], raw[off + 2], raw[off + 3]])
    }
}
