/*
	VulkanFramework
	by John Ryland
	Copyright (c) 2023
*/

////////////////////////////////////////////////////////////////////////////////////
//	Vulkan Pipeline

#include "VulkanPipeline.h"

namespace Vulkan {

Pipeline::Pipeline()
    : m_pipeline(nullptr)
    , m_pipelineLayout(nullptr)
{
}

// virtual
Pipeline::~Pipeline()
{
    Destroy();
}

// virtual
void Pipeline::Destroy()
{
    if (m_pipeline)
        vkDestroyPipeline(m_owner->m_device, m_pipeline, nullptr);
    if (m_pipelineLayout)
        vkDestroyPipelineLayout(m_owner->m_device, m_pipelineLayout, nullptr);
    m_pipeline = nullptr;
    m_pipelineLayout = nullptr;
}

// virtual
void Pipeline::CreateLayout(Device* device)
{
    m_owner = device;

    VkPipelineLayoutCreateInfo pipelineLayoutInfo {};
    pipelineLayoutInfo.sType          = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    pipelineLayoutInfo.setLayoutCount = 1;
    pipelineLayoutInfo.pSetLayouts    = m_descriptorSetLayout;
    m_owner->CheckResult(vkCreatePipelineLayout(m_owner->m_device, &pipelineLayoutInfo, m_owner->m_allocator, &m_pipelineLayout), "failed to create pipeline layout!");
}

// ImGui,  cullEnabled = false, depthEnabled = false, blendEnabled = true

// virtual
void Pipeline::Initialize(Device*               device,
                          VkShaderModule        vertexShader,
                          VkShaderModule        fragmentShader,
                          VkSampleCountFlagBits msaaSamples,
                          bool                  cullEnabled,
                          bool                  depthEnabled,
                          bool                  blendEnabled)
{
    m_owner = device;

    VkPipelineShaderStageCreateInfo shaderStages[2] {};
    shaderStages[0].sType                            = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    shaderStages[0].stage                            = VK_SHADER_STAGE_VERTEX_BIT;
    shaderStages[0].module                           = vertexShader;
    shaderStages[0].pName                            = "main";
    shaderStages[1].sType                            = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    shaderStages[1].stage                            = VK_SHADER_STAGE_FRAGMENT_BIT;
    shaderStages[1].module                           = fragmentShader;
    shaderStages[1].pName                            = "main";

    VkPipelineVertexInputStateCreateInfo vertexInputInfo {};
    vertexInputInfo.sType                            = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
    vertexInputInfo.vertexBindingDescriptionCount    = static_cast<uint32_t>(m_vertexBindingDescriptions.size());
    vertexInputInfo.pVertexBindingDescriptions       = m_vertexBindingDescriptions.data();
    vertexInputInfo.vertexAttributeDescriptionCount  = static_cast<uint32_t>(m_vertexAttributeDescriptions.size());
    vertexInputInfo.pVertexAttributeDescriptions     = m_vertexAttributeDescriptions.data();

    VkPipelineInputAssemblyStateCreateInfo inputAssembly {};
    inputAssembly.sType                              = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
    inputAssembly.topology                           = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
    inputAssembly.primitiveRestartEnable             = VK_FALSE;

    VkPipelineViewportStateCreateInfo viewportState {};
    viewportState.sType                              = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
    viewportState.viewportCount                      = 1;
    viewportState.scissorCount                       = 1;

    VkPipelineRasterizationStateCreateInfo rasterizer {};
    rasterizer.sType                                 = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
    rasterizer.depthClampEnable                      = VK_FALSE;
    rasterizer.rasterizerDiscardEnable               = VK_FALSE;
    rasterizer.polygonMode                           = VK_POLYGON_MODE_FILL;
    rasterizer.lineWidth                             = 1.0f;
    rasterizer.cullMode                              = (cullEnabled) ? VK_CULL_MODE_BACK_BIT : VK_CULL_MODE_NONE;
    rasterizer.frontFace                             = VK_FRONT_FACE_COUNTER_CLOCKWISE;
    rasterizer.depthBiasEnable                       = VK_FALSE;

    VkPipelineMultisampleStateCreateInfo multisampling {};
    multisampling.sType                              = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
    multisampling.sampleShadingEnable                = VK_FALSE;
    multisampling.rasterizationSamples               = (msaaSamples) ? msaaSamples : VK_SAMPLE_COUNT_1_BIT;

    VkPipelineDepthStencilStateCreateInfo depthStencil {};
    depthStencil.sType                               = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
    depthStencil.depthTestEnable                     = (depthEnabled) ? VK_TRUE : VK_FALSE;
    depthStencil.depthWriteEnable                    = (depthEnabled) ? VK_TRUE : VK_FALSE;
    depthStencil.depthCompareOp                      = (depthEnabled) ? VK_COMPARE_OP_LESS : VK_COMPARE_OP_NEVER;
    depthStencil.depthBoundsTestEnable               = VK_FALSE;
    depthStencil.stencilTestEnable                   = VK_FALSE;

    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                 = (blendEnabled) ? VK_TRUE : VK_FALSE;
    colorBlendAttachment.srcColorBlendFactor         = (blendEnabled) ? VK_BLEND_FACTOR_SRC_ALPHA : VK_BLEND_FACTOR_ZERO;
    colorBlendAttachment.dstColorBlendFactor         = (blendEnabled) ? VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA : VK_BLEND_FACTOR_ZERO;
    colorBlendAttachment.colorBlendOp                = VK_BLEND_OP_ADD;
    colorBlendAttachment.srcAlphaBlendFactor         = (blendEnabled) ? VK_BLEND_FACTOR_ONE : VK_BLEND_FACTOR_ZERO;
    colorBlendAttachment.dstAlphaBlendFactor         = (blendEnabled) ? VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA : 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                            = (blendEnabled) ? VK_LOGIC_OP_CLEAR : VK_LOGIC_OP_COPY;
    colorBlending.attachmentCount                    = 1;
    colorBlending.pAttachments                       = &colorBlendAttachment;
    colorBlending.blendConstants[0]                  = 0.0f;
    colorBlending.blendConstants[1]                  = 0.0f;
    colorBlending.blendConstants[2]                  = 0.0f;
    colorBlending.blendConstants[3]                  = 0.0f;

    VkDynamicState dynamicStates[] = { VK_DYNAMIC_STATE_VIEWPORT,
                                       VK_DYNAMIC_STATE_SCISSOR };
    VkPipelineDynamicStateCreateInfo dynamicState {};
    dynamicState.sType                               = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
    dynamicState.dynamicStateCount                   = static_cast<uint32_t>(std::size(dynamicStates));
    dynamicState.pDynamicStates                      = dynamicStates;

    VkGraphicsPipelineCreateInfo pipelineInfo {};
    pipelineInfo.sType                               = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
    pipelineInfo.flags                               = m_pipelineFlags;
    pipelineInfo.stageCount                          = std::size(shaderStages);
    pipelineInfo.pStages                             = shaderStages;
    pipelineInfo.pVertexInputState                   = &vertexInputInfo;
    pipelineInfo.pInputAssemblyState                 = &inputAssembly;
    pipelineInfo.pViewportState                      = &viewportState;
    pipelineInfo.pRasterizationState                 = &rasterizer;
    pipelineInfo.pMultisampleState                   = &multisampling;
    pipelineInfo.pDepthStencilState                  = &depthStencil;
    pipelineInfo.pColorBlendState                    = &colorBlending;
    pipelineInfo.pDynamicState                       = &dynamicState;
    pipelineInfo.layout                              = m_pipelineLayout;
    pipelineInfo.renderPass                          = m_renderPass;
    pipelineInfo.subpass                             = m_subpass;
    pipelineInfo.basePipelineHandle                  = VK_NULL_HANDLE;
    m_owner->CheckResult(vkCreateGraphicsPipelines(m_owner->m_device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_pipeline), "failed to create graphics pipeline!");
}

} // Vulkan namespace
