#pragma once

#include "gfx/gpu_ssbo.h"
#include <cstddef>

namespace fn::gfx {

// Binner 2D. nx*ny bins; SSBO interno uint[nx*ny] row-major (idx = y*nx + x).
struct GpuHistogram2D {
    unsigned int program = 0;
    unsigned int loc_count = 0;
    unsigned int loc_nx = 0;
    unsigned int loc_ny = 0;
    unsigned int loc_min = 0;
    unsigned int loc_inv_range = 0;
    Ssbo bins;
    int nx = 0;
    int ny = 0;
};

GpuHistogram2D gpu_histogram_2d_create(int nx, int ny);

void gpu_histogram_2d_clear(GpuHistogram2D& h);

// Acumula un SSBO vec2[count] (interpretado como float pares) en bins 2D.
// Samples es un Ssbo float[2*count] (xy interleaved). xmin/xmax/ymin/ymax
// definen el rango; samples fuera se descartan.
//
// Tras esta llamada se requiere barrier_storage()/barrier_buffer_update()
// segun lo que vaya despues.
void gpu_histogram_2d_accumulate(GpuHistogram2D& h,
                                 const Ssbo& samples_xy,
                                 int count,
                                 float xmin, float xmax,
                                 float ymin, float ymax);

// Lee bins a CPU. out debe tener al menos nx*ny enteros.
void gpu_histogram_2d_readback(const GpuHistogram2D& h, unsigned int* out);

// Convertir uint[nx*ny] a float[nx*ny] normalizado (max=1.0). Helper CPU
// para alimentar heatmap_cpp_viz / contour_cpp_viz que esperan z[].
void gpu_histogram_2d_to_density(const unsigned int* counts, int nx, int ny,
                                 float* out_density);

void gpu_histogram_2d_destroy(GpuHistogram2D& h);

} // namespace fn::gfx
