public import includes;
import p = platform;
import vulkan : Vulkan, VULKAN_LIBS;

// Global Functions

PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = null;
PFN_vkCreateInstance vkCreateInstance = null;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties = null;

// Instance Functions

PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices = null;
PFN_vkCreateDevice vkCreateDevice = null;
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties = null;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR = null;
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties = null;
PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2 = null;
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties = null;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR = null;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR = null;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = null;
PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties = null;
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = null;
PFN_vkDestroyInstance vkDestroyInstance = null;
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR = null;

debug
{
	PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = null;
	PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = null;
}

// Platform Functions

version(linux)
{
	PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR = null;
};

version(Windows)
{
	PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = null;
};


// Device Functions

PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = null;
PFN_vkCreateImage vkCreateImage = null;
PFN_vkCreateImageView vkCreateImageView = null;
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR = null;
PFN_vkGetDeviceQueue vkGetDeviceQueue = null;
PFN_vkCreateSemaphore vkCreateSemaphore = null;
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers = null;
PFN_vkCreateCommandPool vkCreateCommandPool = null;
PFN_vkCreateFence vkCreateFence = null;
PFN_vkCreateDescriptorPool vkCreateDescriptorPool = null;
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout = null;
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets = null;
PFN_vkCreatePipelineLayout vkCreatePipelineLayout = null;
PFN_vkResetDescriptorPool vkResetDescriptorPool = null;
PFN_vkCreateShaderModule vkCreateShaderModule = null;
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines = null;
PFN_vkCreateComputePipelines vkCreateComputePipelines = null;
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets = null;
PFN_vkDestroyDevice vkDestroyDevice = null;
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool = null;
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR = null;
PFN_vkDestroyImage vkDestroyImage = null;
PFN_vkDestroyImageView vkDestroyImageView = null;
PFN_vkDestroyCommandPool vkDestroyCommandPool = null;
PFN_vkDestroySemaphore vkDestroySemaphore = null;
PFN_vkDestroyFence vkDestroyFence = null;
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout = null;
PFN_vkDestroyPipeline vkDestroyPipeline = null;
PFN_vkWaitForFences vkWaitForFences = null;
PFN_vkBeginCommandBuffer vkBeginCommandBuffer = null;
PFN_vkEndCommandBuffer vkEndCommandBuffer = null;
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR = null;
PFN_vkCmdBindPipeline vkCmdBindPipeline = null;
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets = null;
PFN_vkCmdDispatch vkCmdDispatch = null;
PFN_vkCmdBeginRendering vkCmdBeginRendering = null;
PFN_vkCmdEndRendering vkCmdEndRendering = null;
PFN_vkCmdSetViewport vkCmdSetViewport = null;
PFN_vkCmdSetScissor vkCmdSetScissor = null;
PFN_vkCmdPushConstants vkCmdPushConstants = null;
PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer = null;
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers = null;
PFN_vkCmdDrawIndexed vkCmdDrawIndexed = null;
PFN_vkCmdBlitImage2 vkCmdBlitImage2 = null;
PFN_vkCmdPipelineBarrier2 vkCmdPipelineBarrier2 = null;
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage = null;
PFN_vkCmdCopyBuffer vkCmdCopyBuffer = null;
PFN_vkQueueSubmit2 vkQueueSubmit2 = null;
PFN_vkResetFences vkResetFences = null;
PFN_vkResetCommandBuffer vkResetCommandBuffer = null;
PFN_vkFreeCommandBuffers vkFreeCommandBuffers = null;
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout = null;
PFN_vkDestroyShaderModule vkDestroyShaderModule = null;
PFN_vkQueuePresentKHR vkQueuePresentKHR = null;
PFN_vkCmdDraw vkCmdDraw = null;
PFN_vkDeviceWaitIdle vkDeviceWaitIdle = null;
PFN_vkCmdClearColorImage vkCmdClearColorImage = null;
PFN_vkCreateSampler vkCreateSampler = null;
PFN_vkDestroySampler vkDestroySampler = null;
PFN_vkGetBufferDeviceAddress vkGetBufferDeviceAddress = null;
PFN_vkWaitSemaphores vkWaitSemaphores = null;
PFN_vkQueueWaitIdle vkQueueWaitIdle = null;

bool 
LoadGlobalFunctions()
{
	p.Library lib;
	p.Function fn;
	foreach(name; VULKAN_LIBS) 
	{
		lib = p.LoadLibrary(name);
		if (lib.ptr)
		{
			fn = p.LoadFunction(lib, "vkGetInstanceProcAddr");
			vkGetInstanceProcAddr = cast(PFN_vkGetInstanceProcAddr)fn.ptr;
		}
	}

	if (fn.ptr)
	{
		vkGetInstanceProcAddr = cast(PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr(null, "vkGetInstanceProcAddr");
		assert(vkGetInstanceProcAddr != null, "LoadGlobalFunctions failure: Unable to load vkGetInstanceProcAddr");

		vkCreateInstance = cast(PFN_vkCreateInstance)vkGetInstanceProcAddr(null, "vkCreateInstance");
		assert(vkCreateInstance != null, "LoadGlobalFunctions failure: Unable to load VkCreateInstance");

		vkEnumerateInstanceLayerProperties = cast(PFN_vkEnumerateInstanceLayerProperties)vkGetInstanceProcAddr(null, "vkEnumerateInstanceLayerProperties");
		assert(vkEnumerateInstanceLayerProperties != null, "LoadGlobalFunctions failure: Unable to load vkEnumerateInstanceLayerProperties");
	}

	return fn.ptr != null;
}

void
LoadDeviceFunctions(Vulkan* vk)
{
	vkCreateSwapchainKHR = cast(PFN_vkCreateSwapchainKHR)vkGetDeviceProcAddr(vk.device, "vkCreateSwapchainKHR");
	vkCreateImage = cast(PFN_vkCreateImage)vkGetDeviceProcAddr(vk.device, "vkCreateImage");
	vkCreateImageView = cast(PFN_vkCreateImageView)vkGetDeviceProcAddr(vk.device, "vkCreateImageView");
	vkGetSwapchainImagesKHR = cast(PFN_vkGetSwapchainImagesKHR)vkGetDeviceProcAddr(vk.device, "vkGetSwapchainImagesKHR");
	vkGetDeviceQueue = cast(PFN_vkGetDeviceQueue)vkGetDeviceProcAddr(vk.device, "vkGetDeviceQueue");
	vkCreateSemaphore = cast(PFN_vkCreateSemaphore)vkGetDeviceProcAddr(vk.device, "vkCreateSemaphore");
	vkAllocateCommandBuffers = cast(PFN_vkAllocateCommandBuffers)vkGetDeviceProcAddr(vk.device, "vkAllocateCommandBuffers");
	vkCreateCommandPool = cast(PFN_vkCreateCommandPool)vkGetDeviceProcAddr(vk.device, "vkCreateCommandPool");
	vkCreateFence = cast(PFN_vkCreateFence)vkGetDeviceProcAddr(vk.device, "vkCreateFence");
	vkCreateDescriptorPool = cast(PFN_vkCreateDescriptorPool)vkGetDeviceProcAddr(vk.device, "vkCreateDescriptorPool");
	vkCreateDescriptorSetLayout = cast(PFN_vkCreateDescriptorSetLayout)vkGetDeviceProcAddr(vk.device, "vkCreateDescriptorSetLayout");
	vkAllocateDescriptorSets = cast(PFN_vkAllocateDescriptorSets)vkGetDeviceProcAddr(vk.device, "vkAllocateDescriptorSets");
	vkCreatePipelineLayout = cast(PFN_vkCreatePipelineLayout)vkGetDeviceProcAddr(vk.device, "vkCreatePipelineLayout");
	vkResetDescriptorPool = cast(PFN_vkResetDescriptorPool)vkGetDeviceProcAddr(vk.device, "vkResetDescriptorPool");
	vkCreateShaderModule = cast(PFN_vkCreateShaderModule)vkGetDeviceProcAddr(vk.device, "vkCreateShaderModule");
	vkCreateGraphicsPipelines = cast(PFN_vkCreateGraphicsPipelines)vkGetDeviceProcAddr(vk.device, "vkCreateGraphicsPipelines");
	vkCreateComputePipelines = cast(PFN_vkCreateComputePipelines)vkGetDeviceProcAddr(vk.device, "vkCreateComputePipelines");
	vkUpdateDescriptorSets = cast(PFN_vkUpdateDescriptorSets)vkGetDeviceProcAddr(vk.device, "vkUpdateDescriptorSets");
	vkDestroyDevice = cast(PFN_vkDestroyDevice)vkGetDeviceProcAddr(vk.device, "vkDestroyDevice");
	vkDestroyDescriptorPool = cast(PFN_vkDestroyDescriptorPool)vkGetDeviceProcAddr(vk.device, "vkDestroyDescriptorPool");
	vkDestroySwapchainKHR = cast(PFN_vkDestroySwapchainKHR)vkGetDeviceProcAddr(vk.device, "vkDestroySwapchainKHR");
	vkDestroyImage = cast(PFN_vkDestroyImage)vkGetDeviceProcAddr(vk.device, "vkDestroyImage");
	vkDestroyImageView = cast(PFN_vkDestroyImageView)vkGetDeviceProcAddr(vk.device, "vkDestroyImageView");
	vkDestroyCommandPool = cast(PFN_vkDestroyCommandPool)vkGetDeviceProcAddr(vk.device, "vkDestroyCommandPool");
	vkDestroySemaphore = cast(PFN_vkDestroySemaphore)vkGetDeviceProcAddr(vk.device, "vkDestroySemaphore");
	vkDestroyFence = cast(PFN_vkDestroyFence)vkGetDeviceProcAddr(vk.device, "vkDestroyFence");
	vkDestroyPipelineLayout = cast(PFN_vkDestroyPipelineLayout)vkGetDeviceProcAddr(vk.device, "vkDestroyPipelineLayout");
	vkDestroyPipeline = cast(PFN_vkDestroyPipeline)vkGetDeviceProcAddr(vk.device, "vkDestroyPipeline");
	vkWaitForFences = cast(PFN_vkWaitForFences)vkGetDeviceProcAddr(vk.device, "vkWaitForFences");
	vkBeginCommandBuffer = cast(PFN_vkBeginCommandBuffer)vkGetDeviceProcAddr(vk.device, "vkBeginCommandBuffer");
	vkEndCommandBuffer = cast(PFN_vkEndCommandBuffer)vkGetDeviceProcAddr(vk.device, "vkEndCommandBuffer");
	vkAcquireNextImageKHR = cast(PFN_vkAcquireNextImageKHR)vkGetDeviceProcAddr(vk.device, "vkAcquireNextImageKHR");
	vkCmdBindPipeline = cast(PFN_vkCmdBindPipeline)vkGetDeviceProcAddr(vk.device, "vkCmdBindPipeline");
	vkCmdBindDescriptorSets = cast(PFN_vkCmdBindDescriptorSets)vkGetDeviceProcAddr(vk.device, "vkCmdBindDescriptorSets");
	vkCmdDispatch = cast(PFN_vkCmdDispatch)vkGetDeviceProcAddr(vk.device, "vkCmdDispatch");
	vkCmdBeginRendering = cast(PFN_vkCmdBeginRendering)vkGetDeviceProcAddr(vk.device, "vkCmdBeginRendering");
	vkCmdEndRendering = cast(PFN_vkCmdEndRendering)vkGetDeviceProcAddr(vk.device, "vkCmdEndRendering");
	vkCmdSetViewport = cast(PFN_vkCmdSetViewport)vkGetDeviceProcAddr(vk.device, "vkCmdSetViewport");
	vkCmdSetScissor = cast(PFN_vkCmdSetScissor)vkGetDeviceProcAddr(vk.device, "vkCmdSetScissor");
	vkCmdPushConstants = cast(PFN_vkCmdPushConstants)vkGetDeviceProcAddr(vk.device, "vkCmdPushConstants");
	vkCmdBindIndexBuffer = cast(PFN_vkCmdBindIndexBuffer)vkGetDeviceProcAddr(vk.device, "vkCmdBindIndexBuffer");
	vkCmdBindVertexBuffers = cast(PFN_vkCmdBindVertexBuffers)vkGetDeviceProcAddr(vk.device, "vkCmdBindVertexBuffers");
	vkCmdDrawIndexed = cast(PFN_vkCmdDrawIndexed)vkGetDeviceProcAddr(vk.device, "vkCmdDrawIndexed");
	vkCmdBlitImage2 = cast(PFN_vkCmdBlitImage2)vkGetDeviceProcAddr(vk.device, "vkCmdBlitImage2");
	vkCmdPipelineBarrier2 = cast(PFN_vkCmdPipelineBarrier2)vkGetDeviceProcAddr(vk.device, "vkCmdPipelineBarrier2");
	vkCmdCopyBufferToImage = cast(PFN_vkCmdCopyBufferToImage)vkGetDeviceProcAddr(vk.device, "vkCmdCopyBufferToImage");
	vkCmdCopyBuffer = cast(PFN_vkCmdCopyBuffer)vkGetDeviceProcAddr(vk.device, "vkCmdCopyBuffer");
	vkQueueSubmit2 = cast(PFN_vkQueueSubmit2)vkGetDeviceProcAddr(vk.device, "vkQueueSubmit2");
	vkResetFences = cast(PFN_vkResetFences)vkGetDeviceProcAddr(vk.device, "vkResetFences");
	vkResetCommandBuffer = cast(PFN_vkResetCommandBuffer)vkGetDeviceProcAddr(vk.device, "vkResetCommandBuffer");
	vkFreeCommandBuffers = cast(PFN_vkFreeCommandBuffers)vkGetDeviceProcAddr(vk.device, "vkFreeCommandBuffers");
	vkDestroyDescriptorSetLayout = cast(PFN_vkDestroyDescriptorSetLayout)vkGetDeviceProcAddr(vk.device, "vkDestroyDescriptorSetLayout");
	vkDestroyShaderModule = cast(PFN_vkDestroyShaderModule)vkGetDeviceProcAddr(vk.device, "vkDestroyShaderModule");
	vkQueuePresentKHR = cast(PFN_vkQueuePresentKHR)vkGetDeviceProcAddr(vk.device, "vkQueuePresentKHR");
	vkCmdDraw = cast(PFN_vkCmdDraw)vkGetDeviceProcAddr(vk.device, "vkCmdDraw");
	vkDeviceWaitIdle = cast(PFN_vkDeviceWaitIdle)vkGetDeviceProcAddr(vk.device, "vkDeviceWaitIdle");
	vkCmdClearColorImage = cast(PFN_vkCmdClearColorImage)vkGetDeviceProcAddr(vk.device, "vkCmdClearColorImage");
	vkCreateSampler = cast(PFN_vkCreateSampler)vkGetDeviceProcAddr(vk.device, "vkCreateSampler");
	vkDestroySampler = cast(PFN_vkDestroySampler)vkGetDeviceProcAddr(vk.device, "vkDestroySampler");
	vkGetBufferDeviceAddress = cast(PFN_vkGetBufferDeviceAddress)vkGetDeviceProcAddr(vk.device, "vkGetBufferDeviceAddress");
	vkWaitSemaphores = cast(PFN_vkWaitSemaphores)vkGetDeviceProcAddr(vk.device, "vkWaitSemaphores");
	vkQueueWaitIdle = cast(PFN_vkQueueWaitIdle)vkGetDeviceProcAddr(vk.device, "vkQueueWaitIdle");

	assert(vkCreateSwapchainKHR != null, "LoadDeviceFunctions failure: function pointer is null");
}

void
LoadInstanceFunctions(Vulkan* vk)
{
	vkEnumeratePhysicalDevices = cast(PFN_vkEnumeratePhysicalDevices)vkGetInstanceProcAddr(vk.instance, "vkEnumeratePhysicalDevices");
	vkDestroySurfaceKHR = cast(PFN_vkDestroySurfaceKHR)vkGetInstanceProcAddr(vk.instance, "vkDestroySurfaceKHR");
	vkDestroyInstance = cast(PFN_vkDestroyInstance)vkGetInstanceProcAddr(vk.instance, "vkDestroyInstance");
	vkCreateDevice = cast(PFN_vkCreateDevice)vkGetInstanceProcAddr(vk.instance, "vkCreateDevice");
	vkGetPhysicalDeviceQueueFamilyProperties = cast(PFN_vkGetPhysicalDeviceQueueFamilyProperties)vkGetInstanceProcAddr(vk.instance, "vkGetPhysicalDeviceQueueFamilyProperties");
	vkGetPhysicalDeviceSurfaceSupportKHR = cast(PFN_vkGetPhysicalDeviceSurfaceSupportKHR)vkGetInstanceProcAddr(vk.instance, "vkGetPhysicalDeviceSurfaceSupportKHR");
	vkGetPhysicalDeviceProperties = cast(PFN_vkGetPhysicalDeviceProperties)vkGetInstanceProcAddr(vk.instance, "vkGetPhysicalDeviceProperties");
	vkGetPhysicalDeviceFeatures2 = cast(PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(vk.instance, "vkGetPhysicalDeviceFeatures2");
	vkEnumerateDeviceExtensionProperties = cast(PFN_vkEnumerateDeviceExtensionProperties)vkGetInstanceProcAddr(vk.instance, "vkEnumerateDeviceExtensionProperties");
	vkGetPhysicalDeviceSurfacePresentModesKHR = cast(PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)vkGetInstanceProcAddr(vk.instance, "vkGetPhysicalDeviceSurfacePresentModesKHR");
	vkGetPhysicalDeviceSurfaceFormatsKHR = cast(PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)vkGetInstanceProcAddr(vk.instance, "vkGetPhysicalDeviceSurfaceFormatsKHR");
	vkGetPhysicalDeviceSurfaceCapabilitiesKHR = cast(PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)vkGetInstanceProcAddr(vk.instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
	vkGetPhysicalDeviceImageFormatProperties = cast(PFN_vkGetPhysicalDeviceImageFormatProperties)vkGetInstanceProcAddr(vk.instance, "vkGetPhysicalDeviceImageFormatProperties");
	vkGetDeviceProcAddr = cast(PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(vk.instance, "vkGetDeviceProcAddr");

version(linux)
{
	vkCreateXcbSurfaceKHR = cast(PFN_vkCreateXcbSurfaceKHR)vkGetInstanceProcAddr(vk.instance, "vkCreateXcbSurfaceKHR");
}

version(Windows)
{
	vkCreateWin32SurfaceKHR = cast(PFN_vkCreateWin32SurfaceKHR)vkGetInstanceProcAddr(vk.instance, "vkCreateWin32SurfaceKHR");
}

debug
{
	vkCreateDebugUtilsMessengerEXT = cast(PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vk.instance, "vkCreateDebugUtilsMessengerEXT");
	vkDestroyDebugUtilsMessengerEXT = cast(PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vk.instance, "vkDestroyDebugUtilsMessengerEXT");
}

	assert(vkEnumeratePhysicalDevices != null, "LoadInstanceFunctions failure: failed to load function");
}
