#pragma once
#include "viz/graph_force_layout.h"

struct GraphData;
struct ForceLayoutConfig;

// GPU-accelerated force-directed layout (issue 0049h). API simetrica con
// `graph_force_layout_step` para que el consumer pueda swappear CPU<->GPU.
//
// Usa compute shaders 4.3 + spatial hash grid (no Barnes-Hut). Requiere un
// contexto GL 4.3 core activo en el thread que llama (igual que el resto del
// renderer). Si el contexto no soporta compute, `_create()` devuelve nullptr.
//
// Modelo de memoria:
//   _create:    aloca SSBOs (positions, velocities, forces, flags, edges,
//               weights, grid_counts, grid_cells, energy).
//   _upload:    copia el GraphData CPU→GPU (positions, velocities, edges,
//               weights, flags). Llamar despues de cualquier mutacion CPU
//               externa (e.g. el usuario arrastra un nodo).
//   _step:      lanza el pipeline de compute. NO toca el GraphData CPU.
//   _readback:  baja `positions` (8*N bytes) y opcionalmente velocities, y
//               actualiza el GraphData CPU. Calcula bounds en CPU.
//   _destroy:   libera SSBOs y programs.
//
// El consumer puede saltarse `_readback` si solo dibuja con la GPU; las
// posiciones siguen vivas en el SSBO `positions` para que el renderer las
// lea via TBO/SSBO sin viajar por la CPU.

struct ForceLayoutGPU; // opaque

// Crea un context GPU. `max_nodes` y `max_edges` definen el tamano fijo de
// los SSBOs (no se redimensionan). `grid_cells_per_side` es la resolucion del
// spatial hash (default 64 → 4096 celdas). Si la compilacion de compute
// shaders falla (driver sin 4.3 / Mesa sin compute), devuelve nullptr y
// escribe el motivo en stderr.
ForceLayoutGPU* graph_force_layout_gpu_create(int max_nodes, int max_edges,
                                              int grid_cells_per_side = 64);

// Sube el grafo entero al GPU. Llamar tras cambios topologicos o tras editar
// posiciones/flags desde CPU. El node_count/edge_count del grafo se cachea
// internamente; subsequent _step usa esos valores.
void graph_force_layout_gpu_upload(ForceLayoutGPU* ctx, const GraphData& graph);

// Ejecuta `config.iterations` pasos del pipeline GPU sobre el ultimo grafo
// subido. Devuelve la energia total (sum |v|^2) tras la ultima iteracion;
// cero si no se llamo a _upload.
float graph_force_layout_gpu_step(ForceLayoutGPU* ctx, const ForceLayoutConfig& config);

// Sincroniza GPU→CPU las posiciones (y velocidades, opcional). Tambien
// actualiza graph.min_x/min_y/max_x/max_y. Es la operacion mas cara (~400
// us para 50k nodos por la latencia de roundtrip GPU→CPU); evitar en
// hot path si el renderer puede leer del SSBO directamente.
void graph_force_layout_gpu_readback(ForceLayoutGPU* ctx, GraphData& graph,
                                     bool include_velocities = false);

void graph_force_layout_gpu_destroy(ForceLayoutGPU* ctx);

// Devuelve el ID GL del SSBO de positions (binding 0). Permite que el
// renderer lea las posiciones directamente sin hacer readback. 0 si ctx
// no es valido. Las posiciones son `vec2[max_nodes]` en std430 layout.
unsigned int graph_force_layout_gpu_positions_ssbo(const ForceLayoutGPU* ctx);

// True si el ultimo _create logro compilar todos los compute shaders. Util
// para el toggle CPU/GPU en demos: si false, deshabilitar el toggle.
bool graph_force_layout_gpu_available();
