// gpu_line_pipeline.cpp - Implementació del pipeline de línies

#include "core/rendering/gpu/gpu_line_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/line_frag.msl.h"
#include "core/rendering/gpu/msl/line_vert.msl.h"
#else
#include "core/rendering/gpu/spv/line_frag_spv.h"
#include "core/rendering/gpu/spv/line_vert_spv.h"
#endif

namespace Rendering::GPU {

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

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

        // Vertex: 1 UBO (viewport_size). Fragment: cap recurs.
#ifdef __APPLE__
        SDL_GPUShader* vert = createShaderMSL(owner_,
            Msl::LINE_VERT_MSL,
            "line_vs",
            SDL_GPU_SHADERSTAGE_VERTEX,
            /*num_samplers=*/0,
            /*num_uniform_buffers=*/1);
        SDL_GPUShader* frag = createShaderMSL(owner_,
            Msl::LINE_FRAG_MSL,
            "line_fs",
            SDL_GPU_SHADERSTAGE_FRAGMENT,
            /*num_samplers=*/0,
            /*num_uniform_buffers=*/0);
#else
        SDL_GPUShader* vert = createShaderSPIRV(owner_,
            LINE_VERT_SPV,
            LINE_VERT_SPV_SIZE,
            "main",
            SDL_GPU_SHADERSTAGE_VERTEX,
            /*num_samplers=*/0,
            /*num_uniform_buffers=*/1);
        SDL_GPUShader* frag = createShaderSPIRV(owner_,
            LINE_FRAG_SPV,
            LINE_FRAG_SPV_SIZE,
            "main",
            SDL_GPU_SHADERSTAGE_FRAGMENT,
            /*num_samplers=*/0,
            /*num_uniform_buffers=*/0);
#endif

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

        // Vertex layout: pos (vec2) + color (vec4) + edge_dist (float) → 7 floats per vèrtex.
        SDL_GPUVertexBufferDescription vbo_desc{};
        vbo_desc.slot = 0;
        vbo_desc.pitch = sizeof(LineVertex);
        vbo_desc.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
        vbo_desc.instance_step_rate = 0;

        SDL_GPUVertexAttribute attrs[3];
        attrs[0].location = 0;  // in_position
        attrs[0].buffer_slot = 0;
        attrs[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
        attrs[0].offset = 0;
        attrs[1].location = 1;  // in_color
        attrs[1].buffer_slot = 0;
        attrs[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4;
        attrs[1].offset = sizeof(float) * 2;
        attrs[2].location = 2;  // in_edge_dist
        attrs[2].buffer_slot = 0;
        attrs[2].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT;
        attrs[2].offset = sizeof(float) * 6;

        SDL_GPUVertexInputState vertex_input{};
        vertex_input.vertex_buffer_descriptions = &vbo_desc;
        vertex_input.num_vertex_buffers = 1;
        vertex_input.vertex_attributes = attrs;
        vertex_input.num_vertex_attributes = 3;

        // Color target = formato pasado por el caller (offscreen u otro).
        // Blending alpha estándar.
        SDL_GPUColorTargetDescription color_target{};
        color_target.format = target_format;
        color_target.blend_state.enable_blend = true;
        color_target.blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA;
        color_target.blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
        color_target.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD;
        color_target.blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE;
        color_target.blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
        color_target.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD;
        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;  // No backface culling para 2D
        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);

        // Els shaders es poden alliberar després de crear el pipeline (SDL els
        // reté internament mentre el pipeline estigui viu).
        SDL_ReleaseGPUShader(owner_, vert);
        SDL_ReleaseGPUShader(owner_, frag);

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

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

}  // namespace Rendering::GPU
