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

#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"

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;
    }

    SDL_GPUShader* vert = device.loadShader("line.vert.spv",
                                            SDL_GPU_SHADERSTAGE_VERTEX,
                                            /*num_uniform_buffers=*/1);
    SDL_GPUShader* frag = device.loadShader("line.frag.spv",
                                            SDL_GPU_SHADERSTAGE_FRAGMENT,
                                            /*num_uniform_buffers=*/0);
    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\n";
        return false;
    }

    // Vertex layout: pos (vec2) + color (vec4) → 6 floats por vertex.
    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[2];
    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;

    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 = 2;

    // 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);

    // Los shaders se pueden liberar tras crear el pipeline (SDL los retiene
    // internamente mientras el pipeline esté vivo).
    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
