
// VULKAN CONFIG

static const VkFormat VK_IMAGE_FORMATS[] = {
	VK_FORMAT_R16G16B16A16_UNORM,
	VK_FORMAT_R8G8B8A8_UNORM,
};

static VkApplicationInfo app_info = {
	.sType = STYPE(APPLICATION_INFO),
	.pApplicationName = "Video Game",
	.applicationVersion = VK_MAKE_API_VERSION(0, 0, 0, 1),
	.pEngineName = "None (yet)",
	.engineVersion = VK_MAKE_API_VERSION(0, 0, 0, 1),
	.apiVersion = VK_API_VERSION_1_3,
};

static const char *instance_layers[] = {
#ifdef BUILD_DEBUG
	"VK_LAYER_KHRONOS_validation"
#endif
};


static const char *instance_extensions[] = {
	VK_KHR_SURFACE_EXTENSION_NAME,

#ifdef __linux__
	VK_KHR_XCB_SURFACE_EXTENSION_NAME,
#endif

#ifdef _WIN32
	VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
#endif

#ifdef BUILD_DEBUG
	VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
#endif

};

static VkInstanceCreateInfo inst_info = {
	.sType = STYPE(INSTANCE_CREATE_INFO),
	.pApplicationInfo = &app_info,
	.enabledLayerCount = sizeof(instance_layers) / sizeof(char *),
	.ppEnabledLayerNames = instance_layers,
	.enabledExtensionCount = sizeof(instance_extensions) / sizeof(char *),
	.ppEnabledExtensionNames = instance_extensions,
};

const char *device_extensions[] = {
	VK_KHR_SWAPCHAIN_EXTENSION_NAME,
	VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME,
};

#ifdef BUILD_DEBUG
static VkDebugUtilsMessengerCreateInfoEXT debug_msg_info = {
	.sType = STYPE(DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT),
	.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
										 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
	.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
								 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
								 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
	.pfnUserCallback = (PFN_vkDebugUtilsMessengerCallbackEXT)vDebugCallback,
};
#endif

static VkPhysicalDeviceVulkan13Features vk_13_features = { 
	.sType = STYPE(PHYSICAL_DEVICE_VULKAN_1_3_FEATURES),
	.synchronization2 = VK_TRUE,
	.dynamicRendering = VK_TRUE
};

static VkPhysicalDeviceVulkan12Features vk_12_features = {
	.sType = STYPE(PHYSICAL_DEVICE_VULKAN_1_2_FEATURES),
	.pNext = &vk_13_features,
	.descriptorIndexing = VK_TRUE,
	.bufferDeviceAddress = VK_TRUE,
	.descriptorBindingUniformBufferUpdateAfterBind = VK_TRUE,
	.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE,
	.descriptorBindingStorageImageUpdateAfterBind = VK_TRUE,
	.descriptorBindingStorageBufferUpdateAfterBind = VK_TRUE,
	.descriptorBindingPartiallyBound = VK_TRUE,
	.shaderSampledImageArrayNonUniformIndexing = VK_TRUE,
	.runtimeDescriptorArray = VK_TRUE,
};

static VkPhysicalDeviceVulkan11Features vk_11_features = {
	.sType = STYPE(PHYSICAL_DEVICE_VULKAN_1_1_FEATURES),
	.pNext = &vk_12_features
};

static const VkPhysicalDeviceFeatures2 vk_features_2 = {
	.sType = STYPE(PHYSICAL_DEVICE_FEATURES_2),
	.pNext = &vk_11_features,
	.features = {
		.shaderUniformBufferArrayDynamicIndexing = VK_TRUE,
		.shaderSampledImageArrayDynamicIndexing = VK_TRUE,
		.shaderStorageBufferArrayDynamicIndexing = VK_TRUE,
		.shaderStorageImageArrayDynamicIndexing = VK_TRUE,
		.samplerAnisotropy											 = VK_TRUE,
	},
};

static VkDeviceCreateInfo device_info = {
	.sType = STYPE(DEVICE_CREATE_INFO),
	.pNext = &vk_features_2,
	.ppEnabledExtensionNames = device_extensions,
	.enabledExtensionCount = Len(device_extensions),
	.pEnabledFeatures = NULL,
};

static VmaAllocatorCreateInfo vma_create_info = {
	.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
	.vulkanApiVersion = VK_API_VERSION_1_3,
};

static VkCommandPoolCreateInfo pool_create_info = {
	.sType = STYPE(COMMAND_POOL_CREATE_INFO),
	.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
};

static VkFenceCreateInfo fence_create_info = {
	.sType = STYPE(FENCE_CREATE_INFO),
	.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};

static VkSemaphoreCreateInfo semaphore_create_info = {
	.sType = STYPE(SEMAPHORE_CREATE_INFO),
};

static VkCommandBufferAllocateInfo cmd_buf_info = {
	.sType = STYPE(COMMAND_BUFFER_ALLOCATE_INFO),
	.commandBufferCount = 1,
	.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
};

static VkSwapchainCreateInfoKHR swapchain_create_info = {
	.sType = STYPE(SWAPCHAIN_CREATE_INFO_KHR),
	.imageArrayLayers = 1,
	.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
	.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
	.clipped = VK_TRUE,
	.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
};

static VkImageViewCreateInfo sc_image_view_create_info = {
	.sType = STYPE(IMAGE_VIEW_CREATE_INFO),
	.viewType = VK_IMAGE_VIEW_TYPE_2D,
	.components = {
		.r = VK_COMPONENT_SWIZZLE_IDENTITY,
		.g = VK_COMPONENT_SWIZZLE_IDENTITY,
		.b = VK_COMPONENT_SWIZZLE_IDENTITY,
		.a = VK_COMPONENT_SWIZZLE_IDENTITY,
	},
	.subresourceRange = {
		.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
		.baseMipLevel = 0,
		.levelCount = 1,
		.baseArrayLayer = 0,
		.layerCount = 1,
	},
};

static VkImageCreateInfo draw_image_create_info = {
	.sType = STYPE(IMAGE_CREATE_INFO),
	.imageType = VK_IMAGE_TYPE_2D,
	.mipLevels = 1,
	.arrayLayers = 1,
	.samples = VK_SAMPLE_COUNT_1_BIT,
	.tiling = VK_IMAGE_TILING_OPTIMAL,
	.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
};

static VkImageViewCreateInfo draw_image_view_create_info = {
	.sType = STYPE(IMAGE_VIEW_CREATE_INFO),
	.viewType = VK_IMAGE_VIEW_TYPE_2D,
	.subresourceRange = {
		.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
		.baseMipLevel = 0,
		.levelCount = 1,
		.baseArrayLayer = 0,
		.layerCount = 1,
	},
};

static VkImageCreateInfo depth_image_create_info = {
	.sType = STYPE(IMAGE_CREATE_INFO),
	.imageType = VK_IMAGE_TYPE_2D,
	.mipLevels = 1,
	.arrayLayers = 1,
	.samples = VK_SAMPLE_COUNT_1_BIT,
	.tiling = VK_IMAGE_TILING_OPTIMAL,
	.format = VK_FORMAT_D32_SFLOAT,
	.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
};

static VkImageViewCreateInfo depth_image_view_create_info = {
	.sType = STYPE(IMAGE_VIEW_CREATE_INFO),
	.viewType = VK_IMAGE_VIEW_TYPE_2D,
	.format = VK_FORMAT_D32_SFLOAT,
	.subresourceRange = {
		.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
		.baseMipLevel = 0,
		.levelCount = 1,
		.baseArrayLayer = 0,
		.layerCount = 1,
	},
};

//
// PIPELINES & DESCRIPTORS
//

static VkDescriptorPoolSize descriptor_pool_sizes[] = {
	{ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 4096 },
	{ .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, .descriptorCount = 4096},
	{ .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 4096},
	{ .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 4096},
};

static VkDescriptorPoolCreateInfo desc_pool_info = {
	.sType = STYPE(DESCRIPTOR_POOL_CREATE_INFO),
	.poolSizeCount = Len(descriptor_pool_sizes),
	.pPoolSizes = descriptor_pool_sizes,
	.maxSets = 12, // Not sure if this is correct
	.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
};

static VkDescriptorBindingFlags shared_desc_binding_flags[] = {	
	VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
	VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
	VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
	VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
};

static VkDescriptorSetLayoutBindingFlagsCreateInfo shared_layout_binding_flags_info = {
	.sType = STYPE(DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO),
	.bindingCount = Len(shared_desc_binding_flags),
	.pBindingFlags = shared_desc_binding_flags,
};

static VkDescriptorSetLayoutBinding shared_layout_bindings[] = {
	{ .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_ALL },
	{ .binding = 1, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_ALL },
	{ .binding = 2, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_ALL },
	{ .binding = 3, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_ALL },
};

static VkDescriptorSetLayoutCreateInfo shared_layout_create_info = {
	.sType = STYPE(DESCRIPTOR_SET_LAYOUT_CREATE_INFO),
	.pNext = &shared_layout_binding_flags_info,
	.bindingCount = Len(shared_layout_bindings),
	.pBindings = shared_layout_bindings,
	.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
};

static VkDescriptorType desc_type_map[vDT_MAX] = {
	[vDT_SAMPLED_IMAGE] = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
	[vDT_MATERIAL] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
};

static VkDescriptorBindingFlags bindless_desc_binding_flags = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT;

static VkDescriptorSetLayoutBindingFlagsCreateInfo bindless_layout_flag_create_info = {
	.sType = STYPE(DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO),
	.bindingCount = 1,
	.pBindingFlags = &bindless_desc_binding_flags,
};

static VkDescriptorSetLayoutBinding bindless_layout_binding = {
	.binding = 0,
	.descriptorCount = 1024,
	.stageFlags = VK_SHADER_STAGE_ALL,
};

static VkDescriptorSetLayoutCreateInfo bindless_layout_create_info = {
	.sType = STYPE(DESCRIPTOR_SET_LAYOUT_CREATE_INFO),
	.pNext = &bindless_layout_flag_create_info,
	.bindingCount = 1,
	.pBindings = &bindless_layout_binding,
	.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
};

static VkDescriptorSetAllocateInfo set_allocate_info = {
	.sType = STYPE(DESCRIPTOR_SET_ALLOCATE_INFO),
	.descriptorSetCount = vDT_MAX,
};

static VkPushConstantRange push_const_range = {
	.offset = 0,
	.size = sizeof(rPushConst),
	.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT,
};

static VkPipelineLayoutCreateInfo pipeline_layout_create_info = {
	.sType = STYPE(PIPELINE_LAYOUT_CREATE_INFO),
	.setLayoutCount = vDT_MAX, 
	.pushConstantRangeCount = 1,
	.pPushConstantRanges = &push_const_range,
};

//
//	PIPELINES
//

#define PIPELINE_DYNAMIC_STATE { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }
 

static VkDynamicState pipeline_dynamic_state[] = PIPELINE_DYNAMIC_STATE;

#define PIPELINE_DEFAULT_DYN_STATE_INFO { \
	.sType = STYPE(PIPELINE_DYNAMIC_STATE_CREATE_INFO), \
	.pDynamicStates = pipeline_dynamic_state, \
	.dynamicStateCount = Len(pipeline_dynamic_state), \
}

static VkPipelineDynamicStateCreateInfo pipeline_default_dyn_state_info = PIPELINE_DEFAULT_DYN_STATE_INFO;

#define PIPELINE_DEFAULT_ASSEMBLY_INFO { \
	.sType = STYPE(PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO), \
	.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, \
	.primitiveRestartEnable = VK_FALSE, \
}

static VkPipelineInputAssemblyStateCreateInfo pipeline_default_assembly_info = PIPELINE_DEFAULT_ASSEMBLY_INFO;

#define PIPELINE_DEFAULT_RASTERIZATION_INFO { \
	.sType = STYPE(PIPELINE_RASTERIZATION_STATE_CREATE_INFO), \
	.polygonMode = VK_POLYGON_MODE_FILL, \
	.lineWidth = 1.0, \
	.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, \
}

static VkPipelineRasterizationStateCreateInfo pipeline_default_rasterization_info = PIPELINE_DEFAULT_RASTERIZATION_INFO;

#define PIPELINE_DEFAULT_MULTISAMPLE_INFO { \
	.sType = STYPE(PIPELINE_MULTISAMPLE_STATE_CREATE_INFO), \
	.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, \
	.minSampleShading = 1.0, \
	.alphaToCoverageEnable = VK_FALSE, \
	.alphaToOneEnable = VK_FALSE, \
}

static VkPipelineMultisampleStateCreateInfo pipeline_default_multisample_info = PIPELINE_DEFAULT_MULTISAMPLE_INFO;

#define PIPELINE_DEFAULT_DEPTH_INFO { \
	.sType = STYPE(PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO), \
	.depthTestEnable = VK_TRUE, \
	.depthWriteEnable = VK_TRUE, \
	.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL, \
	.depthBoundsTestEnable = VK_FALSE, \
	.stencilTestEnable = VK_FALSE, \
	.minDepthBounds = 0.0, \
	.maxDepthBounds = 1.0, \
}

static VkPipelineDepthStencilStateCreateInfo pipeline_default_depth_info = PIPELINE_DEFAULT_DEPTH_INFO;

#define PIPELINE_DEFAULT_RENDERING_INFO { \
	.sType = STYPE(PIPELINE_RENDERING_CREATE_INFO), \
	.colorAttachmentCount = 1, \
}

static VkPipelineRenderingCreateInfo pipeline_default_rendering_info = PIPELINE_DEFAULT_RENDERING_INFO;

#define PIPELINE_DEFAULT_BLEND_ATTACH_STATE { \
	.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, \
	.blendEnable = VK_FALSE, \
}

static VkPipelineColorBlendAttachmentState pipeline_default_blend_attach_state = PIPELINE_DEFAULT_BLEND_ATTACH_STATE;

#define PIPELINE_DEFAULT_BLEND_INFO { \
	.sType = STYPE(PIPELINE_COLOR_BLEND_STATE_CREATE_INFO), \
	.logicOpEnable = VK_FALSE, \
	.logicOp = VK_LOGIC_OP_COPY, \
	.attachmentCount = 1, \
	.pAttachments = &pipeline_default_blend_attach_state, \
}

static VkPipelineColorBlendStateCreateInfo pipeline_default_blend_info = PIPELINE_DEFAULT_BLEND_INFO;

#define PIPELINE_DEFAULT_VERTEX_INFO { \
	.sType = STYPE(PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO), \
}

static VkPipelineVertexInputStateCreateInfo pipeline_default_vertex_info = PIPELINE_DEFAULT_VERTEX_INFO;

#define PIPELINE_DEFAULT_VIEWPORT_INFO { \
	.sType = STYPE(PIPELINE_VIEWPORT_STATE_CREATE_INFO), \
	.viewportCount = 1, \
	.scissorCount = 1, \
}

const static VkPipelineViewportStateCreateInfo pipeline_default_viewport_info = PIPELINE_DEFAULT_VIEWPORT_INFO;


#define INIT_PIPELINE_DEFAULTS(pipeline)  																																\
VkPipelineDynamicStateCreateInfo pipeline##_default_dynamic_info = PIPELINE_DEFAULT_DYN_STATE_INFO; 				\
VkPipelineInputAssemblyStateCreateInfo pipeline##_default_assembly_info = PIPELINE_DEFAULT_ASSEMBLY_INFO; 	\
VkPipelineRasterizationStateCreateInfo pipeline##_default_rasterization_info = PIPELINE_DEFAULT_RASTERIZATION_INFO; \
VkPipelineMultisampleStateCreateInfo pipeline##_default_multisample_info = PIPELINE_DEFAULT_MULTISAMPLE_INFO; \
VkPipelineDepthStencilStateCreateInfo pipeline##_default_depth_info = PIPELINE_DEFAULT_DEPTH_INFO; \
VkPipelineRenderingCreateInfo pipeline##_default_rendering_info = PIPELINE_DEFAULT_RENDERING_INFO; \
VkPipelineColorBlendStateCreateInfo pipeline##_default_blend_info = PIPELINE_DEFAULT_BLEND_INFO; \
VkPipelineVertexInputStateCreateInfo pipeline##_default_vertex_info = PIPELINE_DEFAULT_VERTEX_INFO; \
VkPipelineViewportStateCreateInfo pipeline##_default_viewport_info = PIPELINE_DEFAULT_VIEWPORT_INFO; \
VkGraphicsPipelineCreateInfo pipeline##_create_info = { \
	.sType = STYPE(GRAPHICS_PIPELINE_CREATE_INFO),				\
	.pNext = &pipeline##_default_rendering_info, 									\
	.pVertexInputState = &pipeline##_default_vertex_info,					\
	.pInputAssemblyState = &pipeline##_default_assembly_info,			\
	.pViewportState = &pipeline##_default_viewport_info, 					\
	.pRasterizationState = &pipeline##_default_rasterization_info,\
	.pMultisampleState = &pipeline##_default_multisample_info, 		\
	.pColorBlendState = &pipeline##_default_blend_info, 					\
	.pDepthStencilState = &pipeline##_default_depth_info,					\
	.pDynamicState = &pipeline##_default_dynamic_info,						\
};

INIT_PIPELINE_DEFAULTS(cube);

// GUI Pipeline
INIT_PIPELINE_DEFAULTS(gui);

VkVertexInputBindingDescription gui_input_bind_desc = {
	.binding = 0,
	.stride = sizeof(rUIVertex),
	.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE,
};

VkVertexInputAttributeDescription gui_input_descriptions[] = {
	{ .binding = 0, .location = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = 0 },
	{ .binding = 0, .location = 1, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(rUIVertex, p1) },
	{ .binding = 0, .location = 2, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offset = offsetof(rUIVertex, col) },
	{ .binding = 0, .location = 3, .format = VK_FORMAT_R32_UINT, .offset = offsetof(rUIVertex, tex_idx) },
};

VkPipelineVertexInputStateCreateInfo gui_vertex_input_info = {
	.sType = STYPE(PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO),
	.vertexBindingDescriptionCount = 1,
	.pVertexBindingDescriptions = &gui_input_bind_desc,
	.vertexAttributeDescriptionCount = Len(gui_input_descriptions),
	.pVertexAttributeDescriptions = gui_input_descriptions,
};

VkPipelineInputAssemblyStateCreateInfo gui_assembly_info = {
	.sType = STYPE(PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO),
	.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
	.primitiveRestartEnable = VK_FALSE,
};

static void vCustomizePipelines()
{
	gui_create_info.pVertexInputState = &gui_vertex_input_info;
	gui_create_info.pInputAssemblyState = &gui_assembly_info;
}
