// gpu_bloom_pipeline.cpp - Implementació del pipeline de bloom separable.

#include "core/rendering/gpu/gpu_bloom_pipeline.hpp"

#include <SDL3/SDL.h>
#include <SDL3/SDL_gpu.h>

#include <iostream>

#include "core/rendering/gpu/gpu_device.hpp"
#include "core/rendering/gpu/shader_factory.hpp"

#ifdef __APPLE__
#include "core/rendering/gpu/msl/bloom_frag.msl.h"
#include "core/rendering/gpu/msl/postfx_vert.msl.h"
#else
#include "core/rendering/gpu/spv/bloom_frag_spv.h"
#include "core/rendering/gpu/spv/postfx_vert_spv.h"
#endif

namespace Rendering::GPU {

    GpuBloomPipeline::~GpuBloomPipeline() { destroy(); }

    auto GpuBloomPipeline::init(const GpuDevice& device,
        SDL_GPUTextureFormat target_format) -> bool {
        owner_ = device.get();
        if (owner_ == nullptr) {
            return false;
        }

        // Reutilitzem el vertex shader del postfx (fullscreen triangle, sense UBO).
        // El fragment shader és nou: 1 sampler (input) + 1 UBO (paràmetres del blur).
#ifdef __APPLE__
        SDL_GPUShader* vert = createShaderMSL(owner_,
            Msl::POSTFX_VERT_MSL,
            "postfx_vs",
            SDL_GPU_SHADERSTAGE_VERTEX,
            /*num_samplers=*/0,
            /*num_uniform_buffers=*/0);
        SDL_GPUShader* frag = createShaderMSL(owner_,
            Msl::BLOOM_FRAG_MSL,
            "bloom_fs",
            SDL_GPU_SHADERSTAGE_FRAGMENT,
            /*num_samplers=*/1,
            /*num_uniform_buffers=*/1);
#else
        SDL_GPUShader* vert = createShaderSPIRV(owner_,
            POSTFX_VERT_SPV,
            POSTFX_VERT_SPV_SIZE,
            "main",
            SDL_GPU_SHADERSTAGE_VERTEX,
            /*num_samplers=*/0,
            /*num_uniform_buffers=*/0);
        SDL_GPUShader* frag = createShaderSPIRV(owner_,
            BLOOM_FRAG_SPV,
            BLOOM_FRAG_SPV_SIZE,
            "main",
            SDL_GPU_SHADERSTAGE_FRAGMENT,
            /*num_samplers=*/1,
            /*num_uniform_buffers=*/1);
#endif

        if ((vert == nullptr) || (frag == nullptr)) {
            if (vert != nullptr) {
                SDL_ReleaseGPUShader(owner_, vert);
            }
            if (frag != nullptr) {
                SDL_ReleaseGPUShader(owner_, frag);
            }
            std::cerr << "[GpuBloomPipeline] Error carregant shaders bloom: " << SDL_GetError() << '\n';
            return false;
        }

        // Sense vertex input: els tres vèrtexs del fullscreen triangle es generen al shader.
        SDL_GPUVertexInputState vertex_input{};
        vertex_input.vertex_buffer_descriptions = nullptr;
        vertex_input.num_vertex_buffers = 0;
        vertex_input.vertex_attributes = nullptr;
        vertex_input.num_vertex_attributes = 0;

        // Color target = textura de bloom (output). Sense blending: cada passada
        // reescriu completament el contingut del target.
        SDL_GPUColorTargetDescription color_target{};
        color_target.format = target_format;
        color_target.blend_state.enable_blend = false;
        color_target.blend_state.color_write_mask =
            SDL_GPU_COLORCOMPONENT_R | SDL_GPU_COLORCOMPONENT_G |
            SDL_GPU_COLORCOMPONENT_B | SDL_GPU_COLORCOMPONENT_A;

        SDL_GPUGraphicsPipelineTargetInfo target_info{};
        target_info.color_target_descriptions = &color_target;
        target_info.num_color_targets = 1;
        target_info.has_depth_stencil_target = false;

        SDL_GPUGraphicsPipelineCreateInfo info{};
        info.vertex_shader = vert;
        info.fragment_shader = frag;
        info.vertex_input_state = vertex_input;
        info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
        info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL;
        info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_NONE;
        info.rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE;
        info.multisample_state.sample_count = SDL_GPU_SAMPLECOUNT_1;
        info.depth_stencil_state = {};
        info.target_info = target_info;

        pipeline_ = SDL_CreateGPUGraphicsPipeline(owner_, &info);

        SDL_ReleaseGPUShader(owner_, vert);
        SDL_ReleaseGPUShader(owner_, frag);

        if (pipeline_ == nullptr) {
            std::cerr << "[GpuBloomPipeline] SDL_CreateGPUGraphicsPipeline: "
                      << SDL_GetError() << '\n';
            return false;
        }
        return true;
    }

    void GpuBloomPipeline::destroy() {
        if ((pipeline_ != nullptr) && (owner_ != nullptr)) {
            SDL_ReleaseGPUGraphicsPipeline(owner_, pipeline_);
        }
        pipeline_ = nullptr;
        owner_ = nullptr;
    }

}  // namespace Rendering::GPU
