 #include "vulkanapi.h"
#include "wrapper/queue.h"
#include "wrapper/buffer.h"
#include "wrapper/commandbuffer.h"
#include "thread/worker.h"
#include "object/camera/camera.h"
#include "render/node/rendernode.h"
#include "pass/forwardpass.h"
#include "pass/target.h"
#include "wrapper/swapchain.h"
#include "render/asset/mesh.h"
#include "wrapper/descriptorpool.h"
#include "window.h"
#include "zlog.h"
namespace vulkanapi {
	RenderVulkanAPI::RenderVulkanAPI()
		:backend("vulkan")
		,context(this)
	{
		Buffer::MakeVmaAllocator(backend);
	}
	RenderVulkanAPI::~RenderVulkanAPI()
	{
		zlog::info("~RenderVulkanAPI");
	}
	void RenderVulkanAPI::SwitchContext()
	{

	}
	void RenderVulkanAPI::InitRenderPass()
	{
		swapchain = VulkanWindow::GetSingletonPtr()->GetSwapchain();
		RenderTarget* output = RenderTarget::MakeOutputTarget();
		TargetList.reserve(RENDER_COUNT);
		TargetList.push_back(output);
		PassList.resize(RENDER_COUNT);
		auto gbuffer = GeometryBuffer(backend.GetDevice(), 3, 640, 720);
		for (auto node : mAllNodes) {
			if (!node)continue;
			auto type = node->GetType();
			if (type == RENDER_FORWARD_RENDERING) {
				PassList[type] = ForwardPass::MakePass(backend.GetDevice(), gbuffer, output);
			}
		}
	}
	void RenderVulkanAPI::SetViewPort(uint32_t width, uint32_t height, uint32_t xOffset, uint32_t yOffset)
	{
		mViewPortInfo.width = width;
		mViewPortInfo.height = height;
		mViewPortInfo.xOffset = xOffset;
		mViewPortInfo.yOffset = yOffset;
	}
	void RenderVulkanAPI::BeginFrame()
	{
		swapchain->Aquire(context);
	}
	void RenderVulkanAPI::EndFrame()
	{
		swapchain->Present(context);
	}
	void RenderVulkanAPI::SetStaticMesh(Mesh* mesh)
	{
		auto Indices = mesh->GetIndices();
		auto Vertices = mesh->GetVertices();
		auto meshBuffer = GetNextVAO(mesh->GetVAO());
		meshBuffer->indexCount = Indices.size();
		meshBuffer->vertexCount = Vertices.size();
		
		// ----------------------------------------------- Vertex Buffer -----------------------------------------------
		VkDeviceSize vertexBufferSize = Vertices.data_size();
		VmaAllocationCreateInfo vertexVmaStagingInfo = {};
		VmaAllocation vertexVmaStagingAlloc;
		VkBuffer vertexStagingBuffer = Buffer::CreateStageBuffer(vertexVmaStagingInfo, vertexVmaStagingAlloc, vertexBufferSize);

		// 拷贝数据到StagingBuffer
		Buffer::CopyData(vertexVmaStagingAlloc, Vertices.data(), vertexBufferSize);

		VkBufferUsageFlags vertexFlags = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;//加入光线追踪
		VmaAllocationCreateInfo vertexVmaCreateInfo = {};
		VkBufferCreateInfo vertexCreateInfo = Buffer::MakeDeviceInfo(vertexVmaCreateInfo, vertexBufferSize, vertexFlags);
		meshBuffer->vertexBuffer = Buffer::CreateBuffer(vertexCreateInfo, vertexVmaCreateInfo, meshBuffer->vertexBufferAlloc);
		
		// ----------------------------------------------- Index Buffer -----------------------------------------------
		VkDeviceSize indexBufferSize = sizeof(uint32_t) * Indices.size();
		VmaAllocationCreateInfo indexVmaStagingInfo = {};
		VmaAllocation indexVmaStagingAlloc;
		VkBuffer indexStagingBuffer = Buffer::CreateStageBuffer(indexVmaStagingInfo, indexVmaStagingAlloc, vertexBufferSize);
		// 拷贝数据到StagingBuffer
		Buffer::CopyData(indexVmaStagingAlloc, Indices.data(), indexBufferSize);

		VmaAllocationCreateInfo indexVmaCreateInfo = {};
		VkBufferCreateInfo indexCreateInfo = Buffer::MakeDeviceInfo(indexVmaCreateInfo, indexBufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
		meshBuffer->indexBuffer = Buffer::CreateBuffer(indexCreateInfo, indexVmaCreateInfo, meshBuffer->vertexBufferAlloc);

		auto fn = [=](CommandBuffer& cmd) {
			cmd.CmdCopyBuffer(vertexStagingBuffer, meshBuffer->vertexBuffer, vertexBufferSize);
			cmd.CmdCopyBuffer(indexStagingBuffer, meshBuffer->indexBuffer, indexBufferSize);
		};
		auto callback = [=]() {
			// 销毁StagingBuffer
			Buffer::DestroyBuffer(vertexStagingBuffer, vertexVmaStagingAlloc);
			Buffer::DestroyBuffer(indexStagingBuffer, indexVmaStagingAlloc);
			mesh->EndLoad();
		};
		if (mesh->IsAsync()) {
			Backend::TransferWorker->InvokeBuffer(fn, callback);
		}
		else {
			Backend::TransferWorker->ImmediatelyExecute(fn, callback);
		}
	}
	void RenderVulkanAPI::DrawStaticMesh(Mesh* mesh)
	{
		auto vulkanVAO = VAOList[mesh->GetVAO()];
		auto pipeline = PiPelineList[0];
		uint32_t frame = context.frame;
		context.Pass->SetViewPort(mViewPortInfo.width, mViewPortInfo.height, mViewPortInfo.xOffset, mViewPortInfo.yOffset);
		Backend::RenderWorker->Draw([=](CommandBuffer& cmd) {
			VkCommandBuffer ptr = cmd.Ptr();
			context.Pass->BeginPass(cmd, frame);
			VkBuffer vertexBuffers[] = { vulkanVAO->vertexBuffer };
			VkDeviceSize offsets[] = { 0 };
			VkViewport viewport = {};
			viewport.x = static_cast<float>(mViewPortInfo.xOffset);
			viewport.y = static_cast<float>(mViewPortInfo.yOffset);
			viewport.width = static_cast<float>(mViewPortInfo.width);
			viewport.height = static_cast<float>(mViewPortInfo.height);
			viewport.minDepth = 0.0f;
			viewport.maxDepth = 1.0f;
			vkCmdSetViewport(ptr, 0, 1, &viewport);

			VkRect2D scissor = {};
			scissor.offset = { mViewPortInfo.xOffset, mViewPortInfo.yOffset };
			scissor.extent = { mViewPortInfo.width, mViewPortInfo.height };
			vkCmdSetScissor(ptr, 0, 1, &scissor);

			vkCmdBindVertexBuffers(ptr, 0, 1, vertexBuffers, offsets);
			vkCmdBindIndexBuffer(ptr, vulkanVAO->indexBuffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdBindPipeline(ptr, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline);
			vkCmdBindDescriptorSets(ptr, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipelineLayout, 0, 1, &pipeline->descriptorSet, 0, VK_NULL_HANDLE);
			vkCmdDrawIndexed(ptr, 3, 1, 0, 0, 0); 
			context.Pass->EndPass(cmd);
		});
		Backend::RenderWorker->Flush();
	}
	void RenderVulkanAPI::LoadShader(Shader* shader)
	{
		vector<VkPipelineShaderStageCreateInfo> shaderStages;
		map<VkShaderStageFlagBits, VkShaderModule> shaderModules;
		auto device = backend.GetDevice();
		auto vertData = shader->GetVertData();
		auto vertModule = device.CreateShaderModule(vertData);
		shaderModules.insert(make_pair(VK_SHADER_STAGE_VERTEX_BIT, vertModule));
		auto fragData = shader->GetFragData();
		auto fragModule = device.CreateShaderModule(fragData);
		shaderModules.insert(make_pair(VK_SHADER_STAGE_FRAGMENT_BIT, fragModule));
		for (auto& shaderModule : shaderModules)
		{
			VkPipelineShaderStageCreateInfo shaderStageInfo = {};
			shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
			shaderStageInfo.stage = shaderModule.first;
			shaderStageInfo.module = shaderModule.second;
			shaderStageInfo.pName = "main";
			shaderStages.push_back(shaderStageInfo);
		}
		// 设置顶点输入格式
		VkVertexInputBindingDescription bindingDescription = {};
		bindingDescription.binding = 0;
		bindingDescription.stride = sizeof(Vertex);
		bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;

		array<VkVertexInputAttributeDescription, 6> attributeDescriptions = { };
		attributeDescriptions[0].binding = 0;
		attributeDescriptions[0].location = 0;
		attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[0].offset = offsetof(BoneVertex, Position);
		attributeDescriptions[1].binding = 0;
		attributeDescriptions[1].location = 1;
		attributeDescriptions[1].format = VK_FORMAT_R32G32_SFLOAT;
		attributeDescriptions[1].offset = offsetof(BoneVertex, TexCoords);
		attributeDescriptions[2].binding = 0;
		attributeDescriptions[2].location = 2;
		attributeDescriptions[2].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[2].offset = offsetof(BoneVertex, Normal);
		attributeDescriptions[3].binding = 0;
		attributeDescriptions[3].location = 3;
		attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[3].offset = offsetof(BoneVertex, Tangent);
		attributeDescriptions[4].binding = 0;
		attributeDescriptions[4].location = 4;
		attributeDescriptions[4].format = VK_FORMAT_R32G32B32A32_SFLOAT;
		attributeDescriptions[4].offset = offsetof(BoneVertex, Weights);
		attributeDescriptions[5].binding = 0;
		attributeDescriptions[5].location = 5;
		attributeDescriptions[5].format = VK_FORMAT_R32G32B32A32_UINT;
		attributeDescriptions[5].offset = offsetof(BoneVertex, BoneIDs);
		VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
		vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
		vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
		vertexInputInfo.vertexBindingDescriptionCount = 1;
		vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
		vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());

		// 设置图元
		VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo = {};
		inputAssemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
		inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
		inputAssemblyInfo.primitiveRestartEnable = VK_FALSE;

		// ViewPort信息，这里不直接设置，下面弄成动态的
		VkPipelineViewportStateCreateInfo viewportStateInfo = {};
		viewportStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
		viewportStateInfo.viewportCount = 1;
		viewportStateInfo.scissorCount = 1;

		// View Port和Scissor设置为动态，每帧绘制时决定
		vector<VkDynamicState> dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
		VkPipelineDynamicStateCreateInfo dynamicStateInfo = {};
		dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
		dynamicStateInfo.pDynamicStates = dynamicStates.data();
		dynamicStateInfo.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());

		// 设置光栅化阶段
		VkPipelineRasterizationStateCreateInfo rasterizationInfo = {};
		rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
		// 如果depthClampEnable设置为VK_TRUE，超过远近裁剪面的片元会进行收敛，而不是丢弃它们
		rasterizationInfo.depthClampEnable = VK_FALSE;
		// 如果rasterizerDiscardEnable设置为VK_TRUE，那么几何图元永远不会传递到光栅化阶段
		// 这是禁止任何数据输出到framebuffer的方法
		rasterizationInfo.rasterizerDiscardEnable = VK_FALSE;
		// 设置片元如何从几何模型中产生，如果不是FILL，需要开启GPU feature
		// VK_POLYGON_MODE_FILL: 多边形区域填充
		// VK_POLYGON_MODE_LINE: 多边形边缘线框绘制
		// VK_POLYGON_MODE_POINT : 多边形顶点作为描点绘制
		rasterizationInfo.polygonMode = VK_POLYGON_MODE_FILL;
		rasterizationInfo.lineWidth = 1.0f;
		rasterizationInfo.cullMode = VK_CULL_MODE_FRONT_BIT;
		rasterizationInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
		// 渲染阴影的偏移配置
		rasterizationInfo.depthBiasEnable = VK_FALSE;
		rasterizationInfo.depthBiasConstantFactor = 0.0f;
		rasterizationInfo.depthBiasClamp = 0.0f;
		rasterizationInfo.depthBiasSlopeFactor = 0.0f;

		// 设置Shader采样纹理的MSAA(不是输出到屏幕上的MSAA)，需要创建逻辑设备的时候开启VkPhysicalDeviceFeatures里的sampleRateShading才能生效，暂时关闭
		VkPipelineMultisampleStateCreateInfo multisampleInfo = {};
		multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
		multisampleInfo.sampleShadingEnable = (VK_SAMPLE_COUNT_1_BIT & VK_SAMPLE_COUNT_1_BIT ? VK_FALSE : VK_TRUE);
		multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
		// 这个是调整sampleShading效果的，越接近1效果越平滑，越接近0性能越好
		multisampleInfo.minSampleShading = 1.0f;
		multisampleInfo.pSampleMask = VK_NULL_HANDLE;
		multisampleInfo.alphaToCoverageEnable = VK_FALSE;
		multisampleInfo.alphaToOneEnable = VK_FALSE;

		// Color Blend
		VkPipelineColorBlendAttachmentState colorBlendAttachment{};
		colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
		colorBlendAttachment.blendEnable = VK_TRUE;
		colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
		colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
		colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
		colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
		colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
		colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
		VkPipelineColorBlendStateCreateInfo colorBlending{};
		colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
		colorBlending.logicOpEnable = VK_FALSE;
		colorBlending.logicOp = VK_LOGIC_OP_COPY;
		colorBlending.pAttachments = &colorBlendAttachment;
		colorBlending.attachmentCount = 1;

		// 深度和模板配置
		VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
		depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
		// Depth
		depthStencilInfo.depthWriteEnable = VK_FALSE;
		depthStencilInfo.depthTestEnable = VK_TRUE;
		depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS;
		depthStencilInfo.depthBoundsTestEnable = VK_FALSE;
		depthStencilInfo.minDepthBounds = 0.0f;
		depthStencilInfo.maxDepthBounds = 1.0f;
		// Stencil
		depthStencilInfo.stencilTestEnable = VK_FALSE;
		depthStencilInfo.front = {};
		depthStencilInfo.back = {};

		auto descriptorSetLayout = VulkanContext::CreateDescriptorSetLayout(shader->GetInfo());
		auto pipelineLayout = VulkanContext::CreatePipelineLayout({ descriptorSetLayout }, {});

		VkGraphicsPipelineCreateInfo pipelineInfo{};
		pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
		pipelineInfo.pStages = shaderStages.data();
		pipelineInfo.stageCount = (uint32_t)shaderStages.size();
		pipelineInfo.pVertexInputState = &vertexInputInfo;
		pipelineInfo.pInputAssemblyState = &inputAssemblyInfo;
		pipelineInfo.pViewportState = &viewportStateInfo;
		pipelineInfo.pDynamicState = &dynamicStateInfo;
		pipelineInfo.pRasterizationState = &rasterizationInfo;
		pipelineInfo.pMultisampleState = &multisampleInfo;
		pipelineInfo.pColorBlendState = &colorBlending;
		pipelineInfo.pDepthStencilState = nullptr; &depthStencilInfo;
		pipelineInfo.layout = pipelineLayout;
		pipelineInfo.renderPass = PassList[RENDER_FORWARD_RENDERING]->Ptr();
		pipelineInfo.subpass = 0;
		pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
		pipelineInfo.basePipelineIndex = -1;

		VkPipeline pipeLine;
		if (vkCreateGraphicsPipelines(device.Ptr(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeLine) != VK_SUCCESS)
			throw std::runtime_error("failed to create graphics pipeline!");

		for (auto& shaderModule : shaderModules)
			vkDestroyShaderModule(device.Ptr(), shaderModule.second, nullptr);

		auto vulkan_pipeline = GetNextPipeline(shader->GetID());
		vulkan_pipeline->name = shader->GetName();
		vulkan_pipeline->pipeline = pipeLine;
		vulkan_pipeline->inUse = true;
		vulkan_pipeline->pipelineLayout = pipelineLayout;
		vulkan_pipeline->descriptorSetLayout = descriptorSetLayout;
		vulkan_pipeline->descriptorSet = backend.GetPool().Allocate(descriptorSetLayout);
	}
	VulkanVAO* RenderVulkanAPI::GetNextVAO(uint32_t& index)
	{
		uint32_t length = (uint32_t)VAOList.size();
		for (uint32_t i = 0; i < length; i++)
		{
			if (!VAOList[i]->inUse) {
				index = i;
				return VAOList[i];
			}
		}
		index = length;
		auto vao = new VulkanVAO();
		VAOList.push_back(vao);
		return vao;
	}

	VulkanPipeline* RenderVulkanAPI::GetNextPipeline(uint32_t& index)
	{
		uint32_t length = (uint32_t)PiPelineList.size();
		for (uint32_t i = 0; i < length; i++)
		{
			if (!PiPelineList[i]->inUse) {
				index = i;
				return PiPelineList[i];
			}
		}
		index = length;
		auto info = new VulkanPipeline();
		PiPelineList.push_back(info);
		return info;
	}

}

