use core::fmt::Arguments;

use libk_util::{sync::spin_rwlock::IrqSafeRwLockReadGuard, StaticVector};

use super::sink::{DebugSinkWrapper, DEBUG_SINKS, MAX_DEBUG_SINKS};

/// Locking log sink for dumping panic info
pub struct PanicLoggerSink<'a> {
    lock: IrqSafeRwLockReadGuard<'a, StaticVector<DebugSinkWrapper, MAX_DEBUG_SINKS>>,
}

impl PanicLoggerSink<'_> {
    /// Locks the global logger, allowing the panicking CPU to dump its panic info without being
    /// interrupted
    pub fn lock() -> Self {
        Self {
            lock: DEBUG_SINKS.read(),
        }
    }

    /// Prints a formatted message to the panic log
    pub fn log_fmt(&self, args: Arguments) {
        use log::Log;

        self.log(
            &log::Record::builder()
                .level(log::Level::Error)
                .target(":raw")
                .args(args)
                .build(),
        )
    }
}

impl log::Log for PanicLoggerSink<'_> {
    fn enabled(&self, _metadata: &log::Metadata) -> bool {
        true
    }

    fn log(&self, record: &log::Record) {
        for sink in self.lock.iter() {
            if sink.enabled(record.metadata()) {
                sink.log(record);
            }
        }
    }

    fn flush(&self) {}
}

/// Logs a formatted message to the panic output
pub macro panic_log($sink:expr, $($args:tt)+) {
    $sink.log_fmt(format_args!($($args)+))
}
