use ash::vk;
use tracing::{Level, event};

/// # Safety
/// `str` must be a valid null-terminated C string or null.
unsafe fn str_from_raw_parts<'a>(str: *const i8) -> std::borrow::Cow<'a, str> {
    use std::{borrow::Cow, ffi};
    if str.is_null() {
        Cow::from("")
    } else {
        unsafe { ffi::CStr::from_ptr(str).to_string_lossy() }
    }
}

pub(super) unsafe extern "system" fn debug_callback(
    message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
    message_type: vk::DebugUtilsMessageTypeFlagsEXT,
    callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>,
    user_data: *mut core::ffi::c_void,
) -> vk::Bool32 {
    _ = user_data;
    // SAFETY:
    // - `callback_data` is a valid pointer to `DebugUtilsMessengerCallbackDataEXT`.
    // - `p_message_id_name` is a valid null-terminated C string or NULL.
    // - `p_message` is a valid null-terminated C string or NULL.
    let (message_id_number, message_id_name, message) = unsafe {
        let callback_data = &*callback_data;

        let message_id_number = callback_data.message_id_number;

        let message_id_name = str_from_raw_parts(callback_data.p_message_id_name);
        let message = str_from_raw_parts(callback_data.p_message);
        (message_id_number, message_id_name, message)
    };

    match message_severity {
        vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => {
            event!(target: "VK::DebugUtils", Level::ERROR, "{message_type:?} [{message_id_name}({message_id_number})]: {message}");
        }
        vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => {
            event!(target: "VK::DebugUtils", Level::TRACE, "{message_type:?} [{message_id_name}({message_id_number})]: {message}");
        }
        vk::DebugUtilsMessageSeverityFlagsEXT::INFO => {
            event!(target: "VK::DebugUtils", Level::INFO, "{message_type:?} [{message_id_name}({message_id_number})]: {message}");
        }
        vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => {
            event!(target: "VK::DebugUtils", Level::WARN, "{message_type:?} [{message_id_name}({message_id_number})]: {message}");
        }
        _ => unreachable!(),
    }

    vk::FALSE
}
