use crate::{
    abstraction_element,
    communication::{AbstractCluster, FlexrayChannelName, FlexrayPhysicalChannel},
    AbstractionElement, ArPackage, AutosarAbstractionError,
};
use autosar_data::{Element, ElementName, EnumItem};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FlexrayCluster(Element);
abstraction_element!(FlexrayCluster, FlexrayCluster);
impl FlexrayCluster {
    pub(crate) fn new(
        name: &str,
        package: &ArPackage,
        settings: &FlexrayClusterSettings,
    ) -> Result<Self, AutosarAbstractionError> {
        let elem_pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
        let elem_cluster = elem_pkg_elements.create_named_sub_element(ElementName::FlexrayCluster, name)?;
        if let Ok(cluster_content) = elem_cluster
            .create_sub_element(ElementName::FlexrayClusterVariants)
            .and_then(|fcv| fcv.create_sub_element(ElementName::FlexrayClusterConditional))
        {
            cluster_content
                .create_sub_element(ElementName::ProtocolName)
                .and_then(|pn| pn.set_character_data("FlexRay"))?;
            cluster_content
                .create_sub_element(ElementName::ProtocolVersion)
                .and_then(|pv| pv.set_character_data("2.1"))?;
            cluster_content.create_sub_element(ElementName::PhysicalChannels)?;
        }
        let flexray_cluster = FlexrayCluster(elem_cluster);
        flexray_cluster.update_settings(settings);
        Ok(flexray_cluster)
    }
    pub fn update_settings(&self, settings: &FlexrayClusterSettings) {
        if let Ok(cluster_content) = self
            .0
            .get_or_create_sub_element(ElementName::FlexrayClusterVariants)
            .and_then(|fcv| fcv.get_or_create_sub_element(ElementName::FlexrayClusterConditional))
        {
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::Baudrate)
                .and_then(|br| br.set_character_data(settings.baudrate.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::ActionPointOffset)
                .and_then(|apo| apo.set_character_data(settings.action_point_offset.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::Bit)
                .and_then(|bit| bit.set_character_data(settings.bit));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::CasRxLowMax)
                .and_then(|crlm| crlm.set_character_data(settings.cas_rx_low_max.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::ColdStartAttempts)
                .and_then(|csa| csa.set_character_data(settings.cold_start_attempts.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::Cycle)
                .and_then(|apo| apo.set_character_data(settings.cycle));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::CycleCountMax)
                .and_then(|ccm| ccm.set_character_data(settings.cycle_count_max.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::DetectNitError)
                .and_then(|dne| dne.set_character_data(settings.detect_nit_error.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::DynamicSlotIdlePhase)
                .and_then(|dsip| dsip.set_character_data(settings.dynamic_slot_idle_phase.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::IgnoreAfterTx)
                .and_then(|iat| iat.set_character_data(settings.ignore_after_tx.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::ListenNoise)
                .and_then(|ln| ln.set_character_data(settings.listen_noise.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::MacroPerCycle)
                .and_then(|mpc| mpc.set_character_data(settings.macro_per_cycle.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::MacrotickDuration)
                .and_then(|mpc| mpc.set_character_data(settings.macrotick_duration));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::MaxWithoutClockCorrectionFatal)
                .and_then(|mwccf| mwccf.set_character_data(settings.max_without_clock_correction_fatal.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::MaxWithoutClockCorrectionPassive)
                .and_then(|mwccp| mwccp.set_character_data(settings.max_without_clock_correction_passive.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::MinislotActionPointOffset)
                .and_then(|mapo| mapo.set_character_data(settings.minislot_action_point_offset.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::MinislotDuration)
                .and_then(|md| md.set_character_data(settings.minislot_duration.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::NetworkIdleTime)
                .and_then(|nit| nit.set_character_data(settings.network_idle_time.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::NetworkManagementVectorLength)
                .and_then(|nmvl| nmvl.set_character_data(settings.network_management_vector_length.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::NumberOfMinislots)
                .and_then(|nom| nom.set_character_data(settings.number_of_minislots.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::NumberOfStaticSlots)
                .and_then(|noss| noss.set_character_data(settings.number_of_static_slots.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::OffsetCorrectionStart)
                .and_then(|ocs| ocs.set_character_data(settings.offset_correction_start.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::PayloadLengthStatic)
                .and_then(|pls| pls.set_character_data(settings.payload_length_static.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::SafetyMargin)
                .and_then(|sm| sm.set_character_data(settings.safety_margin.to_string()));
            if let Some(sample_clock_period) = settings.sample_clock_period {
                let _ = cluster_content
                    .get_or_create_sub_element(ElementName::SampleClockPeriod)
                    .and_then(|scp| scp.set_character_data(sample_clock_period));
            } else if let Some(scp) = cluster_content.get_sub_element(ElementName::SampleClockPeriod) {
                let _ = cluster_content.remove_sub_element(scp);
            }
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::StaticSlotDuration)
                .and_then(|ssd| ssd.set_character_data(settings.static_slot_duration.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::SymbolWindow)
                .and_then(|sw| sw.set_character_data(settings.symbol_window.to_string()));
            if let Some(symbol_window_action_point_offset) = settings.symbol_window_action_point_offset {
                let _ = cluster_content
                    .get_or_create_sub_element(ElementName::SymbolWindowActionPointOffset)
                    .and_then(|swapo| swapo.set_character_data(symbol_window_action_point_offset.to_string()));
            } else if let Some(swapo) = cluster_content.get_sub_element(ElementName::SymbolWindowActionPointOffset) {
                let _ = cluster_content.remove_sub_element(swapo);
            }
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::SyncFrameIdCountMax)
                .and_then(|sficm| sficm.set_character_data(settings.sync_frame_id_count_max.to_string()));
            if let Some(transceiver_standby_delay) = settings.transceiver_standby_delay {
                let _ = cluster_content
                    .get_or_create_sub_element(ElementName::TranceiverStandbyDelay)
                    .and_then(|tsd| tsd.set_character_data(transceiver_standby_delay));
            } else if let Some(tsd) = cluster_content.get_sub_element(ElementName::TranceiverStandbyDelay) {
                let _ = cluster_content.remove_sub_element(tsd);
            }
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::TransmissionStartSequenceDuration)
                .and_then(|tssd| tssd.set_character_data(settings.transmission_start_sequence_duration.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::WakeupRxIdle)
                .and_then(|wri| wri.set_character_data(settings.wakeup_rx_idle.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::WakeupRxLow)
                .and_then(|wrl| wrl.set_character_data(settings.wakeup_rx_low.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::WakeupRxWindow)
                .and_then(|wrw| wrw.set_character_data(settings.wakeup_rx_window.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::WakeupTxActive)
                .and_then(|wta| wta.set_character_data(settings.wakeup_tx_active.to_string()));
            let _ = cluster_content
                .get_or_create_sub_element(ElementName::WakeupTxIdle)
                .and_then(|wti| wti.set_character_data(settings.wakeup_tx_idle.to_string()));
        }
    }
    #[must_use]
    pub fn settings(&self) -> FlexrayClusterSettings {
        let mut settings = FlexrayClusterSettings {
            baudrate: 0,
            action_point_offset: 0,
            bit: 0.0,
            cas_rx_low_max: 0,
            cold_start_attempts: 0,
            cycle: 0.0,
            cycle_count_max: 0,
            detect_nit_error: false,
            dynamic_slot_idle_phase: 0,
            ignore_after_tx: 0,
            listen_noise: 0,
            macro_per_cycle: 0,
            macrotick_duration: 0.0,
            max_without_clock_correction_fatal: 0,
            max_without_clock_correction_passive: 0,
            minislot_action_point_offset: 0,
            minislot_duration: 0,
            network_idle_time: 0,
            network_management_vector_length: 0,
            number_of_minislots: 0,
            number_of_static_slots: 0,
            offset_correction_start: 0,
            payload_length_static: 0,
            safety_margin: 0,
            sample_clock_period: None,
            static_slot_duration: 0,
            symbol_window: 0,
            symbol_window_action_point_offset: None,
            sync_frame_id_count_max: 0,
            transceiver_standby_delay: None,
            transmission_start_sequence_duration: 0,
            wakeup_rx_idle: 0,
            wakeup_rx_low: 0,
            wakeup_rx_window: 0,
            wakeup_tx_active: 0,
            wakeup_tx_idle: 0,
        };
        if let Some(cluster_content) = self
            .0
            .get_sub_element(ElementName::FlexrayClusterVariants)
            .and_then(|fcv| fcv.get_sub_element(ElementName::FlexrayClusterConditional))
        {
            if let Some(baudrate) = cluster_content
                .get_sub_element(ElementName::Baudrate)
                .and_then(|br| br.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.baudrate = baudrate;
            }
            if let Some(action_point_offset) = cluster_content
                .get_sub_element(ElementName::ActionPointOffset)
                .and_then(|apo| apo.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.action_point_offset = action_point_offset;
            }
            if let Some(bit) = cluster_content
                .get_sub_element(ElementName::Bit)
                .and_then(|bit| bit.character_data())
                .and_then(|cdata| cdata.float_value())
            {
                settings.bit = bit;
            }
            if let Some(cas_rx_low_max) = cluster_content
                .get_sub_element(ElementName::CasRxLowMax)
                .and_then(|crlm| crlm.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.cas_rx_low_max = cas_rx_low_max;
            }
            if let Some(cold_start_attempts) = cluster_content
                .get_sub_element(ElementName::ColdStartAttempts)
                .and_then(|csa| csa.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.cold_start_attempts = cold_start_attempts;
            }
            if let Some(cycle) = cluster_content
                .get_sub_element(ElementName::Cycle)
                .and_then(|apo| apo.character_data())
                .and_then(|cdata| cdata.float_value())
            {
                settings.cycle = cycle;
            }
            if let Some(cycle_count_max) = cluster_content
                .get_sub_element(ElementName::CycleCountMax)
                .and_then(|ccm| ccm.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.cycle_count_max = cycle_count_max;
            }
            if let Some(detect_nit_error) = cluster_content
                .get_sub_element(ElementName::DetectNitError)
                .and_then(|dne| dne.character_data())
                .and_then(|cdata| cdata.string_value())
            {
                settings.detect_nit_error = (detect_nit_error == "true") || (detect_nit_error == "1");
            }
            if let Some(dynamic_slot_idle_phase) = cluster_content
                .get_sub_element(ElementName::DynamicSlotIdlePhase)
                .and_then(|dsip| dsip.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.dynamic_slot_idle_phase = dynamic_slot_idle_phase;
            }
            if let Some(ignore_after_tx) = cluster_content
                .get_sub_element(ElementName::IgnoreAfterTx)
                .and_then(|iat| iat.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.ignore_after_tx = ignore_after_tx;
            }
            if let Some(listen_noise) = cluster_content
                .get_sub_element(ElementName::ListenNoise)
                .and_then(|ln| ln.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.listen_noise = listen_noise;
            }
            if let Some(macro_per_cycle) = cluster_content
                .get_sub_element(ElementName::MacroPerCycle)
                .and_then(|mpc| mpc.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.macro_per_cycle = macro_per_cycle;
            }
            if let Some(macrotick_duration) = cluster_content
                .get_sub_element(ElementName::MacrotickDuration)
                .and_then(|mpc| mpc.character_data())
                .and_then(|cdata| cdata.float_value())
            {
                settings.macrotick_duration = macrotick_duration;
            }
            if let Some(max_without_clock_correction_fatal) = cluster_content
                .get_sub_element(ElementName::MaxWithoutClockCorrectionFatal)
                .and_then(|mwccf| mwccf.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.max_without_clock_correction_fatal = max_without_clock_correction_fatal;
            }
            if let Some(max_without_clock_correction_passive) = cluster_content
                .get_sub_element(ElementName::MaxWithoutClockCorrectionPassive)
                .and_then(|mwccp| mwccp.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.max_without_clock_correction_passive = max_without_clock_correction_passive;
            }
            if let Some(minislot_action_point_offset) = cluster_content
                .get_sub_element(ElementName::MinislotActionPointOffset)
                .and_then(|mapo| mapo.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.minislot_action_point_offset = minislot_action_point_offset;
            }
            if let Some(minislot_duration) = cluster_content
                .get_sub_element(ElementName::MinislotDuration)
                .and_then(|md| md.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.minislot_duration = minislot_duration;
            }
            if let Some(network_idle_time) = cluster_content
                .get_sub_element(ElementName::NetworkIdleTime)
                .and_then(|nit| nit.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.network_idle_time = network_idle_time;
            }
            if let Some(network_management_vector_length) = cluster_content
                .get_sub_element(ElementName::NetworkManagementVectorLength)
                .and_then(|nmvl| nmvl.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.network_management_vector_length = network_management_vector_length;
            }
            if let Some(number_of_minislots) = cluster_content
                .get_sub_element(ElementName::NumberOfMinislots)
                .and_then(|nom| nom.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.number_of_minislots = number_of_minislots;
            }
            if let Some(number_of_static_slots) = cluster_content
                .get_sub_element(ElementName::NumberOfStaticSlots)
                .and_then(|noss| noss.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.number_of_static_slots = number_of_static_slots;
            }
            if let Some(offset_correction_start) = cluster_content
                .get_sub_element(ElementName::OffsetCorrectionStart)
                .and_then(|ocs| ocs.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.offset_correction_start = offset_correction_start;
            }
            if let Some(payload_length_static) = cluster_content
                .get_sub_element(ElementName::PayloadLengthStatic)
                .and_then(|pls| pls.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.payload_length_static = payload_length_static;
            }
            if let Some(safety_margin) = cluster_content
                .get_sub_element(ElementName::SafetyMargin)
                .and_then(|sm| sm.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.safety_margin = safety_margin;
            }
            settings.sample_clock_period = cluster_content
                .get_sub_element(ElementName::SampleClockPeriod)
                .and_then(|scp| scp.character_data())
                .and_then(|cdata| cdata.float_value());
            if let Some(static_slot_duration) = cluster_content
                .get_sub_element(ElementName::StaticSlotDuration)
                .and_then(|ssd| ssd.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.static_slot_duration = static_slot_duration;
            }
            if let Some(symbol_window) = cluster_content
                .get_sub_element(ElementName::SymbolWindow)
                .and_then(|sw| sw.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.symbol_window = symbol_window;
            }
            settings.symbol_window_action_point_offset = cluster_content
                .get_sub_element(ElementName::SymbolWindowActionPointOffset)
                .and_then(|swapo| swapo.character_data())
                .and_then(|cdata| cdata.parse_integer());
            if let Some(sync_frame_id_count_max) = cluster_content
                .get_sub_element(ElementName::SyncFrameIdCountMax)
                .and_then(|sficm| sficm.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.sync_frame_id_count_max = sync_frame_id_count_max;
            }
            settings.transceiver_standby_delay = cluster_content
                .get_sub_element(ElementName::TranceiverStandbyDelay)
                .and_then(|tsd| tsd.character_data())
                .and_then(|cdata| cdata.float_value());
            if let Some(transmission_start_sequence_duration) = cluster_content
                .get_sub_element(ElementName::TransmissionStartSequenceDuration)
                .and_then(|tssd| tssd.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.transmission_start_sequence_duration = transmission_start_sequence_duration;
            }
            if let Some(wakeup_rx_idle) = cluster_content
                .get_sub_element(ElementName::WakeupRxIdle)
                .and_then(|wri| wri.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.wakeup_rx_idle = wakeup_rx_idle;
            }
            if let Some(wakeup_rx_low) = cluster_content
                .get_sub_element(ElementName::WakeupRxLow)
                .and_then(|wrl| wrl.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.wakeup_rx_low = wakeup_rx_low;
            }
            if let Some(wakeup_rx_window) = cluster_content
                .get_sub_element(ElementName::WakeupRxWindow)
                .and_then(|wrw| wrw.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.wakeup_rx_window = wakeup_rx_window;
            }
            if let Some(wakeup_tx_active) = cluster_content
                .get_sub_element(ElementName::WakeupTxActive)
                .and_then(|wta| wta.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.wakeup_tx_active = wakeup_tx_active;
            }
            if let Some(wakeup_tx_idle) = cluster_content
                .get_sub_element(ElementName::WakeupTxIdle)
                .and_then(|wti| wti.character_data())
                .and_then(|cdata| cdata.parse_integer())
            {
                settings.wakeup_tx_idle = wakeup_tx_idle;
            }
        }
        settings
    }
    pub fn create_physical_channel(
        &self,
        name: &str,
        channel_name: FlexrayChannelName,
    ) -> Result<FlexrayPhysicalChannel, AutosarAbstractionError> {
        let phys_channels = self
            .0
            .get_or_create_sub_element(ElementName::FlexrayClusterVariants)?
            .get_or_create_sub_element(ElementName::FlexrayClusterConditional)?
            .get_or_create_sub_element(ElementName::PhysicalChannels)?;
        for existing_channel in phys_channels.sub_elements() {
            if let Some(existing_channel_name) = FlexrayPhysicalChannel::try_from(existing_channel)
                .ok()
                .and_then(|channel| channel.channel_name())
            {
                if existing_channel_name == channel_name {
                    return Err(AutosarAbstractionError::ItemAlreadyExists);
                }
            }
        }
        let channel = phys_channels.create_named_sub_element(ElementName::FlexrayPhysicalChannel, name)?;
        let cn_item = match channel_name {
            FlexrayChannelName::A => EnumItem::ChannelA,
            FlexrayChannelName::B => EnumItem::ChannelB,
        };
        let _ = channel
            .create_sub_element(ElementName::ChannelName)
            .and_then(|cn| cn.set_character_data(cn_item));
        FlexrayPhysicalChannel::try_from(channel)
    }
    #[must_use]
    pub fn physical_channels(&self) -> FlexrayPhysicalChannelsInfo {
        let mut channel_info = FlexrayPhysicalChannelsInfo {
            channel_a: None,
            channel_b: None,
        };
        if let Some(phys_channels) = self
            .0
            .get_sub_element(ElementName::FlexrayClusterVariants)
            .and_then(|fcv| fcv.get_sub_element(ElementName::FlexrayClusterConditional))
            .and_then(|fcc| fcc.get_sub_element(ElementName::PhysicalChannels))
        {
            for channel_elem in phys_channels.sub_elements() {
                if let Ok(channel) = FlexrayPhysicalChannel::try_from(channel_elem) {
                    match channel.channel_name() {
                        Some(FlexrayChannelName::A) => channel_info.channel_a = Some(channel),
                        Some(FlexrayChannelName::B) => channel_info.channel_b = Some(channel),
                        None => {}
                    }
                }
            }
        }
        channel_info
    }
}
impl AbstractCluster for FlexrayCluster {}
#[derive(Debug, Clone, PartialEq)]
pub struct FlexrayPhysicalChannelsInfo {
    pub channel_a: Option<FlexrayPhysicalChannel>,
    pub channel_b: Option<FlexrayPhysicalChannel>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FlexrayClusterSettings {
    pub baudrate: u32,
    pub action_point_offset: u8,
    pub bit: f64,
    pub cas_rx_low_max: u8,
    pub cold_start_attempts: u8,
    pub cycle: f64,
    pub cycle_count_max: u8,
    pub detect_nit_error: bool,
    pub dynamic_slot_idle_phase: u8,
    pub ignore_after_tx: u16,
    pub listen_noise: u8,
    pub macro_per_cycle: u16,
    pub macrotick_duration: f64,
    pub max_without_clock_correction_fatal: u8,
    pub max_without_clock_correction_passive: u8,
    pub minislot_action_point_offset: u8,
    pub minislot_duration: u8,
    pub network_idle_time: u16,
    pub network_management_vector_length: u8,
    pub number_of_minislots: u16,
    pub number_of_static_slots: u16,
    pub offset_correction_start: u16,
    pub payload_length_static: u16,
    pub safety_margin: u16,
    pub sample_clock_period: Option<f64>,
    pub static_slot_duration: u16,
    pub symbol_window: u8,
    pub symbol_window_action_point_offset: Option<u8>,
    pub sync_frame_id_count_max: u8,
    pub transceiver_standby_delay: Option<f64>,
    pub transmission_start_sequence_duration: u8,
    pub wakeup_rx_idle: u16,
    pub wakeup_rx_low: u8,
    pub wakeup_rx_window: u16,
    pub wakeup_tx_active: u16,
    pub wakeup_tx_idle: u16,
}
impl FlexrayClusterSettings {
    #[must_use]
    pub fn new() -> Self {
        Self {
            baudrate: 10_000_000,
            action_point_offset: 4,
            bit: 1e-7,
            cas_rx_low_max: 80,
            cold_start_attempts: 16,
            cycle: 0.005,
            cycle_count_max: 63,
            detect_nit_error: true,
            dynamic_slot_idle_phase: 0,
            ignore_after_tx: 7,
            listen_noise: 2,
            macro_per_cycle: 5000,
            macrotick_duration: 1e-6,
            max_without_clock_correction_fatal: 5,
            max_without_clock_correction_passive: 3,
            minislot_action_point_offset: 3,
            minislot_duration: 10,
            network_idle_time: 46,
            network_management_vector_length: 0,
            number_of_minislots: 185,
            number_of_static_slots: 50,
            offset_correction_start: 4980,
            payload_length_static: 32,
            safety_margin: 1,
            sample_clock_period: Some(1.25e-8),
            static_slot_duration: 62,
            symbol_window: 0,
            symbol_window_action_point_offset: None,
            sync_frame_id_count_max: 8,
            transceiver_standby_delay: None,
            transmission_start_sequence_duration: 9,
            wakeup_rx_idle: 50,
            wakeup_rx_low: 50,
            wakeup_rx_window: 301,
            wakeup_tx_active: 60,
            wakeup_tx_idle: 180,
        }
    }
    #[must_use]
    pub fn verify(&self) -> bool {
        if 1.0 / f64::from(self.baudrate) != self.bit {
            return false;
        }
        if self.cycle > 0.016 {
            return false;
        }
        if self.cycle_count_max != 63 {
            return false;
        }
        if self.payload_length_static > 127 {
            return false;
        }
        if self.static_slot_duration < 4 || self.static_slot_duration > 661 {
            return false;
        }
        if self.minislot_duration < 2 || self.minislot_duration > 63 {
            return false;
        }
        if self.action_point_offset < 1
            || self.action_point_offset > 63
            || u16::from(self.action_point_offset) >= self.static_slot_duration
        {
            return false;
        }
        if self.minislot_action_point_offset < 1 || self.minislot_action_point_offset > 31 {
            return false;
        }
        if self.cas_rx_low_max < 67 || self.cas_rx_low_max > 99 {
            return false;
        }
        if let Some(sample_clock_period) = self.sample_clock_period {
            if sample_clock_period != 1.25e-8 && sample_clock_period != 2.5e-8 && sample_clock_period != 5e-8 {
                return false;
            }
            if self.bit != (sample_clock_period * 8.0) {
                return false;
            }
        }
        if self.symbol_window > 142 {
            return false;
        }
        if self.macro_per_cycle < 10 || self.macro_per_cycle > 16000 {
            return false;
        }
        if self.cycle / f64::from(self.macro_per_cycle) != self.macrotick_duration {
            return false;
        }
        if self.network_idle_time < 2 || self.network_idle_time > 805 {
            return false;
        }
        if self.dynamic_slot_idle_phase > 2 {
            return false;
        }
        if self.transmission_start_sequence_duration < 3 || self.transmission_start_sequence_duration > 15 {
            return false;
        }
        if self.number_of_static_slots > 1023 || self.number_of_static_slots + self.number_of_minislots > 2047 {
            return false;
        }
        let static_segment_length = u32::from(self.static_slot_duration) * u32::from(self.number_of_static_slots);
        let dynamic_segment_length = u32::from(self.minislot_duration) * u32::from(self.number_of_minislots);
        if (static_segment_length + dynamic_segment_length + u32::from(self.network_idle_time))
            > u32::from(self.macro_per_cycle)
        {
            return false;
        }
        let static_frame_size = 5 + 2 * self.payload_length_static + 3;
        let bits_per_macrotick = self.macrotick_duration / self.bit;
        let static_frame_bits =
            f64::from(self.static_slot_duration - u16::from(self.action_point_offset)) * bits_per_macrotick;
        if (static_frame_bits as u32) < u32::from(static_frame_size * 8) {
            return false;
        }
        if (self.offset_correction_start > self.macro_per_cycle)
            || (self.offset_correction_start < self.macro_per_cycle - self.network_idle_time)
        {
            return false;
        }
        if self.cold_start_attempts < 2 || self.cold_start_attempts > 31 {
            return false;
        }
        if self.wakeup_rx_idle < 14 || self.wakeup_rx_idle > 59 {
            return false;
        }
        if self.wakeup_rx_low < 11 || self.wakeup_rx_low > 59 {
            return false;
        }
        if self.wakeup_rx_window < 76 || self.wakeup_rx_window > 301 {
            return false;
        }
        if self.wakeup_tx_idle < 45 || self.wakeup_tx_idle > 180 {
            return false;
        }
        if self.wakeup_tx_active < 15 || self.wakeup_tx_active > 60 {
            return false;
        }
        if self.listen_noise < 2 || self.listen_noise > 16 {
            return false;
        }
        if (self.max_without_clock_correction_fatal < self.max_without_clock_correction_passive)
            || (self.max_without_clock_correction_fatal > 15)
        {
            return false;
        }
        if self.sync_frame_id_count_max < 2 || self.sync_frame_id_count_max > 15 {
            return false;
        }
        true
    }
}
impl Default for FlexrayClusterSettings {
    fn default() -> Self {
        Self::new()
    }
}
#[cfg(test)]
mod test {
    use crate::{
        communication::{AbstractCluster, FlexrayChannelName, FlexrayClusterSettings},
        ArPackage, SystemCategory,
    };
    use autosar_data::{AutosarModel, AutosarVersion};
    #[test]
    fn cluster() {
        let model = AutosarModel::new();
        model.create_file("filename", AutosarVersion::Autosar_00048).unwrap();
        let pkg = ArPackage::get_or_create(&model, "/test").unwrap();
        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
        let pkg2 = ArPackage::get_or_create(&model, "/flexray").unwrap();
        let settings = FlexrayClusterSettings::default();
        let result = system.create_flexray_cluster("FlxCluster", &pkg2, &settings);
        assert!(result.is_ok());
        let cluster = result.unwrap();
        let settings = FlexrayClusterSettings::default();
        let result = system.create_flexray_cluster("FlxCluster", &pkg2, &settings);
        assert!(result.is_err());
        let linked_system = cluster.system().unwrap();
        assert_eq!(linked_system, system);
        let result = cluster.create_physical_channel("Channel1", FlexrayChannelName::A);
        assert!(result.is_ok());
        let result = cluster.create_physical_channel("Channel2", FlexrayChannelName::B);
        assert!(result.is_ok());
        let result = cluster.create_physical_channel("Channel3", FlexrayChannelName::A);
        assert!(result.is_err());
        let pc = cluster.physical_channels();
        assert!(pc.channel_a.is_some());
        assert!(pc.channel_b.is_some());
    }
    #[test]
    fn flexray_settings() {
        let model = AutosarModel::new();
        model.create_file("filename", AutosarVersion::Autosar_00048).unwrap();
        let pkg = ArPackage::get_or_create(&model, "/test").unwrap();
        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
        let settings = FlexrayClusterSettings::default();
        let cluster = system.create_flexray_cluster("FlxCluster", &pkg, &settings).unwrap();
        let mut settings = FlexrayClusterSettings::new();
        assert!(settings.verify());
        settings.sample_clock_period = Some(1.25e-8);
        settings.symbol_window_action_point_offset = Some(settings.action_point_offset);
        settings.transceiver_standby_delay = Some(1.0);
        assert!(settings.verify());
        cluster.update_settings(&settings);
        let settings2 = cluster.settings();
        assert_eq!(settings, settings2);
        settings.baudrate = 0;
        assert!(!settings.verify());
        settings.baudrate = settings2.baudrate;
        settings.cycle = 0.0161;
        assert!(!settings.verify());
        settings.cycle = settings2.cycle;
        settings.cycle_count_max = 64;
        assert!(!settings.verify());
        settings.cycle_count_max = settings2.cycle_count_max;
        settings.payload_length_static = 128;
        assert!(!settings.verify());
        settings.payload_length_static = settings2.payload_length_static;
        settings.static_slot_duration = 3;
        assert!(!settings.verify());
        settings.static_slot_duration = settings2.static_slot_duration;
        settings.minislot_duration = 64;
        assert!(!settings.verify());
        settings.minislot_duration = settings2.minislot_duration;
        settings.action_point_offset = 64;
        assert!(!settings.verify());
        settings.action_point_offset = settings2.action_point_offset;
        settings.minislot_action_point_offset = 32;
        assert!(!settings.verify());
        settings.minislot_action_point_offset = settings2.minislot_action_point_offset;
        settings.cas_rx_low_max = 66;
        assert!(!settings.verify());
        settings.cas_rx_low_max = settings2.cas_rx_low_max;
        settings.sample_clock_period = Some(1.0);
        assert!(!settings.verify());
        settings.sample_clock_period = settings2.sample_clock_period;
        settings.symbol_window = 143;
        assert!(!settings.verify());
        settings.symbol_window = settings2.symbol_window;
        settings.macro_per_cycle = 9;
        assert!(!settings.verify());
        settings.macro_per_cycle = settings2.macro_per_cycle;
        settings.macro_per_cycle -= 1;
        assert!(!settings.verify());
        settings.macro_per_cycle = settings2.macro_per_cycle;
        settings.network_idle_time = 806;
        assert!(!settings.verify());
        settings.network_idle_time = settings2.network_idle_time;
        settings.dynamic_slot_idle_phase = 3;
        assert!(!settings.verify());
        settings.dynamic_slot_idle_phase = settings2.dynamic_slot_idle_phase;
        settings.transmission_start_sequence_duration = 16;
        assert!(!settings.verify());
        settings.transmission_start_sequence_duration = settings2.transmission_start_sequence_duration;
        settings.number_of_static_slots = 1024;
        assert!(!settings.verify());
        settings.number_of_static_slots = settings2.number_of_static_slots;
        settings.number_of_static_slots += 1;
        assert!(!settings.verify());
        settings.number_of_static_slots = settings2.number_of_static_slots;
        settings.payload_length_static += 1;
        assert!(!settings.verify());
        settings.payload_length_static = settings2.payload_length_static;
        settings.offset_correction_start = settings.macro_per_cycle + 1;
        assert!(!settings.verify());
        settings.offset_correction_start = settings2.offset_correction_start;
        settings.cold_start_attempts = 1;
        assert!(!settings.verify());
        settings.cold_start_attempts = settings2.cold_start_attempts;
        settings.wakeup_rx_idle = 13;
        assert!(!settings.verify());
        settings.wakeup_rx_idle = settings2.wakeup_rx_idle;
        settings.wakeup_rx_low = 10;
        assert!(!settings.verify());
        settings.wakeup_rx_low = settings2.wakeup_rx_low;
        settings.wakeup_rx_window = 75;
        assert!(!settings.verify());
        settings.wakeup_rx_window = settings2.wakeup_rx_window;
        settings.wakeup_tx_idle = 44;
        assert!(!settings.verify());
        settings.wakeup_tx_idle = settings2.wakeup_tx_idle;
        settings.wakeup_tx_active = 14;
        assert!(!settings.verify());
        settings.wakeup_tx_active = settings2.wakeup_tx_active;
        settings.listen_noise = 1;
        assert!(!settings.verify());
        settings.listen_noise = settings2.listen_noise;
        settings.max_without_clock_correction_passive = settings.max_without_clock_correction_fatal + 1;
        assert!(!settings.verify());
        settings.max_without_clock_correction_passive = settings2.max_without_clock_correction_passive;
        settings.sync_frame_id_count_max = 1;
        assert!(!settings.verify());
        settings.sync_frame_id_count_max = settings2.sync_frame_id_count_max;
    }
}