// src/software_uart/runtime.rs
use embassy_executor::task;
use embassy_stm32::{
    dma::Request,
    peripherals::GPDMA1_CH1,
    Peri,
};
use crate::config::RX_PIN_BIT;
use embassy_stm32::dma::{
    ReadableRingBuffer,
    TransferOptions,
};
use crate::config::{RX_OVERSAMPLE, UART_CFG};
use crate::software_uart::decode_uart_samples;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
use embassy_futures::yield_now;
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>,
) {
    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();

    // We read into the second half of a buffer, keeping "leftovers" in the first half.
    const CHUNK_SIZE: usize = 4096;
    const HISTORY_SIZE: usize = 512; // Enough to hold a potential split frame
    const TOTAL_BUF_SIZE: usize = HISTORY_SIZE + CHUNK_SIZE;
    
    // Logic level buffer
    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;

        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;

        // 3. Decode everything we have
        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);
             }
        }

        // 4. Shift remaining data to front
        // We processed 'consumed' samples.
        // We keep everything from 'consumed' up to 'current_end'.
        let remaining = current_end - consumed;
        
        // Safety check: if remaining > HISTORY_SIZE, we are in trouble (buffer too small / decoder stuck).
        // But for now, just shift.
        if remaining > 0 {
            level_buf.copy_within(consumed..current_end, 0);
        }
        valid_len = remaining;
        
        // If valid_len grows too large (decoder not consuming), we must discard to avoid panic on next write
        if valid_len >= HISTORY_SIZE {
             // Discard oldest to make space
             // logic: we move the last (HISTORY_SIZE/2) to 0.
             // This effectively "skips" garbage data.
             let keep = HISTORY_SIZE / 2;
             level_buf.copy_within(valid_len - keep..valid_len, 0);
             valid_len = keep;
        }

        yield_now().await;
    }
}
