//! The module implementing the printing features.
//!
//! For now, the console is implemented here, this may change in the future.
//! The console is implemented using the magic qemu output right now.

use core::fmt;

use crate::synchronization::PseudoLock;
use crate::synchronization::Mutex;

/// This trait implement the same features as `core::fmt::Write`,
/// except it operate on shared references (`&self`) instead of 
/// mutable references (`&mut self`).
pub trait Write {
    /// Write a single character.
    fn write_char(&self, c: char);

    /// Write a Rust format string.
    fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;

    /// Block until the last buffered character has been physically put on the TX wire.
    fn flush(&self);
}

/// Inner Qemu output.
struct QemuOutputInner;

/// Qemu output, access to the inner ressources is protected by
/// a mutex.
struct QemuOutput {
    inner: PseudoLock<QemuOutputInner>,
}

/// The address for the magic qemu output
const QEMU_MAGIC_OUTPUT_ADDR: *mut u8 = 0x3F20_1000 as *mut u8;

static QEMU_OUTPUT: QemuOutput = QemuOutput::new();

impl QemuOutputInner {
    /// Constructor for QemuOutputInner.
    const fn new() -> Self {
        Self {}
    }

    /// Write a character to the output.
    fn write_char(&mut self, c: char) {
        unsafe {
            core::ptr::write_volatile(QEMU_MAGIC_OUTPUT_ADDR, c as u8);
        }
    }
}

impl QemuOutput {
    pub const fn new() -> Self {
        Self {
            inner: PseudoLock::new(QemuOutputInner::new()),
        }
    }
}

/// Allow to use QemuOutputInner for print! and formating macros.
/// `write_str` needs `&mut self` (mutable ref), so we can implement 
/// it only on the inner type, the `QemuOutput` need to be manipulable
/// using unmutable references (`&self`), so we will use a custom 
/// interface for it.
impl fmt::Write for QemuOutputInner {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        for c in s.chars() {
            // \n -> \r\n
            if c == '\n' {
                self.write_char('\r');
            }
            self.write_char(c);
        }
        Ok(())
    }
}

impl Write for QemuOutput {
    fn write_char(&self, c: char) {
        self.inner.lock(|q_out| q_out.write_char(c))
    }

    fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result {
        self.inner.lock(|q_out| fmt::Write::write_fmt(q_out, args))
    }

    /// Empty function, the qemu uart has no buffering afaik
    fn flush(&self) {
        // self.inner.lock(|q_out| q_out.flush())
    }
}

/// Return a reference to the Qemu Output.
pub fn console() -> &'static dyn Write {
    &QEMU_OUTPUT
}

/// The backend for printing to the console.
pub fn _print(args: fmt::Arguments) {
    console().write_fmt(args).unwrap();
}

/// The printing macro.
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*)));
}

/// The line printing macro.
#[macro_export]
macro_rules! println {
    () => ($crate::print!("\n"));
    ($($arg:tt)*) => ({
        $crate::print::_print(format_args_nl!($($arg)*));
    })
}
