use crate::instance_handles::instance_fns;
use vulkan_sys::prelude::*;

static mut DEVICE_FN_HANDLES: Option<VkDeviceHandles> = None;

pub fn is_device_fns_initialized() -> bool {
    unsafe { DEVICE_FN_HANDLES.is_some() }
}

pub fn device_fns() -> &'static VkDeviceHandles {
    unsafe { DEVICE_FN_HANDLES.as_ref().unwrap() }
}

pub fn set_device_fns(handles: VkDeviceHandles) {
    unsafe { DEVICE_FN_HANDLES = Some(handles) };
}

pub struct VkDeviceHandles {
    pub device: VkDevice,

    pub device_functions: DeviceFunctions,
    pub device_wsi_functions: DeviceWSIFunctions,
    pub maintenance3_functions: Maintenance3Functions,

    pub acceleration_structure_functions: AccelerationStructureFunctions,
    pub ray_tracing_pipeline_functions: RayTracingPipelineFunctions,

    pub deferred_operation_functions: DeferredOperationsFunctions,
}

impl VkDeviceHandles {
    pub fn new(device: VkDevice) -> Self {
        let instance = &instance_fns().instance_functions;

        Self {
            device,

            device_functions: DeviceFunctions::new(instance, device),
            device_wsi_functions: DeviceWSIFunctions::new(instance, device),
            maintenance3_functions: Maintenance3Functions::new(instance, device),
            acceleration_structure_functions: AccelerationStructureFunctions::new(instance, device),
            ray_tracing_pipeline_functions: RayTracingPipelineFunctions::new(instance, device),
            deferred_operation_functions: DeferredOperationsFunctions::new(instance, device),
        }
    }
}

macro_rules! cmp_device_fn {
    ($name:ident, {$([$fns:ident, $fn_name:ident],)*}) => {
        match $name {
            $(
                stringify!($fn_name) => return unsafe {
                    mem::transmute(
                        device_fns()
                            .$fns
                            .$fn_name,
                    )
                },
            )*

            _ => ()
        }

    }
}