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

namespace OpenVulkano::Vulkan
{
	void RenderPass::Init(Device* device, FrameBuffer* frameBuffer, bool clearColor, bool clearDepth)
	{
		m_device = device->device;
		m_frameBuffer = frameBuffer;
		m_useClearColor = clearColor;
		m_useClearDepth = clearDepth;
		CreateRenderPass();
		frameBuffer->RegisterRenderPass(this);
		m_beginInfo.renderPass = renderPass;
		m_beginInfo.renderArea = vk::Rect2D(vk::Offset2D(), m_frameBuffer->GetSize2D());
		m_beginInfo.clearValueCount = 2;
		m_beginInfo.pClearValues = m_clearValues.data();
		if (clearColor) SetClearColor(EngineConfiguration::GetEngineConfiguration()->GetFrameBufferClearColor());
		if (clearDepth) SetClearDepth();
	}

	void RenderPass::Close()
	{
		m_device.destroy(renderPass);
		m_frameBuffer = nullptr;
	}

	void RenderPass::Begin(vk::CommandBuffer& commandBuffer, bool primaryBuffer)
	{
		m_beginInfo.framebuffer = m_frameBuffer->GetCurrentFrameBuffer();
		commandBuffer.beginRenderPass(m_beginInfo, primaryBuffer ? vk::SubpassContents::eInline : vk::SubpassContents::eSecondaryCommandBuffers);
	}

	void RenderPass::End(vk::CommandBuffer& commandBuffer)
	{
		commandBuffer.endRenderPass();
	}

	void RenderPass::CreateRenderPass()
	{
		std::vector<vk::AttachmentDescription> attachments;
		attachments.reserve(m_frameBuffer->UseDepthBuffer() ? 2 : 1);

		// Color attachment
		attachments.emplace_back(vk::AttachmentDescriptionFlags(), m_frameBuffer->GetColorFormat(), vk::SampleCountFlagBits::e1,
								 m_useClearColor ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eLoad,
								 vk::AttachmentStoreOp::eStore, vk::ImageLayout::ePresentSrcKHR, vk::ImageLayout::ePresentSrcKHR);

		vk::AttachmentReference* depthReference = nullptr;
		if (m_frameBuffer->UseDepthBuffer())
		{ // Depth attachment
			attachments.emplace_back(vk::AttachmentDescriptionFlags(), m_frameBuffer->GetDepthFormat(), vk::SampleCountFlagBits::e1,
									 m_useClearDepth ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore, m_useClearDepth ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
									 vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eDepthStencilAttachmentOptimal, vk::ImageLayout::eDepthStencilAttachmentOptimal);
			depthReference = new vk::AttachmentReference(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);
		}

		std::vector<vk::AttachmentReference> colorAttachmentReferences;
		colorAttachmentReferences.emplace_back(0, vk::ImageLayout::eColorAttachmentOptimal);

		std::vector<vk::SubpassDescription> subPasses;
		subPasses.emplace_back(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr,
							   colorAttachmentReferences.size(), colorAttachmentReferences.data(), nullptr, depthReference, 0, nullptr);
		std::vector<vk::SubpassDependency> subPassDependencies;
		subPassDependencies.emplace_back(0, VK_SUBPASS_EXTERNAL, vk::PipelineStageFlagBits::eColorAttachmentOutput,
										 vk::PipelineStageFlagBits::eBottomOfPipe, vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
										 vk::AccessFlagBits::eMemoryRead, vk::DependencyFlagBits::eByRegion);
		subPassDependencies.emplace_back(VK_SUBPASS_EXTERNAL, 0, vk::PipelineStageFlagBits::eBottomOfPipe,
										 vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eMemoryRead,
										 vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
										 vk::DependencyFlagBits::eByRegion);

		const vk::RenderPassCreateInfo createInfo(vk::RenderPassCreateFlags(), attachments.size(), attachments.data(),
												  subPasses.size(), subPasses.data(), subPassDependencies.size(), subPassDependencies.data());
		CreateRenderPass(createInfo);
		delete depthReference;
	}

	void RenderPass::CreateRenderPass(const vk::RenderPassCreateInfo& renderPassCreateInfo)
	{
		renderPass = m_device.createRenderPass(renderPassCreateInfo);
	}
}
