use super::{
    array::{descriptions, names},
    registercluster::{
        AllRegistersIter, AllRegistersIterMut, ClusterIter, ClusterIterMut, RegisterIter,
        RegisterIterMut,
    },
    AddressBlock, BuildError, Cluster, Description, DimElement, EmptyToNone, Interrupt, MaybeArray,
    Name, Register, RegisterCluster, RegisterProperties, SvdError, ValidateLevel,
};
pub type Peripheral = MaybeArray<PeripheralInfo>;
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum Error {
    #[error("Peripheral have `registers` tag, but it is empty")]
    EmptyRegisters,
}
#[cfg_attr(
    feature = "serde",
    derive(serde::Deserialize, serde::Serialize),
    serde(rename_all = "camelCase")
)]
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct PeripheralInfo {
    pub name: String,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub display_name: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub version: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub description: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub alternate_peripheral: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub group_name: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub prepend_to_name: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub append_to_name: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub header_struct_name: Option<String>,
    pub base_address: u64,
    #[cfg_attr(feature = "serde", serde(flatten))]
    pub default_register_properties: RegisterProperties,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub address_block: Option<Vec<AddressBlock>>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Vec::is_empty")
    )]
    pub interrupt: Vec<Interrupt>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub registers: Option<Vec<RegisterCluster>>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub derived_from: Option<String>,
}
pub fn base_addresses<'a>(
    info: &'a PeripheralInfo,
    dim: &'a DimElement,
) -> impl Iterator<Item = u64> + 'a {
    (0..dim.dim as u64).map(|i| info.base_address + i * dim.dim_increment as u64)
}
pub fn expand<'a>(
    info: &'a PeripheralInfo,
    dim: &'a DimElement,
) -> impl Iterator<Item = PeripheralInfo> + 'a {
    dim.indexes()
        .zip(names(info, dim))
        .zip(descriptions(info, dim))
        .zip(base_addresses(info, dim))
        .map(|(((idx, name), description), base_address)| {
            let mut info = info.clone();
            info.name = name;
            info.description = description;
            info.base_address = base_address;
            info.display_name = info
                .display_name
                .map(|d| d.replace("[%s]", &idx).replace("%s", &idx));
            info
        })
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct PeripheralInfoBuilder {
    name: Option<String>,
    display_name: Option<String>,
    version: Option<String>,
    description: Option<String>,
    alternate_peripheral: Option<String>,
    group_name: Option<String>,
    prepend_to_name: Option<String>,
    append_to_name: Option<String>,
    header_struct_name: Option<String>,
    base_address: Option<u64>,
    default_register_properties: RegisterProperties,
    address_block: Option<Vec<AddressBlock>>,
    interrupt: Option<Vec<Interrupt>>,
    registers: Option<Vec<RegisterCluster>>,
    derived_from: Option<String>,
}
impl From<PeripheralInfo> for PeripheralInfoBuilder {
    fn from(p: PeripheralInfo) -> Self {
        Self {
            name: Some(p.name),
            display_name: p.display_name,
            version: p.version,
            description: p.description,
            alternate_peripheral: p.alternate_peripheral,
            group_name: p.group_name,
            prepend_to_name: p.prepend_to_name,
            append_to_name: p.append_to_name,
            header_struct_name: p.header_struct_name,
            base_address: Some(p.base_address),
            default_register_properties: p.default_register_properties,
            address_block: p.address_block,
            interrupt: Some(p.interrupt),
            registers: p.registers,
            derived_from: p.derived_from,
        }
    }
}
impl PeripheralInfoBuilder {
    pub fn name(mut self, value: String) -> Self {
        self.name = Some(value);
        self
    }
    pub fn display_name(mut self, value: Option<String>) -> Self {
        self.display_name = value;
        self
    }
    pub fn version(mut self, value: Option<String>) -> Self {
        self.version = value;
        self
    }
    pub fn description(mut self, value: Option<String>) -> Self {
        self.description = value;
        self
    }
    pub fn alternate_peripheral(mut self, value: Option<String>) -> Self {
        self.alternate_peripheral = value;
        self
    }
    pub fn group_name(mut self, value: Option<String>) -> Self {
        self.group_name = value;
        self
    }
    pub fn prepend_to_name(mut self, value: Option<String>) -> Self {
        self.prepend_to_name = value;
        self
    }
    pub fn append_to_name(mut self, value: Option<String>) -> Self {
        self.append_to_name = value;
        self
    }
    pub fn header_struct_name(mut self, value: Option<String>) -> Self {
        self.header_struct_name = value;
        self
    }
    pub fn base_address(mut self, value: u64) -> Self {
        self.base_address = Some(value);
        self
    }
    pub fn default_register_properties(mut self, value: RegisterProperties) -> Self {
        self.default_register_properties = value;
        self
    }
    pub fn address_block(mut self, value: Option<Vec<AddressBlock>>) -> Self {
        self.address_block = value;
        self
    }
    pub fn interrupt(mut self, value: Option<Vec<Interrupt>>) -> Self {
        self.interrupt = value;
        self
    }
    pub fn registers(mut self, value: Option<Vec<RegisterCluster>>) -> Self {
        self.registers = value;
        self
    }
    pub fn derived_from(mut self, value: Option<String>) -> Self {
        self.derived_from = value;
        self
    }
    pub fn build(self, lvl: ValidateLevel) -> Result<PeripheralInfo, SvdError> {
        let mut per = PeripheralInfo {
            name: self
                .name
                .ok_or_else(|| BuildError::Uninitialized("name".to_string()))?,
            display_name: self.display_name.empty_to_none(),
            version: self.version.empty_to_none(),
            description: self.description.empty_to_none(),
            alternate_peripheral: self.alternate_peripheral.empty_to_none(),
            group_name: self.group_name.empty_to_none(),
            prepend_to_name: self.prepend_to_name.empty_to_none(),
            append_to_name: self.append_to_name.empty_to_none(),
            header_struct_name: self.header_struct_name.empty_to_none(),
            base_address: self
                .base_address
                .ok_or_else(|| BuildError::Uninitialized("base_address".to_string()))?,
            default_register_properties: self.default_register_properties.build(lvl)?,
            address_block: self.address_block,
            interrupt: self.interrupt.unwrap_or_default(),
            registers: self.registers,
            derived_from: self.derived_from,
        };
        if !lvl.is_disabled() {
            per.validate(lvl)?;
        }
        Ok(per)
    }
}
impl PeripheralInfo {
    pub fn builder() -> PeripheralInfoBuilder {
        PeripheralInfoBuilder::default()
    }
    pub const fn single(self) -> Peripheral {
        Peripheral::Single(self)
    }
    pub const fn array(self, dim: DimElement) -> Peripheral {
        Peripheral::Array(self, dim)
    }
    pub fn modify_from(
        &mut self,
        builder: PeripheralInfoBuilder,
        lvl: ValidateLevel,
    ) -> Result<(), SvdError> {
        if let Some(name) = builder.name {
            self.name = name;
        }
        if builder.display_name.is_some() {
            self.display_name = builder.display_name.empty_to_none();
        }
        if builder.version.is_some() {
            self.version = builder.version.empty_to_none();
        }
        if builder.description.is_some() {
            self.description = builder.description.empty_to_none();
        }
        if builder.alternate_peripheral.is_some() {
            self.alternate_peripheral = builder.alternate_peripheral.empty_to_none();
        }
        if builder.group_name.is_some() {
            self.group_name = builder.group_name.empty_to_none();
        }
        if builder.prepend_to_name.is_some() {
            self.prepend_to_name = builder.prepend_to_name.empty_to_none();
        }
        if builder.append_to_name.is_some() {
            self.append_to_name = builder.append_to_name.empty_to_none();
        }
        if builder.header_struct_name.is_some() {
            self.header_struct_name = builder.header_struct_name.empty_to_none();
        }
        if let Some(base_address) = builder.base_address {
            self.base_address = base_address;
        }
        if let Some(interrupt) = builder.interrupt {
            self.interrupt = interrupt;
        }
        if builder.derived_from.is_some() {
            self.derived_from = builder.derived_from;
            self.registers = None;
            self.address_block = None;
            self.default_register_properties = RegisterProperties::default();
        } else {
            if builder.address_block.is_some() {
                self.address_block = builder.address_block;
            }
            self.default_register_properties
                .modify_from(builder.default_register_properties, lvl)?;
            if builder.registers.is_some() {
                self.registers = builder.registers.empty_to_none();
            }
        }
        if !lvl.is_disabled() {
            self.validate(lvl)
        } else {
            Ok(())
        }
    }
    pub fn validate(&mut self, lvl: ValidateLevel) -> Result<(), SvdError> {
        if lvl.is_strict() {
            super::check_dimable_name(&self.name, "name")?;
        }
        if let Some(name) = self.derived_from.as_ref() {
            if lvl.is_strict() {
                super::check_dimable_name(name, "derivedFrom")?;
            }
        } else if let Some(registers) = self.registers.as_ref() {
            if registers.is_empty() && lvl.is_strict() {
                return Err(Error::EmptyRegisters.into());
            }
        }
        Ok(())
    }
    pub fn registers(&self) -> RegisterIter {
        RegisterIter {
            all: match &self.registers {
                Some(regs) => regs.iter(),
                None => [].iter(),
            },
        }
    }
    pub fn registers_mut(&mut self) -> RegisterIterMut {
        RegisterIterMut {
            all: match &mut self.registers {
                Some(regs) => regs.iter_mut(),
                None => [].iter_mut(),
            },
        }
    }
    pub fn clusters(&self) -> ClusterIter {
        ClusterIter {
            all: match &self.registers {
                Some(regs) => regs.iter(),
                None => [].iter(),
            },
        }
    }
    pub fn clusters_mut(&mut self) -> ClusterIterMut {
        ClusterIterMut {
            all: match &mut self.registers {
                Some(regs) => regs.iter_mut(),
                None => [].iter_mut(),
            },
        }
    }
    #[deprecated(since = "0.12.1", note = "Please use `all_registers` instead")]
    pub fn reg_iter(&self) -> AllRegistersIter {
        self.all_registers()
    }
    pub fn all_registers(&self) -> AllRegistersIter {
        AllRegistersIter {
            rem: match &self.registers {
                Some(regs) => regs.iter().rev().collect(),
                None => Vec::new(),
            },
        }
    }
    #[deprecated(since = "0.12.1", note = "Please use `all_registers_mut` instead")]
    pub fn reg_iter_mut(&mut self) -> AllRegistersIterMut {
        self.all_registers_mut()
    }
    pub fn all_registers_mut(&mut self) -> AllRegistersIterMut {
        AllRegistersIterMut {
            rem: match &mut self.registers {
                Some(regs) => regs.iter_mut().rev().collect(),
                None => Vec::new(),
            },
        }
    }
    pub fn get_register(&self, name: &str) -> Option<&Register> {
        self.registers().find(|f| f.name == name)
    }
    pub fn get_mut_register(&mut self, name: &str) -> Option<&mut Register> {
        self.registers_mut().find(|f| f.name == name)
    }
    pub fn get_cluster(&self, name: &str) -> Option<&Cluster> {
        self.clusters().find(|f| f.name == name)
    }
    pub fn get_mut_cluster(&mut self, name: &str) -> Option<&mut Cluster> {
        self.clusters_mut().find(|f| f.name == name)
    }
    pub fn get_interrupt(&self, name: &str) -> Option<&Interrupt> {
        self.interrupt.iter().find(|e| e.name == name)
    }
    pub fn get_mut_interrupt(&mut self, name: &str) -> Option<&mut Interrupt> {
        self.interrupt.iter_mut().find(|e| e.name == name)
    }
}
impl Name for PeripheralInfo {
    fn name(&self) -> &str {
        &self.name
    }
}
impl Description for PeripheralInfo {
    fn description(&self) -> Option<&str> {
        self.description.as_deref()
    }
}