//! Debug tracing module

use core::fmt;

pub use abi::debug::TraceLevel;
use abi::{error::Error, option::RequestValue};

/// Prints a debug message using DebugTrace syscall
#[macro_export]
macro_rules! debug_trace {
    ($level:ident, $($args:tt)+) => {
        $crate::debug::_debug_trace($crate::debug::TraceLevel::$level, format_args!($($args)+))
    };
}

struct TracePrinter {
    buf: [u8; 512],
    len: usize,
}

impl fmt::Write for TracePrinter {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        for &c in s.as_bytes() {
            self.buf[self.len] = c;
            self.len += 1;
        }
        Ok(())
    }
}

/// Trace raw message into the kernel's log. If the message is not a valid unicode string, it will
/// get discarded.
pub fn trace_raw(level: TraceLevel, data: &[u8]) {
    if let Ok(str) = core::str::from_utf8(data) {
        unsafe {
            crate::sys::debug_trace(level, str);
        }
    }
}

#[doc(hidden)]
pub fn _debug_trace(level: TraceLevel, a: core::fmt::Arguments<'_>) {
    use fmt::Write;

    let mut printer = TracePrinter {
        buf: [0; 512],
        len: 0,
    };
    printer.write_fmt(a).ok();

    if printer.len != 0 {
        unsafe {
            let s = core::str::from_utf8_unchecked(&printer.buf[..printer.len]);
            crate::sys::debug_trace(level, s);
        }
    }
}

/// Perform a debugging operation on a thread
pub fn debug_control<'de, T: RequestValue<'de>>(
    tid: u32,
    buffer: &'de mut [u8],
    request: &T::Request,
) -> Result<T::Response, Error> {
    let len = T::store_request(request, buffer)?;
    let len = unsafe { crate::sys::debug_control(tid, T::VARIANT.into(), buffer, len) }?;
    T::load_response(&buffer[..len])
}
