// src/pwr.rs
//! Power management (STM32U575, minimal common subset with STM32U5 HAL C)
//!
//! Included:
//! - Enable PWR peripheral clock
//! - Backup-domain write access helpers (DBP)
//! - Enter Standby / Shutdown low-power modes (CR1.LPMS + WFI)
//! - Wakeup source configuration (basic WUPEN1..8 mask via WUCR1)
//! - Wakeup reason reading (WUSR)
//!
//! Skipped (present in U5 HAL C or PWREx, not implemented here yet):
//! - VOS/voltage scaling and EPOD boost
//! - Low-power run mode helper
//! - PVD configuration / EXTI integration
//! - Wakeup pin polarity / selection details (WUCR2/WUCR3)
//! - Security/privileged attributes (SECCFGR/PRIVCFGR)
//! - Explicit STOP 0/1/2/3 helpers (can be added similarly)
//!
//! This module only depends on the PAC.

use crate::pac as pac;
use crate::pac::{PWR, RCC};
use cortex_m::peripheral::SCB;

/// Wake-up source mask for WUPEN1..WUPEN8 (PWR_WUCR1)
///
/// This is a minimal bitmask wrapper around the 8 WUPEN lines.
/// For detailed pin source selection or polarity, configure WUCR2/WUCR3
/// directly via the PAC (not covered here yet).
#[derive(Clone, Copy, Debug, Default)]
pub struct WakeUpSource {
    bits: u16,
}

impl WakeUpSource {
    pub fn new() -> Self {
        Self { bits: 0 }
    }

    pub fn enable_wkup1(mut self, en: bool) -> Self {
        self.set_bit(0, en);
        self
    }
    pub fn enable_wkup2(mut self, en: bool) -> Self {
        self.set_bit(1, en);
        self
    }
    pub fn enable_wkup3(mut self, en: bool) -> Self {
        self.set_bit(2, en);
        self
    }
    pub fn enable_wkup4(mut self, en: bool) -> Self {
        self.set_bit(3, en);
        self
    }
    pub fn enable_wkup5(mut self, en: bool) -> Self {
        self.set_bit(4, en);
        self
    }
    pub fn enable_wkup6(mut self, en: bool) -> Self {
        self.set_bit(5, en);
        self
    }
    pub fn enable_wkup7(mut self, en: bool) -> Self {
        self.set_bit(6, en);
        self
    }
    pub fn enable_wkup8(mut self, en: bool) -> Self {
        self.set_bit(7, en);
        self
    }

    #[inline]
    fn set_bit(&mut self, pos: u8, en: bool) {
        if en {
            self.bits |= 1u16 << pos;
        } else {
            self.bits &= !(1u16 << pos);
        }
    }

    #[inline]
    fn mask8(&self) -> u32 {
        (self.bits as u32) & 0xFF
    }

    /// Construct from raw WUSR flags (WUF1..WUF8).
    pub fn from_wusr_raw(raw: u32) -> Self {
        Self {
            bits: (raw as u16) & 0x00FF,
        }
    }

    /// Raw WUPEN/WUF bit mask (lower 8 bits).
    pub fn bits(&self) -> u16 {
        self.bits
    }
}

/// Minimal PWR handle
pub struct Pwr {
    pub cr1: CR1,
    pub wucr1: WUCR1,
    pub wuscr: WUSCR,
    pub wusr: WUSR,
    pub dbpr: DBPR,
}

impl Pwr {
    /// Enable write access to the backup domain (DBP = 1)
    pub fn enable_backup_write(&mut self) {
        unsafe {
            self.dbpr.reg().modify(|_, w| w.dbp().set_bit());
        }
    }

    /// Disable write access to the backup domain (DBP = 0)
    pub fn disable_backup_write(&mut self) {
        unsafe {
            self.dbpr.reg().modify(|_, w| w.dbp().clear_bit());
        }
    }

    /// Enter Standby low-power mode.
    ///
    /// Minimal flow:
    /// - Program WUCR1 enables from `wkup` mask
    /// - Clear WUF flags
    /// - Set LPMS to Standby, set SLEEPDEEP, WFI
    pub fn standby(&mut self, wkup: WakeUpSource, scb: &mut SCB) -> ! {
        // Enable selected wakeup lines (WUPEN1..8)
        unsafe {
            // WUCR1 contains WUPENx bits in the low 8 bits; write a mask.
            self.wucr1.reg().modify(|r, w| {
                let cur = r.bits();
                let new_en = (cur & !0xFF) | wkup.mask8();
                w.bits(new_en)
            });
        }

        // Clear all wakeup flags (WUSCR CWUFx)
        // Writing 1s clears the corresponding WUF flag.
        unsafe {
            self.wuscr.reg().write(|w| w.bits(0xFF));
        }

        // Configure Standby in CR1.LPMS
        // HAL C uses: MODIFY_REG(PWR->CR1, PWR_CR1_LPMS, PWR_CR1_LPMS_2);
        // We map to LPMS = 0b100 (Standby). Exact encoding may differ by die rev,
        // but LPMS field exists in U5.
        unsafe {
            self.cr1.reg().modify(|_, w| {
                // LPMS is a field; set to Standby (0b100)
                #[allow(unused_unsafe)]
                unsafe {
                    w.lpms().bits(0b100)
                }
            });
        }

        scb.set_sleepdeep();
        cortex_m::asm::dsb();
        cortex_m::asm::wfi();
        loop {}
    }

    /// Enter Shutdown low-power mode.
    ///
    /// Flow mirrors `standby()` but programs LPMS to Shutdown.
    /// This is provided for parity with your L4 API. While HAL PWR C sample
    /// shows Standby, U5 also supports Shutdown via CR1.LPMS.
    pub fn shutdown(&mut self, wkup: WakeUpSource, scb: &mut SCB) -> ! {
        // Enable selected wakeup lines (WUPEN1..8)
        unsafe {
            self.wucr1.reg().modify(|r, w| {
                let cur = r.bits();
                let new_en = (cur & !0xFF) | wkup.mask8();
                w.bits(new_en)
            });
        }

        // Clear all wakeup flags
        unsafe {
            self.wuscr.reg().write(|w| w.bits(0xFF));
        }

        // Configure Shutdown in CR1.LPMS
        // Typical encoding: 0b110 (consult RM for your exact part).
        unsafe {
            self.cr1.reg().modify(|_, w| {
                #[allow(unused_unsafe)]
                unsafe {
                    w.lpms().bits(0b110)
                }
            });
        }

        scb.set_sleepdeep();
        cortex_m::asm::dsb();
        cortex_m::asm::wfi();
        loop {}
    }

    /// Read current wakeup flags (WUSR WUF1..8) into a mask wrapper.
    pub fn read_wakeup_reason(&mut self) -> WakeUpSource {
        let raw = self.wusr.reg().read().bits();
        WakeUpSource::from_wusr_raw(raw)
    }
}

/// Extension trait to constrain `PWR` and enable its bus clock.
pub trait PwrExt {
    /// Constrain the `PWR` peripheral and enable the peripheral clock.
    fn constrain(self) -> Pwr;
}

impl PwrExt for PWR {
    fn constrain(self) -> Pwr {
        // Enable PWR clock on AHB3 (per U5 HAL: __HAL_RCC_PWR_CLK_ENABLE())
        let rcc = unsafe { &*RCC::ptr() };
        rcc.ahb3enr.modify(|_, w| w.pwren().set_bit());
        // Small barrier after clock enabling (mirroring typical patterns)
        cortex_m::asm::dsb();

        Pwr {
            cr1: CR1 { _0: () },
            wucr1: WUCR1 { _0: () },
            wuscr: WUSCR { _0: () },
            wusr: WUSR { _0: () },
            dbpr: DBPR { _0: () },
        }
    }
}

/// CR1 proxy
pub struct CR1 {
    _0: (),
}
impl CR1 {
    #[inline(always)]
    pub(crate) fn reg(&mut self) -> &pac::pwr::CR1 {
        unsafe { &(*PWR::ptr()).cr1 }
    }
}

/// WUCR1 proxy
pub struct WUCR1 {
    _0: (),
}
impl WUCR1 {
    #[inline(always)]
    pub(crate) fn reg(&mut self) -> &pac::pwr::WUCR1 {
        unsafe { &(*PWR::ptr()).wucr1 }
    }
}

/// WUSCR proxy (wake-up flags clear)
pub struct WUSCR {
    _0: (),
}
impl WUSCR {
    #[inline(always)]
    pub(crate) fn reg(&mut self) -> &pac::pwr::WUSCR {
        unsafe { &(*PWR::ptr()).wuscr }
    }
}

/// WUSR proxy (wake-up flags status)
pub struct WUSR {
    _0: (),
}
impl WUSR {
    #[inline(always)]
    pub(crate) fn reg(&mut self) -> &pac::pwr::WUSR {
        unsafe { &(*PWR::ptr()).wusr }
    }
}

/// DBPR proxy (backup domain access)
pub struct DBPR {
    _0: (),
}
impl DBPR {
    #[inline(always)]
    pub(crate) fn reg(&mut self) -> &pac::pwr::DBPR {
        unsafe { &(*PWR::ptr()).dbpr }
    }
}
