/*
 * 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 "FrameBuffer.hpp"
#include "RenderPass.hpp"

void OpenVulkano::Vulkan::FrameBuffer::Init(Device* device, vk::Extent3D size, bool useDepthBuffer)
{
	this->size = size;
	this->device = device;
	this->useDepthBuffer = useDepthBuffer;
	colorFormat = FindColorFormat();
	if (useDepthBuffer)
	{
		depthBufferFormat = FindDepthFormat();
		CreateDepthStencil();
	}
	fullscreenViewport = vk::Viewport{ 0, 0, (float)size.width, (float)size.height, 0, 1 };
	fullscreenScissor = vk::Rect2D{ { 0, 0 }, { size.width, size.height } };
}

void OpenVulkano::Vulkan::FrameBuffer::RegisterRenderPass(RenderPass* renderPass)
{
	if (!device) throw std::
		runtime_error("The frame buffer needs to be initialized before binding it to a render pass");
	if (!renderPasses.empty())
	{
		//TODO check compatibility
		// https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#renderpass-compatibility
	}
	renderPasses.push_back(renderPass);
	if (renderPasses.size() == 1) CreateFrameBuffer();
}

void OpenVulkano::Vulkan::FrameBuffer::Resize(vk::Extent3D size)
{
	this->size = size;
	DestroyFrameBuffer();
	if (depthBuffer) depthBuffer.Close();
	if (useDepthBuffer) CreateDepthStencil();
	CreateFrameBuffer();
	for(RenderPass* renderPass : renderPasses)
	{
		renderPass->Resize(size);
	}
	fullscreenViewport = vk::Viewport{ 0, 0, (float)size.width, (float)size.height, 0, 1 };
	fullscreenScissor = vk::Rect2D{ { 0, 0 }, { size.width, size.height } };
}

void OpenVulkano::Vulkan::FrameBuffer::CreateDepthStencil()
{
	vk::ImageCreateInfo depthStencilCreateInfo({}, vk::ImageType::e2D, depthBufferFormat,
	                                           size, 1, 1);
	depthStencilCreateInfo.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::
		eTransferSrc;

	const vk::ImageAspectFlags aspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
	const vk::ImageViewCreateInfo depthStencilViewCreateInfo({}, {}, vk::ImageViewType::e2D, depthBufferFormat,
	                                                         {}, vk::ImageSubresourceRange(aspectMask, 0, 1, 0, 1));
	depthBuffer.Init(device, depthStencilCreateInfo, depthStencilViewCreateInfo);

	device->ExecuteNow([&](auto commandBuffer)
	{
		depthBuffer.SetLayout(commandBuffer, aspectMask, vk::ImageLayout::eDepthStencilAttachmentOptimal);
	});
}

void OpenVulkano::Vulkan::FrameBuffer::CreateFrameBuffer()
{
	vk::ImageView attachments[2]; // First attachment is the color buffer, second (optional) the depth buffer
	if (useDepthBuffer) attachments[1] = depthBuffer.view; //Depth buffer is the same for all frame buffers
	const vk::FramebufferCreateInfo fbCreateInfo({}, renderPasses[0]->renderPass, useDepthBuffer ? 2 : 1, attachments, size.width,
	                                             size.height, 1);

	auto images = GetImages();
	frameBuffers.resize(images.size());
	for (uint32_t i = 0; i < frameBuffers.size(); i++)
	{
		attachments[0] = images[i]->GetView();
		frameBuffers[i] = device->device.createFramebuffer(fbCreateInfo);
	}
}

void OpenVulkano::Vulkan::FrameBuffer::DestroyFrameBuffer()
{
	for (const auto frameBuffer : frameBuffers)
	{
		device->device.destroyFramebuffer(frameBuffer);
	}
	frameBuffers.clear();
}
