//! The Judas kernel for raspberry pi.
//!
//! This is a kernel written in rust for raspberry pi 3 and 4.
//! This project is heavily inspired by https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials,
//! but it's a project for discovering rust on embedded, so its architecture is less generic and
//! I'm trying to reuse knowledge from Telecom-Paris SE203 class as much as I can.

#![cfg_attr(not(test), no_main)]
#![cfg_attr(not(test), no_std)]
#![feature(format_args_nl)]
#![feature(panic_info_message)]

#![allow(dead_code)]

mod traits;
mod bsp;
mod utils;

#[cfg(not(test))]
mod panic;
#[cfg(not(test))]
mod print;

#[cfg(not(test))]
use core::arch::global_asm;
#[cfg(not(test))]
use core::arch::asm;

use core::time::Duration;

// TODO: handle this with features
#[cfg(not(test))]
use crate::bsp::rpi3::uart::console;
#[cfg(not(test))]
use crate::bsp::rpi3::gpio;
#[cfg(not(test))]
use crate::bsp::aarch64::time::time_manager;

use crate::traits::time::TimeManager;

// TODO: move this to BSP
/// Pause the core with a infinit loop
#[cfg(not(test))]
#[inline(always)]
pub fn wait_forever() -> ! {
    loop {
        unsafe { asm!("wfe"); }
    }
}

// TODO: move this to BSP
#[cfg(not(test))]
global_asm!(include_str!("boot.s"));

/// Start the rust part of the kernel
#[cfg(not(test))]
#[no_mangle]
pub unsafe fn _start_rust() -> ! {

    // Set TX and RX function for pin 14 and 15
    match gpio::set_pin_fonction(14, gpio::PinFunction::AltFunc0) {
        _ => (),
    }
    match gpio::set_pin_fonction(15, gpio::PinFunction::AltFunc0) {
        _ => (),
    }
    // Debut led
    match gpio::set_pin_fonction(20, gpio::PinFunction::Output) {
        _ => (),
    }
    match gpio::set_pin_output_state(20, gpio::PinOutputState::Low) {
        _ => (),
    }
    bsp::rpi3::uart::init();
    println!("Hello there");

    let mut buffer = ['X'; 200];
    let mut i = 0;
    let mut c = console().read_char();
    console().write_char(c);
    while c != '\r' && i < 199 {
        buffer[i] = c;
        c = console().read_char();
        console().write_char(c);
        i += 1;
    }
    buffer[i] = c;
    i += 1;
    let mut j = 0;
    print!("Received: ");
    while j < i {
        print!("{}", buffer[j]);
        j += 1;
    }
    println!("");

    match gpio::set_pin_fonction(21, gpio::PinFunction::Output) {
        Ok(()) => println!("Successfully set pin to output"),
        Err(err) => println!("Failled to set pin: {err}"),
    }
    loop {
        let uptime = time_manager().uptime();
        match gpio::set_pin_output_state(21, gpio::PinOutputState::High) {
            Ok(()) => println!("[{:>6}.{:06}] OUTPUT 21 UP", uptime.as_secs(), uptime.subsec_micros()),
            Err(_err) => println!("Failled to set pin 21 to High"),
        }
        time_manager().sleep(Duration::from_secs(1));
        let uptime = time_manager().uptime();
        match gpio::set_pin_output_state(21, gpio::PinOutputState::Low) {
            Ok(()) => println!("[{:>6}.{:06}] OUTPUT 21 DOWN", uptime.as_secs(), uptime.subsec_micros()),
            Err(_err) => println!("Failled to set pin 21 to Low"),
        }
        time_manager().sleep(Duration::from_secs(1));
    }
    /*
    println!("Hello there");
    panic!("Paniccccccc")
    */
}

#[cfg(test)]
fn main() {}
