// src/bin/main.rs
#![no_std]
#![no_main]

use core::sync::atomic::{AtomicUsize, Ordering};
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::pac;
use embassy_stm32::interrupt::InterruptExt;
use embassy_stm32::timer::low_level::{CountingMode, Timer as LLTimer};
use embassy_stm32::time::khz;
use embassy_stm32::interrupt;
use {defmt_rtt as _, panic_probe as _};

// Pointer to the local `counter` so the ISR can read it with zero per-iteration overhead.
static COUNTER_PTR: AtomicUsize = AtomicUsize::new(0);

// NVIC handler for TIM2
#[interrupt]
fn TIM2() {
    // Clear UIF, stop the timer
    pac::TIM2.sr().modify(|r| r.set_uif(false));
    pac::TIM2.cr1().modify(|r| r.set_cen(false));

    // Snapshot the counter that the loop has been incrementing
    let ptr = COUNTER_PTR.load(Ordering::Relaxed) as *const u32;
    let count = unsafe { core::ptr::read_volatile(ptr) };

    info!("10 seconds elapsed, counter = {}", count);

    // Halt
    loop {
        cortex_m::asm::bkpt();
    }
}

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    info!("Start");
    let p = embassy_stm32::init(Default::default());

    // Configure TIM2
    let mut tim2 = LLTimer::new(p.TIM2);
    tim2.stop();
    tim2.set_counting_mode(CountingMode::EdgeAlignedUp);

    // Make the timer tick at 10 kHz. Driver derives PSC from RCC, so timing is exact.
    tim2.set_tick_freq(khz(10));

    // 10 seconds => 10_000 ticks/s * 10 s = 100_000 ticks. ARR is inclusive: set to 100_000 - 1.
    tim2.set_max_compare_value(100_000 - 1);

    // One-pulse mode (auto-stop after the first update event)
    tim2.regs_core().cr1().modify(|r| r.set_opm(true));

    // Ensure no pending UIF from the UG we generated during PSC/ARR programming.
    // Important: do this BEFORE enabling UIE/NVIC to avoid an immediate ISR.
    let _ = tim2.clear_update_interrupt();

    // Enable update interrupt, reset counter to 0. Do not start yet.
    tim2.enable_update_interrupt(true);
    tim2.reset();

    // Expose the address of the loop counter BEFORE enabling the NVIC so the ISR
    // never runs before COUNTER_PTR is valid.
    let mut counter: u32 = 0;
    COUNTER_PTR.store(&counter as *const u32 as usize, Ordering::Relaxed);

    // Unpend and enable NVIC for TIM2, then start the timer.
    unsafe {
        embassy_stm32::interrupt::TIM2.unpend();
        embassy_stm32::interrupt::TIM2.enable();
    }
    tim2.start();

    // Tight CPU loop for benchmarking
    loop {
        counter = counter.wrapping_add(1);

        if counter % 10000 == 0 {
            info!("CPU doing other work: {}", counter);
        }
    }
}
