// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include <IAEngine/Engine.hpp>
#include <Renderer/Pipeline.hpp>
#include <Renderer/Renderer.hpp>

namespace ia::iae
{
    Pipeline::Pipeline(IN CONST StageDesc &vertexStageDesc, IN CONST StageDesc &pixelStageDesc,
                       IN BOOL enableVertexBuffer, IN BOOL enableDepthTest)
    {
        SDL_GPUShader *vertexShader{};
        SDL_GPUShader *pixelShader{};

        SDL_GPUShaderCreateInfo shaderCreateInfo = {
            .entrypoint = "main",
            .format = SDL_GPU_SHADERFORMAT_SPIRV,
            .num_storage_textures = 0,
            .num_storage_buffers = 0,
        };

        shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_VERTEX;
        shaderCreateInfo.code = vertexStageDesc.SourceData;
        shaderCreateInfo.code_size = vertexStageDesc.SourceLength;
        shaderCreateInfo.num_samplers = vertexStageDesc.SamplerCount;
        shaderCreateInfo.num_uniform_buffers = vertexStageDesc.UniformBufferCount;
        if (!(vertexShader = SDL_CreateGPUShader(Renderer::GetDevice(), &shaderCreateInfo)))
            THROW_UNKNOWN("Failed to create a SDL shader: ", SDL_GetError());

        shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
        shaderCreateInfo.code = pixelStageDesc.SourceData;
        shaderCreateInfo.code_size = pixelStageDesc.SourceLength;
        shaderCreateInfo.num_samplers = pixelStageDesc.SamplerCount;
        shaderCreateInfo.num_uniform_buffers = pixelStageDesc.UniformBufferCount;
        if (!(pixelShader = SDL_CreateGPUShader(Renderer::GetDevice(), &shaderCreateInfo)))
            THROW_UNKNOWN("Failed to create a SDL shader: ", SDL_GetError());

        SDL_GPUColorTargetDescription colorTargetDesc = {
            .format = Renderer::GetRenderTargetFormat(),
        };

        SDL_GPUVertexBufferDescription vertexBufferDesc = {
            .slot = 0,
            .pitch = sizeof(GeometryVertex),
            .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
            .instance_step_rate = 0,
        };

        SDL_GPUVertexAttribute vertexAttributes[] = {
            {.location = 0, .buffer_slot = 0, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, .offset = 0},
            {.location = 1,
             .buffer_slot = 0,
             .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
             .offset = sizeof(glm::vec2)},
            {.location = 2,
             .buffer_slot = 0,
             .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4,
             .offset = sizeof(glm::vec4)},
        };

        SDL_GPUGraphicsPipelineCreateInfo createInfo = {
            .vertex_shader = vertexShader,
            .fragment_shader = pixelShader,
            .vertex_input_state = SDL_GPUVertexInputState{.vertex_buffer_descriptions = &vertexBufferDesc,
                                                          .num_vertex_buffers = enableVertexBuffer ? (UINT32) 1 : 0,
                                                          .vertex_attributes = vertexAttributes,
                                                          .num_vertex_attributes = enableVertexBuffer ? (UINT32) 3 : 0},
            .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
            .rasterizer_state = SDL_GPURasterizerState{.fill_mode = SDL_GPU_FILLMODE_FILL,
                                                       .cull_mode = SDL_GPU_CULLMODE_NONE,
                                                       .front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE},
            .depth_stencil_state = SDL_GPUDepthStencilState{.compare_op = SDL_GPU_COMPAREOP_GREATER_OR_EQUAL,
                                                            .write_mask = 0xFF,
                                                            .enable_depth_test = enableDepthTest,
                                                            .enable_depth_write = enableDepthTest,
                                                            .enable_stencil_test = false},
            .target_info = {.color_target_descriptions = &colorTargetDesc,
                            .num_color_targets = 1,
                            .depth_stencil_format = SDL_GPU_TEXTUREFORMAT_D16_UNORM,
                            .has_depth_stencil_target = enableDepthTest},
        };

        if(!(m_handle = SDL_CreateGPUGraphicsPipeline(Renderer::GetDevice(), &createInfo)))
            THROW_UNKNOWN("Failed to create a SDL graphics pipeline: ", SDL_GetError());

        SDL_ReleaseGPUShader(Renderer::GetDevice(), pixelShader);
        SDL_ReleaseGPUShader(Renderer::GetDevice(), vertexShader);
    }

    Pipeline::~Pipeline()
    {
        SDL_ReleaseGPUGraphicsPipeline(Renderer::GetDevice(), m_handle);
    }
} // namespace ia::iae