//! Enable, Sleep Mode Enable, and Reset traits for peripherals

/// Enable/disable peripheral
pub trait Enable: crate::Sealed {
    /// Enables peripheral
    fn enable(bus: &mut Self::Bus);
    
    /// Disables peripheral
    fn disable(bus: &mut Self::Bus);
    
    /// Check if peripheral is enabled
    fn is_enabled() -> bool;
    
    /// Check if peripheral is disabled
    fn is_disabled() -> bool;
    
    /// Enables peripheral without taking bus ownership
    /// 
    /// # Safety
    /// This can break the bus abstraction if not used carefully
    unsafe fn enable_unchecked();
    
    /// Disables peripheral without taking bus ownership
    /// 
    /// # Safety
    /// This can break the bus abstraction if not used carefully
    unsafe fn disable_unchecked();
}

/// Enable/disable peripheral in sleep mode
pub trait SMEnable: crate::Sealed {
    /// Enables peripheral clock in sleep mode
    fn enable_in_sleep_mode(bus: &mut Self::Bus);
    
    /// Disables peripheral clock in sleep mode
    fn disable_in_sleep_mode(bus: &mut Self::Bus);
    
    /// Check if peripheral is enabled in sleep mode
    fn is_enabled_in_sleep_mode() -> bool;
    
    /// Check if peripheral is disabled in sleep mode
    fn is_disabled_in_sleep_mode() -> bool;
    
    /// Enables peripheral in sleep mode without taking bus ownership
    /// 
    /// # Safety
    /// This can break the bus abstraction if not used carefully
    unsafe fn enable_in_sleep_mode_unchecked();
    
    /// Disables peripheral in sleep mode without taking bus ownership
    /// 
    /// # Safety
    /// This can break the bus abstraction if not used carefully
    unsafe fn disable_in_sleep_mode_unchecked();
}

/// Reset peripheral
pub trait Reset: crate::Sealed {
    /// Resets peripheral
    fn reset(bus: &mut Self::Bus);
    
    /// Resets peripheral without taking bus ownership
    /// 
    /// # Safety
    /// This can break the bus abstraction if not used carefully
    unsafe fn reset_unchecked();
}

/// RCC bus trait
pub trait RccBus: crate::Sealed {
    /// Bus type
    type Bus;
}

macro_rules! bus_enable {
    ($PER:ident => $en:ident) => {
        impl Enable for crate::pac::$PER {
            #[inline(always)]
            fn enable(bus: &mut Self::Bus) {
                bus.enr().modify(|_, w| w.$en().set_bit());
                // Stall pipeline to work around erratum
                cortex_m::asm::dsb();
            }
            
            #[inline(always)]
            fn disable(bus: &mut Self::Bus) {
                bus.enr().modify(|_, w| w.$en().clear_bit());
            }
            
            #[inline(always)]
            fn is_enabled() -> bool {
                Self::Bus::new().enr().read().$en().bit_is_set()
            }
            
            #[inline(always)]
            fn is_disabled() -> bool {
                Self::Bus::new().enr().read().$en().bit_is_clear()
            }
            
            #[inline(always)]
            unsafe fn enable_unchecked() {
                Self::enable(&mut Self::Bus::new());
            }
            
            #[inline(always)]
            unsafe fn disable_unchecked() {
                Self::disable(&mut Self::Bus::new());
            }
        }
    };
}

macro_rules! bus_smenable {
    ($PER:ident => $smen:ident) => {
        impl SMEnable for crate::pac::$PER {
            #[inline(always)]
            fn enable_in_sleep_mode(bus: &mut Self::Bus) {
                bus.smenr().modify(|_, w| w.$smen().set_bit());
                cortex_m::asm::dsb();
            }
            
            #[inline(always)]
            fn disable_in_sleep_mode(bus: &mut Self::Bus) {
                bus.smenr().modify(|_, w| w.$smen().clear_bit());
            }
            
            #[inline(always)]
            fn is_enabled_in_sleep_mode() -> bool {
                Self::Bus::new().smenr().read().$smen().bit_is_set()
            }
            
            #[inline(always)]
            fn is_disabled_in_sleep_mode() -> bool {
                Self::Bus::new().smenr().read().$smen().bit_is_clear()
            }
            
            #[inline(always)]
            unsafe fn enable_in_sleep_mode_unchecked() {
                Self::enable_in_sleep_mode(&mut Self::Bus::new());
            }
            
            #[inline(always)]
            unsafe fn disable_in_sleep_mode_unchecked() {
                Self::disable_in_sleep_mode(&mut Self::Bus::new());
            }
        }
    };
}

macro_rules! bus_reset {
    ($PER:ident => $rst:ident) => {
        impl Reset for crate::pac::$PER {
            #[inline(always)]
            fn reset(bus: &mut Self::Bus) {
                bus.rstr().modify(|_, w| w.$rst().set_bit());
                bus.rstr().modify(|_, w| w.$rst().clear_bit());
            }
            
            #[inline(always)]
            unsafe fn reset_unchecked() {
                Self::reset(&mut Self::Bus::new());
            }
        }
    };
}

macro_rules! bus {
    ($($PER:ident => ($busX:ty, $($en:ident)?, $($smen:ident)?, $($rst:ident)?),)+) => {
        $(
            impl crate::Sealed for crate::pac::$PER {}
            
            impl RccBus for crate::pac::$PER {
                type Bus = $busX;
            }
            
            $(bus_enable!($PER => $en);)?
            $(bus_smenable!($PER => $smen);)?
            $(bus_reset!($PER => $rst);)?
        )+
    };
}

// STM32U575 peripheral mappings (based on C HAL register definitions)
bus! {
    // AHB1 peripherals
    GPDMA1 => (super::AHB1, gpdma1en, gpdma1smen, gpdma1rst),
    CORDIC => (super::AHB1, cordicen, cordicsmen, cordicrst),
    FMAC => (super::AHB1, fmacen, fmacsmen, fmacrst),
    MDF1 => (super::AHB1, mdf1en, mdf1smen, mdf1rst),
    FLASH => (super::AHB1, flashen, flashsmen,),
    CRC => (super::AHB1, crcen, crcsmen, crcrst),
    TSC => (super::AHB1, tscen, tscsmen, tscrst),
    RAMCFG => (super::AHB1, ramcfgen, ramcfgsmen, ramcfgrst),
    GTZC1 => (super::AHB1, gtzc1en, gtzc1smen,),
    ICACHE => (super::AHB1,, icachesmen,),
    DCACHE1 => (super::AHB1, dcache1en, dcache1smen,),
    SRAM1 => (super::AHB1, sram1en, sram1smen,),

    // AHB2_1 peripherals (AHB2ENR1)
    GPIOA => (super::AHB2_1, gpioaen, gpioasmen, gpioarst),
    GPIOB => (super::AHB2_1, gpioben, gpiobsmen, gpiobrst),
    GPIOC => (super::AHB2_1, gpiocen, gpiocsmen, gpiocrst),
    GPIOD => (super::AHB2_1, gpioden, gpiodsmen, gpiodrst),
    GPIOE => (super::AHB2_1, gpioeen, gpioesmen, gpioerst),
    GPIOG => (super::AHB2_1, gpiogen, gpiogsmen, gpiogrst),
    GPIOH => (super::AHB2_1, gpiohen, gpiohsmen, gpiohrst),
    ADC12 => (super::AHB2_1, adc12en, adc12smen, adc12rst),
    DCMI => (super::AHB2_1, dcmi_pssien, dcmi_pssismen, dcmi_pssirst),
    // TODO: USB_OTG_FS => (super::AHB2_1, otgen, otgsmen, otgrst), // Check exact name
    AES => (super::AHB2_1, aesen, aessmen, aesrst),
    HASH => (super::AHB2_1, hashen, hashsmen, hashrst),
    RNG => (super::AHB2_1, rngen, rngsmen, rngrst),
    // TODO: PKA, SAES, OCTOSPIM, OTFDEC1, OTFDEC2 if present
    SDMMC1 => (super::AHB2_1, sdmmc1en, sdmmc1smen, sdmmc1rst),
    SRAM2 => (super::AHB2_1, sram2en, sram2smen,),

    // AHB2_2 peripherals (AHB2ENR2)
    // TODO: FMC, OCTOSPI1, OCTOSPI2 based on actual device
    
    // AHB3 peripherals
    LPGPIO1 => (super::AHB3, lpgpio1en, lpgpio1smen, lpgpio1rst),
    PWR => (super::AHB3, pwren, pwrsmen,),
    ADC4 => (super::AHB3, adc4en, adc4smen, adc4rst),
    DAC1 => (super::AHB3, dac1en, dac1smen, dac1rst),
    LPDMA1 => (super::AHB3, lpdma1en, lpdma1smen, lpdma1rst),
    ADF1 => (super::AHB3, adf1en, adf1smen, adf1rst),
    GTZC2 => (super::AHB3, gtzc2en, gtzc2smen,),
    SRAM4 => (super::AHB3, sram4en, sram4smen,),

    // APB1R1 peripherals
    TIM2 => (super::APB1R1, tim2en, tim2smen, tim2rst),
    TIM3 => (super::APB1R1, tim3en, tim3smen, tim3rst),
    TIM4 => (super::APB1R1, tim4en, tim4smen, tim4rst),
    TIM5 => (super::APB1R1, tim5en, tim5smen, tim5rst),
    TIM6 => (super::APB1R1, tim6en, tim6smen, tim6rst),
    TIM7 => (super::APB1R1, tim7en, tim7smen, tim7rst),
    WWDG => (super::APB1R1, wwdgen, wwdgsmen,),
    SPI2 => (super::APB1R1, spi2en, spi2smen, spi2rst),
    USART3 => (super::APB1R1, usart3en, usart3smen, usart3rst),
    UART4 => (super::APB1R1, uart4en, uart4smen, uart4rst),
    UART5 => (super::APB1R1, uart5en, uart5smen, uart5rst),
    I2C1 => (super::APB1R1, i2c1en, i2c1smen, i2c1rst),
    I2C2 => (super::APB1R1, i2c2en, i2c2smen, i2c2rst),
    CRS => (super::APB1R1, crsen, crssmen, crsrst),

    // APB1R2 peripherals
    I2C4 => (super::APB1R2, i2c4en, i2c4smen, i2c4rst),
    LPTIM2 => (super::APB1R2, lptim2en, lptim2smen, lptim2rst),
    FDCAN1 => (super::APB1R2, fdcan1en, fdcan1smen, fdcan1rst),

    // APB2 peripherals
    TIM1 => (super::APB2, tim1en, tim1smen, tim1rst),
    SPI1 => (super::APB2, spi1en, spi1smen, spi1rst),
    TIM8 => (super::APB2, tim8en, tim8smen, tim8rst),
    USART1 => (super::APB2, usart1en, usart1smen, usart1rst),
    TIM15 => (super::APB2, tim15en, tim15smen, tim15rst),
    TIM16 => (super::APB2, tim16en, tim16smen, tim16rst),
    TIM17 => (super::APB2, tim17en, tim17smen, tim17rst),
    SAI1 => (super::APB2, sai1en, sai1smen, sai1rst),

    // APB3 peripherals
    SYSCFG => (super::APB3, syscfgen, syscfgsmen, syscfgrst),
    SPI3 => (super::APB3, spi3en, spi3smen, spi3rst),
    LPUART1 => (super::APB3, lpuart1en, lpuart1smen, lpuart1rst),
    I2C3 => (super::APB3, i2c3en, i2c3smen, i2c3rst),
    LPTIM1 => (super::APB3, lptim1en, lptim1smen, lptim1rst),
    LPTIM3 => (super::APB3, lptim3en, lptim3smen, lptim3rst),
    LPTIM4 => (super::APB3, lptim4en, lptim4smen, lptim4rst),
    OPAMP => (super::APB3, opampen, opampsmen, opamprst),
    COMP => (super::APB3, compen, compsmen, comprst),
    VREFBUF => (super::APB3, vrefen, vrefsmen, vrefrst),
}

// TODO: Add conditional compilation for optional peripherals:
// - GPIOF, GPIOI, GPIOJ (not on all packages)
// - SAI2, USART2, USART6, I2C5, I2C6
// - USB_FS (USB_DRD_FS)
// - FMC, OCTOSPI2, HSPI1
// - DMA2D, JPEG, GPU2D, GFXMMU, LTDC, DSI
// - UCPD1, SDMMC2
