// src/gpio_dma_uart_tx.rs
use embassy_executor::task;
use embassy_stm32::{
    dma::{Request, Transfer, TransferOptions},
    peripherals::GPDMA1_CH0,
    Peri,
};
use embassy_futures::yield_now;
use defmt::info;

use crate::UartConfig;
use embassy_sync::pipe::Pipe;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use crate::uart_emulation::encode_uart_byte_cfg;

pub const TIM6_UP_REQ: Request = 4;

pub async fn encode_uart_frames<'a>(
    pin_bit: u8,
    bytes: &[u8],
    out_buf: &'a mut [u32],
    uart_cfg: &UartConfig,
) -> usize {
    let mut offset = 0;
    for &b in bytes {
        let mut frame = [0u32; 12];
        let used = encode_uart_byte_cfg(pin_bit, b, uart_cfg, &mut frame);

        if offset + used <= out_buf.len() {
            out_buf[offset..offset + used].copy_from_slice(&frame[..used]);
            offset += used;
        } else {
            break;
        }

        yield_now().await;
    }
    offset
}

#[task]
pub async fn tx_dma_task(
    mut ch: Peri<'static, GPDMA1_CH0>,
    register: *mut u32, // GPIOx_BSRR
    pipe_rx: &'static Pipe<CriticalSectionRawMutex, 1024>,
    tx_pin_bit: u8,
    uart_cfg: &'static UartConfig,
) {
    info!("TX DMA task ready (One‑shot)");
    let mut frame_buf = [0u32; 4096];
    let mut rx_buf = [0u8; 256];
    let tim6 = embassy_stm32::pac::TIM6;

    loop {
        let n = pipe_rx.read(&mut rx_buf).await;
        if n == 0 {
            yield_now().await;
            continue;
        }

        let used = encode_uart_frames(tx_pin_bit, &rx_buf[..n], &mut frame_buf, uart_cfg).await;
        if used > 0 {
            // Clear pending UIF
            tim6.sr().write(|w| w.set_uif(false));
            // Wait for the next UIF (next bit tick)
            while !tim6.sr().read().uif() {
                yield_now().await;
            }
            // Clear UIF so first DMA beat happens on the FOLLOWING tick
            tim6.sr().write(|w| w.set_uif(false));

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

            unsafe {
                let transfer = Transfer::new_write(
                    ch.reborrow(),
                    TIM6_UP_REQ,
                    &frame_buf[..used],
                    register,
                    tx_opts,
                );
                transfer.await;
            }
        }

        // info!("tx_dma_task sent {} words", used);
        yield_now().await;
    }
}
