use core::fmt;

use yggdrasil_rt::debug::TraceLevel;

struct LogSink;
struct MessageBuffer {
    buffer: [u8; 256],
    len: usize,
}

impl MessageBuffer {
    pub const fn new() -> Self {
        Self {
            buffer: [0; 256],
            len: 0,
        }
    }
}

impl fmt::Write for MessageBuffer {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let len = (self.buffer.len() - self.len).min(s.len());
        self.buffer[self.len..self.len + len].copy_from_slice(&s.as_bytes()[..len]);
        self.len += len;
        Ok(())
    }
}

impl log::Log for LogSink {
    fn flush(&self) {}

    fn log(&self, record: &log::Record) {
        use fmt::Write;
        let level = match record.level() {
            log::Level::Trace | log::Level::Debug => TraceLevel::Debug,
            log::Level::Info => TraceLevel::Info,
            log::Level::Warn => TraceLevel::Warn,
            log::Level::Error => TraceLevel::Error,
        };
        let mut buffer = MessageBuffer::new();
        write!(buffer, "{}", record.args()).ok();
        let text = match core::str::from_utf8(&buffer.buffer[..buffer.len]) {
            Ok(text) => text,
            Err(error) => {
                let valid = unsafe {
                    core::str::from_utf8_unchecked(&buffer.buffer[..error.valid_up_to()])
                };
                valid
            }
        };
        unsafe {
            yggdrasil_rt::sys::debug_trace(level, text);
        }
    }

    fn enabled(&self, _metadata: &log::Metadata) -> bool {
        true
    }
}

static LOG: LogSink = LogSink;

pub(crate) fn init_log_sink() {
    log::set_max_level(log::LevelFilter::Debug);
    log::set_logger(&LOG).ok();
}
