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

use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::dma::Request;
use embassy_stm32::gpio::{Input, Output, Level, Pull, Speed};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
use embassy_stm32::dma::{TransferOptions, WritableRingBuffer};
use dma_gpio::software_uart::{
    dma_timer::{init_tim6_for_uart, init_tim7_for_uart},
    gpio_dma_uart_tx::encode_uart_frames,
    gpio_dma_uart_rx::rx_dma_task,
    debug::dump_tim6_regs,
};
use dma_gpio::config::{BAUD, TX_PIN_BIT, RX_OVERSAMPLE, TX_OVERSAMPLE};
use dma_gpio::config::{TX_RING_BYTES, RX_RING_BYTES, PIPE_RX_SIZE};
use static_cell::StaticCell;
use embassy_futures::yield_now;
use {defmt_rtt as _, panic_probe as _};

pub const TIM6_UP_REQ: Request = 4;

static PIPE_RX: Pipe<CriticalSectionRawMutex, PIPE_RX_SIZE> = Pipe::new();
static TX_RING: StaticCell<[u32; TX_RING_BYTES]> = StaticCell::new();
static RX_RING: StaticCell<[u8; RX_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, Pull::Up);
    let _tx = Output::new(p.PA2, Level::High, Speed::VeryHigh);

    init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE);
    init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE);

    dump_tim6_regs();

    // Safe one-time init from StaticCell
    let rx_ring: &mut [u8; RX_RING_BYTES] = RX_RING.init([0; RX_RING_BYTES]);
    let tx_ring_mem: &mut [u32; TX_RING_BYTES] = TX_RING.init([0; TX_RING_BYTES]);

    // Spawn tasks
    spawner.spawn(rx_dma_task(p.GPDMA1_CH1, &PIPE_RX, rx_ring).unwrap());

    // Create and start the TX DMA ring in main.
    // let bsrr_ptr = embassy_stm32::pac::GPIOA.bsrr().as_ptr() as *mut u32;
    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;

    // SAFETY: tx_ring_mem is exclusive
    let mut tx_ring = unsafe {
        WritableRingBuffer::new(
            p.GPDMA1_CH0,
            TIM6_UP_REQ,
            odr_ptr,
            tx_ring_mem,
            tx_opts,
        )
    };
    // Start DMA
    tx_ring.start();
    info!("TX DMA ring started");

    let mut frame_buf = [0u32; 4096];

    loop {
        info!("tick start");
        // Timer::after(Duration::from_millis(100)).await;
        // info!("tick end");
        let used = encode_uart_frames(
            TX_PIN_BIT,
            b"Hello marshmallow\r\n",
            &mut frame_buf,
        )
        .await;

        if used == 0 {
            info!("encode_uart_frames() produced 0 words, skipping write");
            yield_now().await;
            continue;
        }
        let _ = tx_ring.write_exact(&frame_buf[..used]).await;
        info!("text");
        yield_now().await;
    }
}
