//! Quick and dirty uart driver

use core::fmt::Write;
/// TODO repr(C) a register bank, and add instances
///
use core::ptr::{read_volatile, write_volatile};

// TODO these offsets may be wrong. I'm still unsure about CSR semantics
const REG_DIVISOR_OFFSET: u32 = 0;
const REG_SR_OFFSET: u32 = 2;
const REG_DR_OFFSET: u32 = 3;

const FLAG_SR_TX_FULL: u8 = 1 << 0;
const FLAG_SR_TX_EMPTY: u8 = 1 << 1;
const FLAG_SR_RX_FULL: u8 = 1 << 2;
const FLAG_SR_RX_EMPTY: u8 = 1 << 3;

pub enum Error {
    TxFull,
    RxEmpty,
}

// Hacky derive, shouldn't exist but it's fine :tm:
#[derive(Copy, Clone)]
pub struct AmlibUart {
    base_addr: u32,
}

impl AmlibUart {
    pub fn new(base_addr: u32) -> Self {
        Self { base_addr }
    }

    pub fn read_status(&mut self) -> u8 {
        unsafe { read_volatile((self.base_addr + REG_SR_OFFSET) as *const u8) }
    }

    pub fn try_get_char(&mut self) -> Result<u8, Error> {
        if self.read_status() & FLAG_SR_RX_EMPTY != 0 {
            return Err(Error::RxEmpty);
        }

        unsafe { Ok(read_volatile((self.base_addr + REG_DR_OFFSET) as *const u8)) }
    }

    pub fn try_put_char(&mut self, c: u8) -> Result<(), Error> {
        if self.read_status() & FLAG_SR_TX_FULL != 0 {
            return Err(Error::TxFull);
        }

        unsafe {
            write_volatile((self.base_addr + REG_DR_OFFSET) as *mut u8, c);
        }
        Ok(())
    }
}

// Blocking implementation of write
impl Write for AmlibUart {
    fn write_str(&mut self, s: &str) -> core::fmt::Result {
        for b in s.as_bytes() {
            // It's okay to loop on this because we'll always clear the buffer
            while let Err(Error::TxFull) = self.try_put_char(*b) {}
            //self.try_put_char(*b);
        }

        Ok(())
    }
}
