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

use defmt::*;
use embassy_executor::Spawner;
use embassy_futures::yield_now;
use embassy_stm32::dma::Request;
use embassy_stm32::gpio::{Input, Output, Level, Pull, Speed};
use embassy_time::{Duration, Timer};
use embassy_stm32::dma::{TransferOptions, WritableRingBuffer};
use dma_gpio::software_uart::{
    dma_timer::init_tim6_for_uart,
    gpio_dma_uart_tx::encode_uart_frames,
    debug::dump_tim6_regs,
};
use dma_gpio::config::{BAUD, TX_PIN_BIT, TX_OVERSAMPLE, TX_RING_BYTES};
use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _};

pub const TIM6_UP_REQ: Request = 4;

static TX_RING: StaticCell<[u32; TX_RING_BYTES]> = StaticCell::new();

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

    let _rx = Input::new(p.PA3, embassy_stm32::gpio::Pull::Up);
    let _tx = Output::new(p.PA2, Level::High, Speed::VeryHigh);

    init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE);
    dump_tim6_regs();

    let idle: u32 = 1u32 << TX_PIN_BIT;

    // Ring initialized to idle (line high).
    let tx_ring_mem: &mut [u32; TX_RING_BYTES] = TX_RING.init([0; TX_RING_BYTES]);

    let odr_ptr = embassy_stm32::pac::GPIOA.odr().as_ptr() as *mut u32;

    let mut tx_opts = TransferOptions::default();
    tx_opts.half_transfer_ir = true;
    tx_opts.complete_transfer_ir = true;

    let mut tx_ring = unsafe {
        WritableRingBuffer::new(
            p.GPDMA1_CH0,
            TIM6_UP_REQ,
            odr_ptr,
            tx_ring_mem,
            tx_opts,
        )
    };

    {
        // Full idle buffer matching ring capacity.
        let idle_buf = [idle; TX_RING_BYTES];

        let written = tx_ring
            .write_exact(&idle_buf)
            .await
            .expect("Failed to prefill TX ring with idle");

        info!(
            "TX ring prefilled with idle via write_exact: written={} cap={}",
            written,
            tx_ring.capacity()
        );
    }

    tx_ring.start();
    info!(
        "TX DMA ring started: cap_words={}",
        tx_ring.capacity()
    );
    unsafe {
        use embassy_stm32::pac::gpdma::Gpdma;
        let dma = Gpdma::from_ptr(0x4002_0000 as _); // GPDMA1 base for STM32U5
        let ch = dma.ch(0); // Channel 0

        let cr = ch.cr().read();
        let tr1 = ch.tr1().read();
        let tr2 = ch.tr2().read();
        let br1 = ch.br1().read();
        let sar = ch.sar().read();
        let dar = ch.dar().read();
        let llr = ch.llr().read();
        let lbar = ch.lbar().read();

        info!(
            "GPDMA1_CH0: EN={} HTIE={} TCIE={} SDW={:?} DDW={:?} SINC={} DINC={} BNDT={} SAR=0x{:08x} DAR=0x{:08x}",
            cr.en(),
            cr.htie(),
            cr.tcie(),
            tr1.sdw(),
            tr1.ddw(),
            tr1.sinc(),
            tr1.dinc(),
            br1.bndt(),
            sar, // already u32
            dar, // already u32
        );
        info!(
            "GPDMA1_CH0: LBAR=0x{:08x} LLR=0x{:08x}",
            lbar.lba(),
            llr.0
        );
    }

    let mut frame_buf = [0u32; 4096];

    loop {
        info!("tick start");
        Timer::after(Duration::from_millis(1021)).await;
        //info!("tick end");

        let used = encode_uart_frames(
            TX_PIN_BIT,
            b"H\r\n",
            &mut frame_buf,
        )
        .await;

        if used == 0 {
            info!("encode_uart_frames() produced 0 words, skipping write");
            yield_now().await;
            continue;
        }

        // Debug: confirm encoded content.
        let preview = core::cmp::min(used, 32);
        info!(
            "TX frame used={} words, head={=[?]}",
            used,
            &frame_buf[..preview]
        );

        // Now it is safe to call write_exact.
        match tx_ring.write_exact(&frame_buf[..used]).await {
            Ok(written) => {
                let len = tx_ring.len().unwrap_or(0);
                info!(
                    "write_exact ok: written={} ring_used={} ring_cap={}",
                    written,
                    len,
                    tx_ring.capacity()
                );
            }
            Err(e) => {
                let len = tx_ring.len().unwrap_or(0);
                warn!(
                    "write_exact error: {:?}, ring_used={} ring_cap={}",
                    e,
                    len,
                    tx_ring.capacity()
                );
            }
        }

        info!("tick end");
        yield_now().await;
    }
}
