#include <Vk/RenderPass/RenderPass.hpp>
#include <Utilities/Utilities.hpp>

Vk::RenderPass::Builder::Builder() :
    m_subpass{},
    m_dependency{}
{
    m_subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;

    m_dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
    m_dependency.dstSubpass = 0;
    m_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    m_dependency.srcAccessMask = 0;
    m_dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    m_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}

Vk::RenderPass::Builder& Vk::RenderPass::Builder::addColorAttachment(
    VkFormat format,
    VkAttachmentLoadOp loadOp,
    VkAttachmentStoreOp storeOp,
    VkImageLayout initialLayout,
    VkImageLayout finalLayout
)
{
    int attachmentIndex = (int) m_attachmentDescriptions.size();

    VkAttachmentDescription colorAttachment{};
    colorAttachment.format = format;
    colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
    colorAttachment.loadOp = loadOp;
    colorAttachment.storeOp = storeOp;
    colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    colorAttachment.initialLayout = initialLayout;
    colorAttachment.finalLayout = finalLayout;

    m_attachmentDescriptions.push_back(colorAttachment);

    VkAttachmentReference colorAttachmentRef{};
    colorAttachmentRef.attachment = attachmentIndex;
    colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    m_colorAttachmentRefs.push_back(colorAttachmentRef);

    return *this;
}

Vk::RenderPass::Builder& Vk::RenderPass::Builder::addDepthAttachment(
    VkFormat format,
    VkAttachmentLoadOp loadOp,
    VkAttachmentStoreOp storeOp,
    VkImageLayout initialLayout,
    VkImageLayout finalLayout
)
{
    int attachmentIndex = (int) m_attachmentDescriptions.size();

    VkAttachmentDescription depthAttachment{};
    depthAttachment.format = format;
    depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
    depthAttachment.loadOp = loadOp;
    depthAttachment.storeOp = storeOp;
    depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    depthAttachment.initialLayout = initialLayout;
    depthAttachment.finalLayout = finalLayout;

    m_attachmentDescriptions.push_back(depthAttachment);

    m_depthAttachmentRef.attachment = attachmentIndex;
    m_depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    m_subpass.pDepthStencilAttachment = &m_depthAttachmentRef;

    m_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
    m_dependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    m_dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
    m_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

    return *this;
}

std::shared_ptr<Vk::RenderPass> Vk::RenderPass::Builder::build(std::shared_ptr<Vk::Device> device)
{
    m_subpass.colorAttachmentCount = m_colorAttachmentRefs.size();
    m_subpass.pColorAttachments = m_colorAttachmentRefs.data();

    VkRenderPassCreateInfo renderPassInfo{};
    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    renderPassInfo.attachmentCount = m_attachmentDescriptions.size();
    renderPassInfo.pAttachments = m_attachmentDescriptions.data();
    renderPassInfo.subpassCount = 1;
    renderPassInfo.pSubpasses = &m_subpass;
    renderPassInfo.dependencyCount = 1;
    renderPassInfo.pDependencies = &m_dependency;

    VkRenderPass renderPass;
    if (vkCreateRenderPass(device->inner(), &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS)
    {
        crash("Failed to create a render pass!");
    }

    return std::make_shared<Vk::RenderPass>(device, renderPass);
}

Vk::RenderPass::RenderPass(std::shared_ptr<Vk::Device> device, VkRenderPass renderPass) :
    m_device(device),
    m_renderPass(renderPass)
{}

Vk::RenderPass::~RenderPass()
{
    vkDestroyRenderPass(m_device->inner(), m_renderPass, nullptr);
}

VkRenderPass Vk::RenderPass::inner()
{
    return m_renderPass;
}