#include "math3d.h"
#include "filesystem.h"
#include "rendering.h"
#include "tier0/lib.h"
#include "tier1/utlvector.h"
#include "tier0/platform.h"
#include "rendering.h"
#include "vk_helper.h"
#include "vulkan/vulkan_core.h"

#define STB_IMAGE_IMPLEMENTATION

#if defined(__APPLE__) && defined(__MACH__)
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE
// iOS
#define STBI_NO_THREAD_LOCALS
#else
// macOS
#endif
#else
// Other platforms
#endif
#include "stb_image.h"

#define VULKAN_RENDERING_IMPLEMENTATION
#include "vk_video.h"

VkSampler g_invalidTextureSampler;

vk_buffer_t g_cameraProperties;
CameraProjection *g_cameraDataMap;

mat4 g_cameraView;

IImage *g_meshDepth;
IImage *g_meshDepthMSAA;
IImage *g_meshColor;
IImage *g_meshColorMSAA;
class CVkGraphicsPipeline: public IGraphicsPipeline
{
public:
	vk_tripipeline_t m_pipeline;
};

class CVkComputePipeline: public IComputePipeline
{
public:
	vk_comppipeline_t m_pipeline;
};

class CVkRayTracingPipeline: public IRayTracingPipeline 
{
public:
};

VkFormat IRenderer_FormatToVk( EImageFormat format )
{
	switch (format)
	{
	case IMAGE_FORMAT_R8G8B8A8: return VK_FORMAT_R8G8B8A8_UNORM;
	case IMAGE_FORMAT_R16G16B16A16: return VK_FORMAT_R16G16B16A16_UNORM;
	case IMAGE_FORMAT_DEPTH: return VK_FORMAT_D32_SFLOAT;
	default: return VK_FORMAT_R8G8B8A8_UNORM;
	}
};

VkAttachmentLoadOp IRenderer_LoadOpVk( EAttachmentLoadMode mode )
{
	switch (mode)
	{
	case ATTACHMENT_LOAD_MODE_DONT_CARE: return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
	case ATTACHMENT_LOAD_MODE_CLEAR: return VK_ATTACHMENT_LOAD_OP_CLEAR;
	case ATTACHMENT_LOAD_MODE_LOAD: return VK_ATTACHMENT_LOAD_OP_LOAD;
	default: return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
	}
}

VkAttachmentStoreOp IRenderer_StoreOpVk( EAttachmentStoreMode mode )
{
	switch (mode)
	{
	case ATTACHMENT_STORE_MODE_DONT_CARE: return VK_ATTACHMENT_STORE_OP_DONT_CARE;
	case ATTACHMENT_STORE_MODE_STORE: return VK_ATTACHMENT_STORE_OP_STORE;
	default: return VK_ATTACHMENT_STORE_OP_DONT_CARE;
	}
}

void IVulkan::Init()
{
	char invalidTexture[1024] = {};
	for (int i = 0; i < 16; i++) {
		for (int j = 0; j < 16; j++) {
			int index = i * 16 + j;
			if ((i + j) % 2 == 0) {
				invalidTexture[index*4] = 255;
			}
		}
	}
	ITextureManager::LoadTexture(invalidTexture, 16, 16, 4);
	VkPhysicalDeviceProperties properties{};
	vkGetPhysicalDeviceProperties(g_vkPhysicalDevice, &properties);
	VkSamplerCreateInfo samplerInfo{};
	samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
	samplerInfo.magFilter = VK_FILTER_NEAREST;
	samplerInfo.minFilter = VK_FILTER_NEAREST;
	samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
	samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
	samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
	samplerInfo.anisotropyEnable = VK_FALSE;
	samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
	samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
	samplerInfo.unnormalizedCoordinates = VK_FALSE;
	samplerInfo.compareEnable = VK_FALSE;
	samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
	samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
	samplerInfo.mipLodBias = 0.0f;
	samplerInfo.minLod = 0.0f;
	samplerInfo.maxLod = 0.0f;
	vkCreateSampler(g_vkDevice, &samplerInfo, nullptr, &g_invalidTextureSampler);
	
	g_cameraProperties.Create(sizeof(CameraProjection), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
	g_cameraDataMap = (CameraProjection*)g_cameraProperties.Map(0, 64);

	g_meshDepth = IRenderer::CreateImage(IMAGE_FORMAT_DEPTH, IMAGE_USAGE_DEPTH_ATTACHMENT, 1280, 720, 1);
	g_meshDepthMSAA = IRenderer::CreateImage(IMAGE_FORMAT_DEPTH, IMAGE_USAGE_DEPTH_ATTACHMENT, 1280, 720, 4);
	g_meshColor = IRenderer::CreateImage(IMAGE_FORMAT_R8G8B8A8, IMAGE_USAGE_COLOR_ATTACHMENT, 1280, 720, 1);
	g_meshColorMSAA = IRenderer::CreateImage(IMAGE_FORMAT_R8G8B8A8, IMAGE_USAGE_COLOR_ATTACHMENT, 1280, 720, 4);
	glm_mat4_identity(g_cameraView);
};
void IVulkan::CreatePipelines()
{
	for (auto &step: g_StepPrepass)
		step.pPipeline->Init();
	for (auto &step: g_StepMeshRendering)
		step.pPipeline->Init();
	for (auto &step: g_StepShading)
		step.pPipeline->Init();
	for (auto &step: g_StepPostProcessing)
		step.pPipeline->Init();
	for (auto &step: g_StepUI)
		step.pPipeline->Init();
}

void IVulkan::Frame()
{
	mat4 perspective;
	glm_mat4_inv(g_cameraView, g_cameraDataMap->viewprojection);
	glm_perspective(glm_rad(90),(float)g_nWindowWidth/g_nWindowHeight, 0.01, 100, perspective);
	glm_rotate(perspective, glm_rad(90), (vec4){1,0,0,0});
	glm_scale(perspective, (vec4){1,-1,1,1});
	glm_rotate(perspective, glm_rad(90), (vec4){0,0,1,0});
	glm_mat4_mul(perspective,g_cameraDataMap->viewprojection,g_cameraDataMap->viewprojection);

	if (g_bConfigNotify)
	{
		IRenderer::DestroyImage(g_meshDepth);
		IRenderer::DestroyImage(g_meshDepthMSAA);
		IRenderer::DestroyImage(g_meshColor);
		IRenderer::DestroyImage(g_meshColorMSAA);
		g_meshDepth = IRenderer::CreateImage(IMAGE_FORMAT_DEPTH, IMAGE_USAGE_DEPTH_ATTACHMENT, g_nWindowWidth, g_nWindowHeight, 1);
		g_meshDepthMSAA = IRenderer::CreateImage(IMAGE_FORMAT_DEPTH, IMAGE_USAGE_DEPTH_ATTACHMENT, g_nWindowWidth, g_nWindowHeight, 4);
		g_meshColor = IRenderer::CreateImage(IMAGE_FORMAT_R8G8B8A8, IMAGE_USAGE_COLOR_ATTACHMENT, g_nWindowWidth, g_nWindowHeight, 1);
		g_meshColorMSAA = IRenderer::CreateImage(IMAGE_FORMAT_R8G8B8A8, IMAGE_USAGE_COLOR_ATTACHMENT, g_nWindowWidth, g_nWindowHeight, 4);
	}

	for (auto &step: g_StepPrepass)
		step.pPipeline->Frame(0);
	IRenderer::Begin(g_nWindowWidth, g_nWindowHeight,
			{
			{
				g_meshColor,
				g_meshColorMSAA,
				ATTACHMENT_LOAD_MODE_DONT_CARE,
				ATTACHMENT_STORE_MODE_STORE,
			}
			},
			{
				g_meshDepth,
				g_meshDepthMSAA,
				ATTACHMENT_LOAD_MODE_DONT_CARE,
				ATTACHMENT_STORE_MODE_STORE,
			});
	for (auto &step: g_StepMeshRendering)
		step.pPipeline->Frame(0);
	IRenderer::End();
	for (auto &step: g_StepShading)
		step.pPipeline->Frame(0);
	for (auto &step: g_StepPostProcessing)
		step.pPipeline->Frame(0);
	for (auto &step: g_StepUI)
		step.pPipeline->Frame(0);
};

void vk_shader_t::Create( const char *szPath, VkShaderStageFlagBits shaderStage )
{
	FileHandle_t shader = IFileSystem::Open(szPath, IFILE_READ);
	if (!shader)
		Plat_FatalErrorFunc("Failed to open shader %s\n", szPath);
	CUtlBuffer<uint8_t> buffer(IFileSystem::Size(shader));
	IFileSystem::Read(shader, buffer.GetMemory(), buffer.GetSize());
	Create(buffer, shaderStage);
	IFileSystem::Close(shader);
}

void vk_shader_t::Create( CUtlBuffer<uint8_t> &spirv, VkShaderStageFlagBits shaderStage )
{
	if (m_shaderModule != NULL)
		Plat_FatalErrorFunc("Shader already exists");
	VkShaderModuleCreateInfo createInfo = {};
	createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
	createInfo.codeSize = spirv.GetSize();
	createInfo.pCode = (uint32_t*)spirv.GetMemory();
	vkCreateShaderModule(g_vkDevice, &createInfo, NULL, &m_shaderModule);
	m_stageCreateInfo = {};
	m_stageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
	m_stageCreateInfo.module = m_shaderModule;
	m_stageCreateInfo.stage = shaderStage;
	m_stageCreateInfo.pName = "main";
}

void vk_shader_t::Destroy( void )
{
	if (m_shaderModule == NULL)
		Plat_FatalErrorFunc("Shader doesn't exist");
	vkDestroyShaderModule(g_vkDevice, m_shaderModule, NULL);
}


void vk_tripipeline_t::Create(
		CUtlVector<vk_shader_t> &shaders,
		CUtlVector<VkDescriptorSetLayoutBinding> &bindings,
		uint32_t pushConstantSize,
		CUtlVector<VkFormat> formats
		)
{
	VkPushConstantRange pushConstantRange = {};
	pushConstantRange.stageFlags = VK_SHADER_STAGE_ALL;
	pushConstantRange.offset = 0;
	pushConstantRange.size = pushConstantSize;

	VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {};
	descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
	descriptorSetLayoutCreateInfo.bindingCount = bindings.GetSize();
	descriptorSetLayoutCreateInfo.pBindings = bindings.GetData();
	vkCreateDescriptorSetLayout(g_vkDevice, &descriptorSetLayoutCreateInfo, NULL, &m_descriptorSetLayout);


	VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
	pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
	pipelineLayoutCreateInfo.setLayoutCount = 1;
	pipelineLayoutCreateInfo.pSetLayouts = &m_descriptorSetLayout;
	if (pushConstantSize != 0)
	{
		pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
		pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
	}
	vkCreatePipelineLayout(g_vkDevice, &pipelineLayoutCreateInfo, NULL, &m_layout);

	VkDynamicState dynamicStates[] = {
		/* pVertexInputState */
		VK_DYNAMIC_STATE_VERTEX_INPUT_EXT,
		/* pInputAssemblyState */
		VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE,
		VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY,
		/* pViewportState */
		VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT,
		VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT,
		/* pRasterizationState */
		VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
		VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE,
		VK_DYNAMIC_STATE_POLYGON_MODE_EXT,
		VK_DYNAMIC_STATE_CULL_MODE,
		VK_DYNAMIC_STATE_FRONT_FACE,
		VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE,
		VK_DYNAMIC_STATE_DEPTH_BIAS,
		VK_DYNAMIC_STATE_LINE_WIDTH,
		/* pMultisampleState */
		VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT,
		VK_DYNAMIC_STATE_SAMPLE_MASK_EXT, 
		VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT,
		/* pDepthStencilState */
		VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE,
		VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE,
		VK_DYNAMIC_STATE_DEPTH_COMPARE_OP,
		VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE,
		VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE,
		VK_DYNAMIC_STATE_STENCIL_OP,
		VK_DYNAMIC_STATE_DEPTH_BOUNDS,
		/* pColorBlendState */
		VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
		VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
		VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
		VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
		VK_DYNAMIC_STATE_BLEND_CONSTANTS,
	};

	VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo = {
		.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
		.dynamicStateCount = sizeof(dynamicStates)/sizeof(VkDynamicState),
		.pDynamicStates = dynamicStates,
	};

	VkPipelineInputAssemblyStateCreateInfo piasci = {
		.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
		.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
		.primitiveRestartEnable = VK_TRUE,
	};


	VkPipelineRenderingCreateInfo prci = {
		.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
		.pNext = NULL,
		.colorAttachmentCount = (uint32_t)formats.GetSize(),
		.pColorAttachmentFormats = formats.GetData(),
		.depthAttachmentFormat = VK_FORMAT_D32_SFLOAT,
	};

	VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo = {};
	graphicsPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
	graphicsPipelineCreateInfo.pInputAssemblyState = &piasci;
	graphicsPipelineCreateInfo.layout = m_layout;
	graphicsPipelineCreateInfo.pDynamicState = &pipelineDynamicStateCreateInfo;
	graphicsPipelineCreateInfo.stageCount = shaders.GetSize();
	CUtlVector<VkPipelineShaderStageCreateInfo> stages(graphicsPipelineCreateInfo.stageCount);
	uint32_t i = 0;
	for (auto &shader: shaders)
	{
		stages[i] = shader.m_stageCreateInfo;
		i++;
	}
	graphicsPipelineCreateInfo.pStages = stages.GetData();
	graphicsPipelineCreateInfo.pNext = &prci;
	vkCreateGraphicsPipelines(g_vkDevice, NULL, 1, &graphicsPipelineCreateInfo, NULL, &m_pipeline);
}
void vk_tripipeline_t::Destroy()
{

}
void vk_comppipeline_t::Create(
	vk_shader_t &shader,
	CUtlVector<VkDescriptorSetLayoutBinding> &bindings,
	uint32_t pushConstantsSize
)
{
	VkPushConstantRange pushConstantRange = {};
	pushConstantRange.stageFlags = VK_SHADER_STAGE_ALL;
	pushConstantRange.offset = 0;
	pushConstantRange.size = pushConstantsSize;

	VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {};
	descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
	descriptorSetLayoutCreateInfo.bindingCount = bindings.GetSize();
	descriptorSetLayoutCreateInfo.pBindings = bindings.GetData();
	vkCreateDescriptorSetLayout(g_vkDevice, &descriptorSetLayoutCreateInfo, NULL, &m_descriptorSetLayout);


	VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
	pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
	pipelineLayoutCreateInfo.setLayoutCount = 1;
	pipelineLayoutCreateInfo.pSetLayouts = &m_descriptorSetLayout;
	if (pushConstantsSize != 0)
	{
		pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
		pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
	}
	vkCreatePipelineLayout(g_vkDevice, &pipelineLayoutCreateInfo, NULL, &m_layout);

	VkComputePipelineCreateInfo cpci = {};
	cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
	cpci.stage = shader.m_stageCreateInfo;
	cpci.layout = m_layout;
	vkCreateComputePipelines(g_vkDevice, NULL, 1, &cpci, NULL, &m_pipeline);
}

void vk_comppipeline_t::Destroy()
{

}

void vk_buffer_t::Create(size_t size, VkBufferUsageFlags usage)
{
	VkBufferCreateInfo bufferCreateInfo = {};
	bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
	bufferCreateInfo.usage = usage;
	bufferCreateInfo.size = size;


	VmaAllocationCreateInfo allocInfo = {};
	allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
	allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | 
		VMA_ALLOCATION_CREATE_MAPPED_BIT;
	m_nSize = size;
	vmaCreateBuffer(g_allocator, &bufferCreateInfo, &allocInfo, &m_buffer, &m_memory, NULL);
}

void vk_buffer_t::Destroy()
{

}

void *vk_buffer_t::Map(size_t offset, size_t size)
{
	void *pData;
	vmaMapMemory(g_allocator, m_memory, &pData);
	return pData;
}

void vk_buffer_t::Unmap()
{
	vmaUnmapMemory(g_allocator, m_memory);
}


void vk_buffer_t::CopyTo(struct vk_image2d_t *image)
{

}

void vk_buffer_t::CopyTo(struct vk_buffer_t *buffer)
{

}
void vk_image2d_t::Create(size_t x, size_t y, VkFormat format, VkImageUsageFlags usage, VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT)
{
	VkImageCreateInfo imageInfo={};
	imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
	imageInfo.imageType = VK_IMAGE_TYPE_2D;
	imageInfo.extent.width = x > 1 ? x : 1;
	imageInfo.extent.height = y > 1 ? y : 1;
	imageInfo.extent.depth = 1;
	imageInfo.mipLevels = 1;
	imageInfo.arrayLayers = 1;
	imageInfo.usage = usage;
	imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
	imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	imageInfo.format=format;	
	imageInfo.samples = samples;

	VmaAllocationCreateInfo alloc = {};
	alloc.usage=VMA_MEMORY_USAGE_AUTO;
	vmaCreateImage(g_allocator, &imageInfo,&alloc, &m_image, &m_memory,0);

	m_X=imageInfo.extent.width;
	m_Y=imageInfo.extent.width;
	m_format=format;

	VkImageView imageView = 0;
	VkImageViewCreateInfo imageViewCreateInfo={};
	imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
	imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
	imageViewCreateInfo.format = format;
	imageViewCreateInfo.image = m_image;
	imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
	imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
	imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
	imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
	imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
	if (format == VK_FORMAT_D32_SFLOAT) {
		imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
	}
	imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
	imageViewCreateInfo.subresourceRange.levelCount = 1;
	imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
	imageViewCreateInfo.subresourceRange.layerCount = 1;
	vkCreateImageView(g_vkDevice, &imageViewCreateInfo, 0, &m_imageView);
}
void vk_image2d_t::Destroy()
{
	vkDestroyImageView(g_vkDevice, m_imageView, NULL);
	vmaDestroyImage(g_allocator, m_image, NULL);
}

void CopyTo(struct vk_image2d_t *image);
void CopyTo(struct vk_buffer_t *buffer);

void *CVkBuffer::Map()
{
	if (!m_pAllocated)
		m_pAllocated = m_buffer.Map(0, m_buffer.m_nSize);
	return m_pAllocated;
};
void CVkBuffer::Unmap()
{
	if (!m_pAllocated)
		return;	
	m_buffer.Unmap();
	m_pAllocated = 0;
};

CUtlVector<ITexture*> g_textures;
CUtlVector<ITexture*> g_newtextures;

ITexture *ITextureManager::LoadTexture( void *pData, uint32_t X, uint32_t Y, uint32_t numChannels )
{
	CVkTexture *pTexture = new CVkTexture;
	*pTexture = {};
	VkDeviceSize imageSize = X*Y*4;
	vk_buffer_t gpu_buffer = {};
	gpu_buffer.Create(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
	void *gpu_buffer_map = gpu_buffer.Map(0, 0);
	V_memcpy(gpu_buffer_map, pData, imageSize);
	gpu_buffer.Unmap();
	pTexture->image = {};
	pTexture->image.Create(X, Y, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);

	VkCommandPool commandPool;
	VkCommandBuffer commandBuffer;

	VkCommandPoolCreateInfo poolInfo{};
	poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
	poolInfo.queueFamilyIndex = g_drawfamily;
	poolInfo.flags = 0;
	vkCreateCommandPool(g_vkDevice, &poolInfo, NULL, &commandPool);

	VkCommandBufferAllocateInfo cmdAllocInfo{};
	cmdAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
	cmdAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
	cmdAllocInfo.commandPool = commandPool;
	cmdAllocInfo.commandBufferCount = 1;
	vkAllocateCommandBuffers(g_vkDevice, &cmdAllocInfo, &commandBuffer);


	VkCommandBufferBeginInfo beginInfo{};
	beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
	beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
	vkBeginCommandBuffer(commandBuffer, &beginInfo);

	VkImageMemoryBarrier barrier{};
	barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
	barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
	barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
	barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
	barrier.image = pTexture->image.m_image;
	barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
	barrier.subresourceRange.baseMipLevel = 0;
	barrier.subresourceRange.levelCount = 1;
	barrier.subresourceRange.baseArrayLayer = 0;
	barrier.subresourceRange.layerCount = 1;
	barrier.srcAccessMask = 0;
	barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

	vkCmdPipelineBarrier(
	    commandBuffer,
	    VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
	    0,
	    0, NULL,
	    0, NULL,
	    1, &barrier
	);

	VkBufferImageCopy region{};
	region.bufferOffset = 0;
	region.bufferRowLength = 0;
	region.bufferImageHeight = 0;
	region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
	region.imageSubresource.mipLevel = 0;
	region.imageSubresource.baseArrayLayer = 0;
	region.imageSubresource.layerCount = 1;
	region.imageOffset = {0, 0, 0};
	region.imageExtent = {(uint32_t)X, (uint32_t)Y, 1};
	vkCmdCopyBufferToImage(
			commandBuffer,
			gpu_buffer.m_buffer,
			pTexture->image.m_image,
			VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
			1,
			&region
			);


	barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
	barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
	barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
	barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

	vkCmdPipelineBarrier(
		commandBuffer,
		VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
		0,
		0, NULL,
		0, NULL,
		1, &barrier
		);

	vkEndCommandBuffer(commandBuffer);

	VkSubmitInfo submitInfo{};
	submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
	submitInfo.commandBufferCount = 1;
	submitInfo.pCommandBuffers = &commandBuffer;

	vkQueueSubmit(g_drawQueue, 1, &submitInfo, VK_NULL_HANDLE);
	vkQueueWaitIdle(g_drawQueue);
	vkFreeCommandBuffers(g_vkDevice, commandPool, 1, &commandBuffer);
	vkDestroyCommandPool(g_vkDevice, commandPool, NULL);

	g_newtextures.AppendTail(pTexture);
	g_textures.AppendTail(pTexture);
	return pTexture;
};

ITexture *ITextureManager::LoadTexture( const char *szName )
{
	for (ITexture *texture: g_textures)
	{
		if (!texture->szName)
			continue;
		if (!V_strcmp(texture->szName, szName))
			return texture;
	};
	FileHandle_t file = IFileSystem::Open(szName, IFILE_READ);
	if (!file)
		Plat_FatalErrorFunc("Failed to load %s\n", szName);
	V_printf("cool %s\n",szName);

	CUtlBuffer<stbi_uc> buffer(IFileSystem::Size(file));
	IFileSystem::Read(file, buffer.GetMemory(), buffer.GetSize());
	int nImageX;
	int nImageY;
	int nImageChannels;
	stbi_uc *pixels = stbi_load_from_memory((stbi_uc*)buffer.GetMemory(), buffer.GetSize(), &nImageX, &nImageY, &nImageChannels, STBI_rgb_alpha);
	ITexture *pTexture = ITextureManager::LoadTexture(pixels, nImageX, nImageY, 4);
	pTexture->szName = szName;
	return pTexture;
};


IStorageBuffer *IRenderer::CreateStorageBuffer( uint32_t uSize )
{
	CVkBuffer *pBuffer = new CVkBuffer();
	pBuffer->m_buffer.Create(uSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
	return pBuffer;
}

IUniformBuffer *IRenderer::CreateUniformBuffer( uint32_t uSize )
{
	CVkBuffer *pBuffer = new CVkBuffer();
	pBuffer->m_buffer.Create(uSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
	return pBuffer;
}


IVertexBuffer *IRenderer::CreateVertexBuffer( uint32_t uSize )
{
	CVkBuffer *pBuffer = new CVkBuffer();
	pBuffer->m_buffer.Create(uSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
	return pBuffer;
}

IIndexBuffer *IRenderer::CreateIndexBuffer( uint32_t uSize )
{
	CVkBuffer *pBuffer = new CVkBuffer();
	pBuffer->m_buffer.Create(uSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
	return pBuffer;
}


IImage *IRenderer::CreateImage( EImageFormat format, uint32_t usage, uint32_t nWidth, uint32_t nHeight, uint32_t nSamples )
{
	VkFormat vkformat;
	VkImageUsageFlags vkusage;
	CVkImage *pImage = new CVkImage();
	VkSampleCountFlagBits samples;
	switch (format)
	{
		case IMAGE_FORMAT_R8G8B8A8: vkformat = VK_FORMAT_R8G8B8A8_UNORM; break;
		case IMAGE_FORMAT_R16G16B16A16: vkformat = VK_FORMAT_R16G16B16A16_UNORM; break;
		case IMAGE_FORMAT_DEPTH: vkformat = VK_FORMAT_D32_SFLOAT; break;
		default: return 0;
	};

	if (usage&IMAGE_USAGE_COLOR_ATTACHMENT) vkusage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
	if (usage&IMAGE_USAGE_DEPTH_ATTACHMENT) vkusage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
	if (usage&IMAGE_USAGE_STORAGE) vkusage |= VK_IMAGE_USAGE_STORAGE_BIT;

	switch (nSamples)
	{
		case 1: samples = VK_SAMPLE_COUNT_1_BIT; break;
		case 4: samples = VK_SAMPLE_COUNT_4_BIT; break;
		default: samples = VK_SAMPLE_COUNT_1_BIT; break;
	}
	pImage->m_image.Create(nWidth, nHeight, vkformat, vkusage, samples);
	return pImage;
};
void IRenderer::DestroyBuffer( IBuffer *pBuffer )
{
	CVkBuffer *pVkBuffer = (CVkBuffer*)pBuffer;
	if (pVkBuffer)
		pVkBuffer->m_buffer.Destroy();
}

void IRenderer::DestroyImage( IImage *pImage )
{
	CVkImage *pVkImage = (CVkImage*)pImage;
	if (pVkImage)
		pVkImage->m_image.Destroy();
}



void IRenderer::SetConstants( uint32_t nSize, uint32_t nOffset, void *pData )
{

}

void IRenderer::Barrier( EBarrierStage stageIn, uint32_t stageOut, CUtlVector<BufferBarrier_t> buffers, CUtlVector<ImageBarrier_t> images )
{

}

void IRenderer::BindData( uint32_t binding, IBuffer *pBuffer, IImage* pImage)
{

}



void IRenderer::BindPipeline( IPipeline *pPipeline )
{
	if (!pPipeline)
		return;
	if (pPipeline->type == PIPELINE_TYPE_RASTERIZATION)
	{
		CVkGraphicsPipeline *pVkPipeline = (CVkGraphicsPipeline*)pPipeline;
		vkCmdBindPipeline(g_vkCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pVkPipeline->m_pipeline.m_pipeline);
	}
}


void IRenderer::Begin( uint32_t nWidth, uint32_t nHeight, CUtlVector<RenderingColorAttachment_t> attachments, RenderingDepthAttachment_t depth )
{
	CUtlVector<VkRenderingAttachmentInfo> colorAttachments = {};
	VkRenderingAttachmentInfo depthAttachment = {};
	for (auto &attachment: attachments)
	{
		VkRenderingAttachmentInfo vkattachment = {};
		vkattachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
		CVkImage *pImage = (CVkImage*)attachment.pTemporary;
		CVkImage *pTemporary = (CVkImage*)attachment.pTemporary;
		if (attachment.pTemporary)
		{
			vkattachment.imageView = pTemporary->m_image.m_imageView;
			vkattachment.resolveImageView = pImage->m_image.m_imageView;
			vkattachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
			vkattachment.resolveImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
		} else 
		{
			vkattachment.imageView = pImage->m_image.m_imageView;
			vkattachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
		}
		vkattachment.loadOp = IRenderer_LoadOpVk(attachment.loadMode);
		vkattachment.storeOp = IRenderer_StoreOpVk(attachment.storeMode);
		colorAttachments.AppendTail(vkattachment);
	}
	depthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
	CVkImage *pDepthImage = (CVkImage*)depth.pTemporary;
	CVkImage *pDepthTemporary = (CVkImage*)depth.pTemporary;
	if (depth.pTemporary)
	{
		depthAttachment.imageView = pDepthTemporary->m_image.m_imageView;
		depthAttachment.resolveImageView = pDepthImage->m_image.m_imageView;
		depthAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
		depthAttachment.resolveImageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
	} else 
	{
		depthAttachment.imageView = pDepthImage->m_image.m_imageView;
		depthAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
	}
	depthAttachment.loadOp = IRenderer_LoadOpVk(depth.loadMode);
	depthAttachment.storeOp = IRenderer_StoreOpVk(depth.storeMode);

	VkRenderingInfo renderInfo = {
		.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
		.renderArea = {{0, 0}, {nWidth, nHeight}},
		.layerCount = 1,
		.colorAttachmentCount = (uint32_t)colorAttachments.GetSize(),
		.pColorAttachments = colorAttachments.GetData(),
		.pDepthAttachment = &depthAttachment,
	};
	vkCmdBeginRendering(g_vkCommandBuffer, &renderInfo);
}

void IRenderer::ResetState()
{

}

void IRenderer::SetDepthMode( EDepthMode mode )
{

}

void IRenderer::Draw( IVertexBuffer *pVertex, IIndexBuffer *pIndex )
{

}

void IRenderer::End()
{
	vkCmdEndRendering(g_vkCommandBuffer);
}



IGraphicsPipeline *IRenderer::CreateGraphicsPipeline(
	CUtlVector<Shader_t> shaders,
	CUtlVector<ShaderInput_t> inputs,
	uint32_t nConstantsSize,
	CUtlVector<EImageFormat> outputFormats
)
{
	CVkGraphicsPipeline *pipeline = new CVkGraphicsPipeline;
	pipeline->type = PIPELINE_TYPE_RASTERIZATION;
	CUtlVector<vk_shader_t> vkshaders(shaders.GetSize());
	CUtlVector<VkDescriptorSetLayoutBinding> vkbindings(inputs.GetSize());
	CUtlVector<VkFormat> vkformats(outputFormats.GetSize());

	for ( uint32_t i = 0; i < vkshaders.GetSize(); i++ )
	{
		VkShaderStageFlagBits flags;
		if (shaders[i].type == SHADER_TYPE_VERTEX) flags = VK_SHADER_STAGE_VERTEX_BIT;
		if (shaders[i].type == SHADER_TYPE_FRAGMENT) flags = VK_SHADER_STAGE_FRAGMENT_BIT;
		vkshaders[i].Create(shaders[i].szPath, flags);
	}
	for ( uint32_t i = 0; i < vkbindings.GetSize(); i++ )
	{
		vkbindings[i].binding = inputs[i].binding;
		vkbindings[i].descriptorCount = 1;
		if (inputs[i].type == SHADER_INPUT_TYPE_IMAGE) vkbindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
		if (inputs[i].type == SHADER_INPUT_TYPE_UNIFORM_BUFFER) vkbindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
		if (inputs[i].type == SHADER_INPUT_TYPE_STORAGE_BUFFER) vkbindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
		if (inputs[i].type == SHADER_INPUT_TYPE_TLAS) vkbindings[i].descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
		if (inputs[i].type == SHADER_INPUT_TYPE_TEXTURES)
		{
			vkbindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
			vkbindings[i].descriptorCount = 1024;
		}
	}
	for ( uint32_t i = 0; i < vkformats.GetSize(); i++ )
	{
		vkformats[i] = IRenderer_FormatToVk(outputFormats[i]);
	}
	pipeline->m_pipeline.Create(vkshaders, vkbindings, nConstantsSize, vkformats);
	return 0;
};


CUtlVector<RenderingStep_t> g_StepPrepass;
CUtlVector<RenderingStep_t> g_StepMeshRendering;
CUtlVector<RenderingStep_t> g_StepShading;
CUtlVector<RenderingStep_t> g_StepPostProcessing;
CUtlVector<RenderingStep_t> g_StepUI;

CRenderingStep::CRenderingStep()
{

}

CRenderingStep::CRenderingStep(const char *szStepName, CreateRenderStepFn pfn)
{

}
	
CPrepassRenderingStep::CPrepassRenderingStep(const char *szStepName, CreateRenderStepFn pfn)
{
	g_StepPrepass.AppendTail({pfn(), szStepName});
}

CMeshRenderingStep::CMeshRenderingStep(const char *szStepName, CreateRenderStepFn pfn)
{
	g_StepMeshRendering.AppendTail({pfn(), szStepName});
}

CShadingRenderingStep::CShadingRenderingStep(const char *szStepName, CreateRenderStepFn pfn)
{
	g_StepShading.AppendTail({pfn(), szStepName});
}

CPostProcessingRenderingStep::CPostProcessingRenderingStep(const char *szStepName, CreateRenderStepFn pfn)
{
	g_StepPostProcessing.AppendTail({pfn(), szStepName});
}

CUIRenderingStep::CUIRenderingStep(const char *szStepName, CreateRenderStepFn pfn)
{
	g_StepUI.AppendTail({pfn(), szStepName});
}

