#pragma once

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

namespace fn::gfx {

// Estado opaco del binner. Cachea el programa compute, los uniforms y un
// SSBO uint[nbins] de bins. Reutilizable across dispatches con el mismo
// tamano; si nbins cambia se debe destruir y crear de nuevo.
struct GpuHistogram1D {
    unsigned int program = 0;
    unsigned int loc_count = 0;
    unsigned int loc_nbins = 0;
    unsigned int loc_min = 0;
    unsigned int loc_inv_range = 0;
    Ssbo bins;            // uint[nbins] — atomicAdd target
    int nbins = 0;
};

// Crea binner para nbins bins. Compila el compute y reserva el SSBO uint[nbins].
GpuHistogram1D gpu_histogram_1d_create(int nbins);

// Borra el SSBO de bins (todos a cero) sin tocar el programa. Llamar antes
// de cada acumulacion para empezar de cero.
void gpu_histogram_1d_clear(GpuHistogram1D& h);

// Acumula un SSBO float[count] en los bins del binner. range_min y range_max
// definen los limites; samples fuera del rango se descartan (no clamp). El
// SSBO de samples debe estar bindeado por el caller en binding=0; el binner
// bindea automaticamente sus bins en binding=1.
//
// Tras esta llamada se requiere barrier_storage() o barrier_buffer_update()
// segun lo que el caller vaya a hacer despues.
void gpu_histogram_1d_accumulate(GpuHistogram1D& h,
                                 const Ssbo& samples,
                                 int count,
                                 float range_min,
                                 float range_max);

// Lee los counts de bins a CPU. out debe tener al menos h.nbins enteros.
// Hace ssbo_readback (sincrono).
void gpu_histogram_1d_readback(const GpuHistogram1D& h, unsigned int* out);

// Libera programa + SSBO. Seguro multiples veces.
void gpu_histogram_1d_destroy(GpuHistogram1D& h);

} // namespace fn::gfx
