// src/gpio_dma_uart_rx.rs

use embassy_executor::task;
use embassy_stm32::{
    dma::Request,
    peripherals::GPDMA1_CH1,
    Peri,
};
use embassy_stm32::dma::{
    ReadableRingBuffer,
    TransferOptions,
};
use crate::decode_uart_samples;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
use embassy_futures::yield_now;
use crate::UartConfig;
use defmt::info;

// datasheet tabulka 137
pub const TIM7_UP_REQ: Request = 5;

/// RX DMA task: reads GPIO samples paced by TIM7 and fills PIPE_RX
#[task]
pub async fn rx_dma_task(
    ch: Peri<'static, GPDMA1_CH1>,
    register: *mut u8,
    ring: &'static mut [u8],
    pipe_rx: &'static Pipe<CriticalSectionRawMutex, 4096>,
    rx_pin_bit: u8,
    rx_oversample: u16,
    uart_cfg: &'static UartConfig,
) {
    let mut opts = TransferOptions::default();
    opts.half_transfer_ir = true;
    opts.complete_transfer_ir = true;

    // SAFETY: ring is exclusive to this task
    let mut rx = unsafe {
        ReadableRingBuffer::new(
            ch,
            TIM7_UP_REQ,
            register,
            ring,
            opts
        )
    };
    rx.start();

    const CHUNK_SIZE: usize = 4096;
    const HISTORY_SIZE: usize = 512;
    const TOTAL_BUF_SIZE: usize = HISTORY_SIZE + CHUNK_SIZE;
    
    let mut level_buf = [0u8; TOTAL_BUF_SIZE];
    let mut valid_len = 0usize; 

    let mut raw_chunk = [0u8; CHUNK_SIZE];

    loop {
        let _ = rx.read_exact(&mut raw_chunk).await;

        // Extract Rx pin value from IDR
        for (i, b) in raw_chunk.iter().enumerate() {
             level_buf[valid_len + i] = ((*b >> rx_pin_bit) & 1) as u8;
        }
        let current_end = valid_len + CHUNK_SIZE;

        let (decoded, consumed) = decode_uart_samples(
            &level_buf[..current_end], 
            rx_oversample,
            uart_cfg
        );

        if !decoded.is_empty() {
             pipe_rx.write(decoded.as_slice()).await;

             // for byte in decoded.as_slice() {
                 // info!("DMA BUFFER CHAR: {} (ASCII: {})", *byte, *byte as char);
             // }
        }

        // Shift remaining data to history partition 
        let remaining = current_end - consumed;
        
        if remaining > 0 {
            level_buf.copy_within(consumed..current_end, 0);
        }
        valid_len = remaining;
        
        // SAFETY if decoder is stuck and buffer is filling up, discard old data
        if valid_len >= HISTORY_SIZE {
             let keep = HISTORY_SIZE / 2;
             level_buf.copy_within(valid_len - keep..valid_len, 0);
             valid_len = keep;
        }

        yield_now().await;
    }
}
