//! bare8.rs
//!
//! Serial
//!
//! What it covers:

#![no_main]
#![no_std]

use panic_rtt_target as _;
use stm32f4xx_hal::nb::block;


use stm32f4xx_hal::{
    prelude::*,
    serial::{config::Config, Event, Rx, Serial, Tx},
    stm32::USART2,
};

use rtic::app;
use rtt_target::{rprintln, rtt_init_print};

#[app(device = stm32f4xx_hal::stm32, peripherals = true)]
const APP: () = {
    struct Resources {
        // Late resources
        TX: Tx<USART2>,
        RX: Rx<USART2>,
        RECV: i32,
        ERR: i32,
    }

    // init runs in an interrupt free section
    #[init]
    fn init(cx: init::Context) -> init::LateResources {
        rtt_init_print!();
        rprintln!("init");

        let device = cx.device;

        let rcc = device.RCC.constrain();

        // 16 MHz (default, all clocks)
        let clocks = rcc.cfgr.freeze();

        let gpioa = device.GPIOA.split();

        let tx = gpioa.pa2.into_alternate_af7();
        let rx = gpioa.pa3.into_alternate_af7();

        let mut errors:i32 = 0;
        let mut received:i32 = 0;

        let mut serial = Serial::usart2(
            device.USART2,
            (tx, rx),
            Config::default().baudrate(115_200.bps()),
            clocks,
        )
        .unwrap();

        // generate interrupt on Rxne
        serial.listen(Event::Rxne);

        // Separate out the sender and receiver of the serial port
        let (tx, rx) = serial.split();

        // Late resources
        init::LateResources { TX: tx, RX: rx, ERR:errors, RECV:received  }
    }

    // idle may be interrupted by other interrupts/tasks in the system
    #[idle()]
    fn idle(_cx: idle::Context) -> ! {
        loop {
            continue;
        }
    }

    // capacity sets the size of the input buffer (# outstanding messages)
    #[task(resources = [TX], priority = 2, capacity = 128)]
    fn rx(cx: rx::Context, data: u8) {
        let tx = cx.resources.TX;
        block!(tx.write(data)).unwrap();
        //rprintln!("data {:b}", data);
        
    }

    // Task bound to the USART2 interrupt.
    #[task(binds = USART2,  priority = 3, resources = [RX], spawn = [rx,traceing])]
    fn usart2(cx: usart2::Context) {
        let rx = cx.resources.RX;
        
        match block!(rx.read()) {
            Ok(byte) => {
                cx.spawn.rx(byte).unwrap();
                let _res=cx.spawn.traceing(true);
            }
            Err(_err) => {
                let _res=cx.spawn.traceing(false);
            }
        }
        
    }
    //Handles prints, resources ERR and RECV is the number of bytes and errors received respectivly
    #[task(priority = 1, resources = [ERR, RECV])]
    fn traceing(cx: traceing::Context, state: bool) {
        let received = cx.resources.RECV;
        let errors = cx.resources.ERR;
        match state {
            true => {
                *received += 1;
                rprintln!("Ok:s   {:?}", received);
            }
            false => {
                *errors += 1;
                rprintln!("Errors {:?}", errors);
            }
        }

    }

    extern "C" {
        fn EXTI0();
        fn USART1();
    }
};

// 0. Background
//
//    As seen in the prior example, you may loose data unless polling frequently enough.
//    Let's try an interrupt driven approach instead.
//
//    In init we just add:
//
//    // generate interrupt on Rxne
//    serial.listen(Event::Rxne);
//
//    This causes the USART hardware to generate an interrupt when data is available.
//
//    // Task bound to the USART2 interrupt.
//    #[task(binds = USART2,  priority = 2, resources = [RX], spawn = [rx])]
//    fn usart2(cx: usart2::Context) {
//      let rx = cx.resources.RX;
//      let data = rx.read().unwrap();
//      cx.spawn.rx(data).unwrap();
//    }
//
//    The `usart2` task will be triggered, and we read one byte from the internal
//    buffer in the USART2 hardware. (panic if something goes bad)
//
//    We send the read byte to the `rx` task (by `cx.spawn.rx(data).unwrap();`)
//    (We panic if the capacity of the message queue is reached)
//
//    // capacity sets the size of the input buffer (# outstanding messages)
//    #[task(resources = [TX], priority = 1, capacity = 128)]
//    fn rx(cx: rx::Context, data: u8) {
//        let tx = cx.resources.TX;
//        tx.write(data).unwrap();
//        rprintln!("data {}", data);
//    }
//
//    Here we echo the data back, `tx.write(data).unwrap();` (panic if usart is busy)
//    We then trace the received data `rprintln!("data {}", data);`
//
//    The `priority = 2` gives the `usart2` task the highest priority
//    (to ensure that we don't miss data).
//
//    The `priority = 1` gives the `rx` task a lower priority.
//    Here we can take our time and process the data.
//
//    `idle` runs at priority 0, lowest priority in the system.
//    Here we can do some background job, when nothing urgent is happening.
//
//    This is an example of a good design!
//
// 1. In this example we use RTT.
//
//    > cargo run --example rtic_bare9
//
//    Try breaking it!!!!
//    Throw any data at it, and see if you could make it panic!
//
//    Were you able to crash it?
//
//    ** your answer here **
//    yes, couldnt handle 123
//
//    Notice, the input tracing in `moserial` seems broken, and may loose data.
//    So don't be alarmed if data is missing, its a GUI tool after all.
//
//    If you want to sniff the `ttyACM0`, install e.g., `interceptty` and run
//    > interceptty /dev/ttyACM0
//
//    In another terminal, you can do:
//    > cat examples/rtic_bare9.rs > /dev/ttyACM0
//
//    Incoming data will be intercepted/displayed by `interceptty`.
//    (In the RTT trace you will see that data is indeed arriving to the target.)
//
//    Commit your answer (bare9_1)
//
// 2. Now, re-implement the received and error counters from previous exercise.
//
//    Good design:
//    - Defer any tracing to lower priority task/tasks
//      (you may introduce an error task at low priority).
//
//    - State variables can be introduced either locally (static mut), or
//      by using a resource.
//
//      If a resource is shared among tasks of different priorities:
//      The highest priority task will have direct access to the data,
//      the lower priority task(s) will need to lock the resource first.
//
//    Check the RTIC book, https://rtic.rs/0.5/book/en/by-example
//    regarding resources, software tasks, error handling etc.
//
//    Test that your implementation works and traces number of
//    bytes received and errors encountered.
//
//    If implemented correctly, it should be very hard (or impossible)
//    to get an error.
//
//    You can force an error by doing some "stupid delay" (faking workload),
//    e.g., burning clock cycles using `cortex_m::asm::delay` in the
//    `rx` task. Still you need to saturate the capacity (128 bytes).
//
//    To make errors easier to produce, reduce the capacity.
//
//    Once finished, comment your code.
//
//    Commit your code (bare9_2)
//
// 3. Discussion
//
//    Here you have used RTIC to implement a highly efficient and good design.
//
//    Tasks in RTIC are run-to-end, with non-blocking access to resources.
//    (Even `lock` is non-blocking, isn't that sweet?)
//
//    Tasks in RTIC are scheduled according to priorities.
//    (A higher priority task `H` always preempts lower priority task `L` running,
//    unless `L` holds a resource with higher or equal ceiling as `H`.)
//
//    Tasks in RTIC can spawn other tasks.
//    (`capacity` sets the message queue size.)
//
//    By design RTIC guarantees race- and deadlock-free execution.
//
//    It also comes with theoretical underpinning for static analysis.
//    - task response time
//    - overall schedulability
//    - stack memory analysis
//    - etc.
//
//    RTIC leverages on the zero-cost abstractions in Rust,
//    and the implementation offers best in class performance.