import vulkan;

import dlibincludes;

// Global Functions

PFN_vkGetInstanceProcAddr              vkGetInstanceProcAddr;
PFN_vkCreateInstance                   vkCreateInstance;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;

// Instance Functions

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

version(VULKAN_DEBUG)
{
	PFN_vkCreateDebugUtilsMessengerEXT  vkCreateDebugUtilsMessengerEXT;
	PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT;
}

// Platform Functions

version(linux)
{
	PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
}

version(Windows)
{
	PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
}


version(AMD_GPU) version(VULKAN_DEBUG)
{
	PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD;
}

// Device Functions

PFN_vkCreateSwapchainKHR         vkCreateSwapchainKHR;
PFN_vkCmdBeginRenderPass         vkCmdBeginRenderPass;
PFN_vkCmdEndRenderPass           vkCmdEndRenderPass;
PFN_vkCreateFramebuffer          vkCreateFramebuffer;
PFN_vkDestroyFramebuffer         vkDestroyFramebuffer;
PFN_vkCreateRenderPass           vkCreateRenderPass;
PFN_vkDestroyRenderPass          vkDestroyRenderPass;
PFN_vkCreateImage                vkCreateImage;
PFN_vkCreateImageView            vkCreateImageView;
PFN_vkCreateBufferView           vkCreateBufferView;
PFN_vkGetSwapchainImagesKHR      vkGetSwapchainImagesKHR;
PFN_vkGetDeviceQueue             vkGetDeviceQueue;
PFN_vkCreateSemaphore            vkCreateSemaphore;
PFN_vkAllocateCommandBuffers     vkAllocateCommandBuffers;
PFN_vkCreateCommandPool          vkCreateCommandPool;
PFN_vkCreateFence                vkCreateFence;
PFN_vkCreateDescriptorPool       vkCreateDescriptorPool;
PFN_vkCreateDescriptorSetLayout  vkCreateDescriptorSetLayout;
PFN_vkAllocateDescriptorSets     vkAllocateDescriptorSets;
PFN_vkCreatePipelineLayout       vkCreatePipelineLayout;
PFN_vkResetDescriptorPool        vkResetDescriptorPool;
PFN_vkCreateShaderModule         vkCreateShaderModule;
PFN_vkCreateGraphicsPipelines    vkCreateGraphicsPipelines;
PFN_vkCreateComputePipelines     vkCreateComputePipelines;
PFN_vkUpdateDescriptorSets       vkUpdateDescriptorSets;
PFN_vkDestroyDevice              vkDestroyDevice;
PFN_vkDestroyDescriptorPool      vkDestroyDescriptorPool;
PFN_vkDestroySwapchainKHR        vkDestroySwapchainKHR;
PFN_vkDestroyBufferView          vkDestroyBufferView;
PFN_vkQueueSubmit2KHR            vkQueueSubmit2KHR;
PFN_vkDestroyImage               vkDestroyImage;
PFN_vkCmdBlitImage               vkCmdBlitImage;
PFN_vkDestroyImageView           vkDestroyImageView;
PFN_vkDestroyCommandPool         vkDestroyCommandPool;
PFN_vkDestroySemaphore           vkDestroySemaphore;
PFN_vkDestroyFence               vkDestroyFence;
PFN_vkDestroyPipelineLayout      vkDestroyPipelineLayout;
PFN_vkCmdPipelineBarrier         vkCmdPipelineBarrier;
PFN_vkCmdPipelineBarrier2KHR     vkCmdPipelineBarrier2KHR;
PFN_vkDestroyPipeline            vkDestroyPipeline;
PFN_vkWaitForFences              vkWaitForFences;
PFN_vkBeginCommandBuffer         vkBeginCommandBuffer;
PFN_vkEndCommandBuffer           vkEndCommandBuffer;
PFN_vkAcquireNextImageKHR        vkAcquireNextImageKHR;
PFN_vkCmdBindPipeline            vkCmdBindPipeline;
PFN_vkCmdBindDescriptorSets      vkCmdBindDescriptorSets;
PFN_vkCmdDispatch                vkCmdDispatch;
PFN_vkCmdSetViewport             vkCmdSetViewport;
PFN_vkCmdSetScissor              vkCmdSetScissor;
PFN_vkCmdPushConstants           vkCmdPushConstants;
PFN_vkCmdBindIndexBuffer         vkCmdBindIndexBuffer;
PFN_vkCmdBindVertexBuffers       vkCmdBindVertexBuffers;
PFN_vkCmdDrawIndexed             vkCmdDrawIndexed;
PFN_vkCmdCopyBufferToImage       vkCmdCopyBufferToImage;
PFN_vkCmdCopyBuffer              vkCmdCopyBuffer;
PFN_vkResetFences                vkResetFences;
PFN_vkResetCommandBuffer         vkResetCommandBuffer;
PFN_vkFreeCommandBuffers         vkFreeCommandBuffers;
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
PFN_vkDestroyShaderModule        vkDestroyShaderModule;
PFN_vkQueuePresentKHR            vkQueuePresentKHR;
PFN_vkCmdDraw                    vkCmdDraw;
PFN_vkDeviceWaitIdle             vkDeviceWaitIdle;
PFN_vkCmdClearColorImage         vkCmdClearColorImage;
PFN_vkCreateSampler              vkCreateSampler;
PFN_vkDestroySampler             vkDestroySampler;
PFN_vkGetBufferDeviceAddress     vkGetBufferDeviceAddress;
PFN_vkWaitSemaphores             vkWaitSemaphores;
PFN_vkQueueWaitIdle              vkQueueWaitIdle;
PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT;
PFN_vkCmdSetStencilOpEXT         vkCmdSetStencilOpEXT;
PFN_vkCmdSetStencilReference     vkCmdSetStencilReference;
PFN_vkCmdSetStencilCompareMask   vkCmdSetStencilCompareMask;
PFN_vkCmdSetStencilWriteMask     vkCmdSetStencilWriteMask;
PFN_vkCmdBeginRenderingKHR       vkCmdBeginRenderingKHR;
PFN_vkCmdEndRenderingKHR         vkCmdEndRenderingKHR;

alias vkQueueSubmit2            = vkQueueSubmit2KHR;
alias vkCmdPipelineBarrier2     = vkCmdPipelineBarrier2KHR;
alias vkCmdSetStencilTestEnable = vkCmdSetStencilTestEnableEXT;
alias vkCmdSetStencilOp         = vkCmdSetStencilOpEXT;
alias vkCmdBeginRendering       = vkCmdBeginRenderingKHR;
alias vkCmdEndRendering         = vkCmdEndRenderingKHR;

static string
LoadDeviceFunc(alias fn)()
{
	import std.format : format;
	import std.string : chompPrefix;
	enum string fn_str = __traits(identifier, fn);
	enum string vk_str = __traits(identifier, g_vk);
	return format("%s = cast(typeof(%s))vkGetDeviceProcAddr(%s.device, \"%s\"); assert(%s != null, \"Function pointer %s is null\");", fn_str, fn_str, vk_str, fn_str, fn_str, fn_str);
}

static string
LoadInstanceFunc(alias fn)()
{
	import std.format : format;
	import std.string : chompPrefix;
	enum string fn_str = __traits(identifier, fn);
	enum string vk_str = __traits(identifier, g_vk);
	return format("%s = cast(typeof(%s))vkGetInstanceProcAddr(%s.instance, \"%s\"); assert(%s != null, \"Function pointer %s is null\");", fn_str, fn_str, vk_str, fn_str, fn_str, fn_str);
}

void
LoadDeviceFunctions()
{
	mixin(LoadDeviceFunc!(vkCreateSwapchainKHR));
	mixin(LoadDeviceFunc!(vkCmdBeginRenderPass));
	mixin(LoadDeviceFunc!(vkCmdEndRenderPass));
	mixin(LoadDeviceFunc!(vkCreateFramebuffer));
	mixin(LoadDeviceFunc!(vkDestroyFramebuffer));
	mixin(LoadDeviceFunc!(vkCreateRenderPass));
	mixin(LoadDeviceFunc!(vkDestroyRenderPass));
	mixin(LoadDeviceFunc!(vkCreateImage));
	mixin(LoadDeviceFunc!(vkCreateImageView));
	mixin(LoadDeviceFunc!(vkQueueSubmit2));
	mixin(LoadDeviceFunc!(vkCreateBufferView));
	mixin(LoadDeviceFunc!(vkGetSwapchainImagesKHR));
	mixin(LoadDeviceFunc!(vkGetDeviceQueue));
	mixin(LoadDeviceFunc!(vkCreateSemaphore));
	mixin(LoadDeviceFunc!(vkAllocateCommandBuffers));
	mixin(LoadDeviceFunc!(vkCreateCommandPool));
	mixin(LoadDeviceFunc!(vkCmdPipelineBarrier));
	mixin(LoadDeviceFunc!(vkCmdPipelineBarrier2));
	mixin(LoadDeviceFunc!(vkCreateFence));
	mixin(LoadDeviceFunc!(vkCreateDescriptorPool));
	mixin(LoadDeviceFunc!(vkCreateDescriptorSetLayout));
	mixin(LoadDeviceFunc!(vkAllocateDescriptorSets));
	mixin(LoadDeviceFunc!(vkCreatePipelineLayout));
	mixin(LoadDeviceFunc!(vkResetDescriptorPool));
	mixin(LoadDeviceFunc!(vkCreateShaderModule));
	mixin(LoadDeviceFunc!(vkCreateGraphicsPipelines));
	mixin(LoadDeviceFunc!(vkCreateComputePipelines));
	mixin(LoadDeviceFunc!(vkUpdateDescriptorSets));
	mixin(LoadDeviceFunc!(vkDestroyDevice));
	mixin(LoadDeviceFunc!(vkDestroyDescriptorPool));
	mixin(LoadDeviceFunc!(vkDestroySwapchainKHR));
	mixin(LoadDeviceFunc!(vkDestroyBufferView));
	mixin(LoadDeviceFunc!(vkDestroyImage));
	mixin(LoadDeviceFunc!(vkDestroyImageView));
	mixin(LoadDeviceFunc!(vkDestroyCommandPool));
	mixin(LoadDeviceFunc!(vkDestroySemaphore));
	mixin(LoadDeviceFunc!(vkDestroyFence));
	mixin(LoadDeviceFunc!(vkDestroyPipelineLayout));
	mixin(LoadDeviceFunc!(vkDestroyPipeline));
	mixin(LoadDeviceFunc!(vkWaitForFences));
	mixin(LoadDeviceFunc!(vkBeginCommandBuffer));
	mixin(LoadDeviceFunc!(vkEndCommandBuffer));
	mixin(LoadDeviceFunc!(vkAcquireNextImageKHR));
	mixin(LoadDeviceFunc!(vkCmdBindPipeline));
	mixin(LoadDeviceFunc!(vkCmdBindDescriptorSets));
	mixin(LoadDeviceFunc!(vkCmdDispatch));
	mixin(LoadDeviceFunc!(vkCmdSetViewport));
	mixin(LoadDeviceFunc!(vkCmdSetScissor));
	mixin(LoadDeviceFunc!(vkCmdPushConstants));
	mixin(LoadDeviceFunc!(vkCmdBindIndexBuffer));
	mixin(LoadDeviceFunc!(vkCmdBindVertexBuffers));
	mixin(LoadDeviceFunc!(vkCmdDrawIndexed));
	mixin(LoadDeviceFunc!(vkCmdBlitImage));
	mixin(LoadDeviceFunc!(vkCmdCopyBufferToImage));
	mixin(LoadDeviceFunc!(vkCmdCopyBuffer));
	mixin(LoadDeviceFunc!(vkResetFences));
	mixin(LoadDeviceFunc!(vkResetCommandBuffer));
	mixin(LoadDeviceFunc!(vkFreeCommandBuffers));
	mixin(LoadDeviceFunc!(vkDestroyDescriptorSetLayout));
	mixin(LoadDeviceFunc!(vkDestroyShaderModule));
	mixin(LoadDeviceFunc!(vkQueuePresentKHR));
	mixin(LoadDeviceFunc!(vkCmdDraw));
	mixin(LoadDeviceFunc!(vkDeviceWaitIdle));
	mixin(LoadDeviceFunc!(vkCmdClearColorImage));
	mixin(LoadDeviceFunc!(vkCreateSampler));
	mixin(LoadDeviceFunc!(vkDestroySampler));
	mixin(LoadDeviceFunc!(vkGetBufferDeviceAddress));
	mixin(LoadDeviceFunc!(vkWaitSemaphores));
	mixin(LoadDeviceFunc!(vkQueueWaitIdle));
	mixin(LoadDeviceFunc!(vkCmdSetStencilTestEnableEXT));
	mixin(LoadDeviceFunc!(vkCmdSetStencilOpEXT));
	mixin(LoadDeviceFunc!(vkCmdSetStencilReference));
	mixin(LoadDeviceFunc!(vkCmdSetStencilCompareMask));
	mixin(LoadDeviceFunc!(vkCmdSetStencilWriteMask));
	mixin(LoadDeviceFunc!(vkCmdBeginRenderingKHR));
	mixin(LoadDeviceFunc!(vkCmdEndRenderingKHR));

	version(AMD_GPU) version(VULKAN_DEBUG)
	{
		mixin(LoadDeviceFunc!(vkGetShaderInfoAMD));
	}
}

void
LoadInstanceFunctions()
{
	mixin(LoadInstanceFunc!(vkEnumeratePhysicalDevices));
	mixin(LoadInstanceFunc!(vkDestroySurfaceKHR));
	mixin(LoadInstanceFunc!(vkDestroyInstance));
	mixin(LoadInstanceFunc!(vkCreateDevice));
	mixin(LoadInstanceFunc!(vkGetPhysicalDeviceQueueFamilyProperties));
	mixin(LoadInstanceFunc!(vkGetPhysicalDeviceSurfaceSupportKHR));
	mixin(LoadInstanceFunc!(vkGetPhysicalDeviceProperties));
	mixin(LoadInstanceFunc!(vkGetPhysicalDeviceFeatures2));
	mixin(LoadInstanceFunc!(vkEnumerateDeviceExtensionProperties));
	mixin(LoadInstanceFunc!(vkGetPhysicalDeviceSurfacePresentModesKHR));
	mixin(LoadInstanceFunc!(vkGetPhysicalDeviceSurfaceFormatsKHR));
	mixin(LoadInstanceFunc!(vkGetPhysicalDeviceSurfaceCapabilitiesKHR));
	mixin(LoadInstanceFunc!(vkGetPhysicalDeviceImageFormatProperties));
	mixin(LoadInstanceFunc!(vkGetDeviceProcAddr));

	version(linux)
	{
		mixin(LoadInstanceFunc!(vkCreateXlibSurfaceKHR));
	}

	version(Windows)
	{
		mixin(LoadInstanceFunc!(vkCreateWin32SurfaceKHR));
	}

	version(VULKAN_DEBUG)
	{
		mixin(LoadInstanceFunc!(vkCreateDebugUtilsMessengerEXT));
		mixin(LoadInstanceFunc!(vkDestroyDebugUtilsMessengerEXT));
	}
}

