//! Echo back data over the serial line in packs of eight (@ 9_600 bauds) while running the heartbeat task from `5-heartbeat`.
//!
//! This assumes that a LED is connected to pc13 as is the case on the blue pill board.

#![no_main]
#![no_std]

use async_embedded::task;
use async_stm32f1xx::{
    serial::{RxStream3, TxSink3},
    timer::AsyncTimer,
};
use cortex_m_rt::entry;
use defmt_rtt as _; // global logger
use futures::sink::SinkExt;
use panic_probe as _; // panic handler
use stm32f1xx_hal::{
    dma,
    gpio::State,
    pac::Peripherals,
    prelude::*,
    serial::{Config, Serial},
    timer::Timer,
};

#[entry]
fn main() -> ! {
    // Extract needed peripherals
    let dp = Peripherals::take().unwrap();

    // Avoid AHB going into low-power mode causing RTT to stop working
    dp.RCC.ahbenr.modify(|_, w| w.dma1en().enabled());

    let rcc = dp.RCC.constrain();
    let mut apb1 = rcc.apb1;
    let mut acr = dp.FLASH.constrain().acr;
    let clocks = rcc.cfgr.freeze(&mut acr);

    // Create Timer
    let mut timer: AsyncTimer<_> = Timer::tim2(dp.TIM2, &clocks, &mut apb1).into();

    // Create Led
    let mut apb2 = rcc.apb2;
    let gpioc = dp.GPIOC.split(&mut apb2);
    let mut cr = gpioc.crh;
    let mut led = gpioc
        .pc13
        .into_push_pull_output_with_state(&mut cr, State::High);

    // heartbeat task
    task::spawn(async move {
        loop {
            led.toggle().unwrap();
            timer.delay_for(10.hz()).await;
            led.toggle().unwrap();
            timer.delay_for(4.hz()).await;
            led.toggle().unwrap();
            timer.delay_for(10.hz()).await;
            led.toggle().unwrap();
            timer.delay_for(2.hz()).await;
        }
    });

    // Create TxSink and RxSink
    let mut gpiob = dp.GPIOB.split(&mut apb2);
    let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
    let rx = gpiob.pb11;
    let afio = dp.AFIO.constrain(&mut apb2);
    let mut mapr = afio.mapr;
    let serial = Serial::usart3(
        dp.USART3,
        (tx, rx),
        &mut mapr,
        Config::default().baudrate(9_600.bps()),
        clocks,
        &mut apb1,
    );
    let mut ahb = rcc.ahb;
    let channels = dp.DMA1.split(&mut ahb);
    let (tx, rx) = serial.split();
    let tx_buf = {
        static mut BUF: [u8; 8] = [0; 8];
        // Safety: We only create one mutable reference
        unsafe { &mut BUF }
    };
    let mut tx_sink =
        TxSink3::new(tx_buf, tx.with_dma(channels.2)).sink_map_err(|_| dma::Error::Overrun);
    let rx_buf = {
        static mut BUF: [[u8; 8]; 2] = [[0; 8]; 2];
        // Safety: We only create one mutable reference
        unsafe { &mut BUF }
    };
    let mut rx_stream = RxStream3::new(rx_buf, rx.with_dma(channels.3));

    task::block_on(async {
        tx_sink.send_all(&mut rx_stream).await.unwrap();
        unreachable!("rx_stream is empty");
    })
}
