/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#include "Scene/Prefabs/LabelDrawable.hpp"
#include "Scene/SimpleDrawable.hpp"
#include "Scene/TextDrawable.hpp"
#include "Scene/DrawEncoder.hpp"
#include "VulkanGeometry.hpp"
#include "Vulkan/VulkanDrawContext.hpp"
#include "Vulkan/Scene/VulkanTexture.hpp"
#include "Vulkan/Scene/VulkanUniformBuffer.hpp"

using namespace OpenVulkano::Scene;

namespace OpenVulkano::Vulkan
{

	void EncodeBackground(LabelDrawable* labelDrawable, Vulkan::VulkanDrawContext* drawContext)
	{
		if (labelDrawable->IsBillboard())
		{
			OpenVulkano::Scene::UniformBuffer* buffer = labelDrawable->GetBillboardBuffer();
			VulkanUniformBuffer* vkBuffer = buffer->GetRenderResource();
			if (!vkBuffer)
			{
				vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(buffer);
			}
			vkBuffer->Record(drawContext);
		}

		OpenVulkano::Scene::UniformBuffer* labelBuffer = labelDrawable->GetLabelBuffer();
		VulkanUniformBuffer* vkBuffer = labelBuffer->GetRenderResource();
		if (!vkBuffer)
		{
			vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(labelBuffer);
		}
		vkBuffer->Record(drawContext);

		for (Node* node: labelDrawable->GetNodes())
		{
			if (!node->IsEnabled()) [[unlikely]]
			{
				continue;
			}
			drawContext->EncodeNode(node);
		}
		drawContext->commandBuffer.draw(4, 1, 0, 0);
	}

	void EncodeTextDrawable(LabelDrawable* labelDrawable, Vulkan::VulkanDrawContext* drawContext)
	{
		for (TextDrawable& entry : labelDrawable->GetTexts())
		{
			OpenVulkano::Scene::Shader* shader = entry.GetShader();
			drawContext->EncodeShader(shader);
			Geometry* mesh = entry.GetMesh();
			VulkanGeometry* renderGeo = mesh->GetRenderResource();
			if (!renderGeo)
			{
				renderGeo = drawContext->renderer->GetResourceManager().PrepareGeometry(mesh);
			}
			renderGeo->RecordBind(drawContext->commandBuffer);
			
			std::array<OpenVulkano::Scene::UniformBuffer*, 2> uniforms = { nullptr, nullptr };
			// fragment shader buffer
			uniforms[0] = entry.GetBuffer();
			if (labelDrawable->IsBillboard())
			{
				// vertex shader buffer
				uniforms[1] = labelDrawable->GetBillboardBuffer();
			}

			for (OpenVulkano::Scene::UniformBuffer* buffer : uniforms)
			{
				if (buffer)
				{
					VulkanUniformBuffer* vkBuffer = buffer->GetRenderResource();
					if (!vkBuffer)
					{
						vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(buffer);
					}
					vkBuffer->Record(drawContext);
				}
			}
		
			if (Material* material = entry.GetMaterial())
			{
				if (Texture* texture = material->texture)
				{
					VulkanTexture* renderTexture = texture->GetRenderResource();
					if (!renderTexture)
					{
						drawContext->renderer->GetResourceManager().PrepareMaterial(entry.GetMaterial());
						renderTexture = texture->GetRenderResource();
					}
					renderTexture->Record(drawContext);
				}
			}
		
			for (Node* node: labelDrawable->GetNodes())
			{
				if (!node->IsEnabled()) [[unlikely]]
				{
					continue;
				}
				drawContext->EncodeNode(node);
				renderGeo->RecordDraw(drawContext->commandBuffer);
			}
		}
	}

	void EncodeLabelDrawable(Drawable* instance, Vulkan::VulkanDrawContext* drawContext)
	{
		LabelDrawable* labelDrawable = static_cast<LabelDrawable*>(instance);
		EncodeBackground(labelDrawable, drawContext);
		EncodeTextDrawable(labelDrawable, drawContext);
	}
}

namespace
{
	void* labelDrawableVulkanEncoderReg = DrawEncoder::RegisterVulkanEncodeFunction<LabelDrawable>(&OpenVulkano::Vulkan::EncodeLabelDrawable);
}