#ifndef PROC_NODES_PROC_GEOMETRY_NODE_H
#define PROC_NODES_PROC_GEOMETRY_NODE_H
#include "../graph/graph_executor.h"
#include "../graph/proc_node_graph.h"
#include <godot_cpp/classes/mesh_instance3d.hpp>
#include <godot_cpp/classes/multi_mesh_instance3d.hpp>
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/rendering_device.hpp>
namespace godot {
// Scene node that hosts and evaluates a proc-nodes graph
class ProcGeometryNode : public Node3D {
GDCLASS(ProcGeometryNode, Node3D);
protected:
static void _bind_methods();
void _notification(int p_what);
Ref<ProcNodeGraph> graph;
// The mesh handed to the graph's Group Input. A Blender geometry tree acts
// on the object's own mesh; without this a bridged graph has no geometry to
// act on and bakes to nothing.
Ref<Mesh> input_mesh;
// The same input, as a MESH rather than as a draw buffer: points, the
// corner array, faces as corner ranges, edges and attributes on their
// domains. blender-bridge fills this; when it is present it WINS over
// input_mesh, because input_mesh can only ever be the triangulated,
// corner-expanded shadow of it. See ProcMesh::from_mesh_data.
Dictionary input_mesh_data;
bool auto_update = true;
bool live_evaluation = true;
bool use_gpu = true;
bool needs_update = true;
// Cached execution plan
ExecutionPlan cached_plan;
bool plan_dirty = true;
// Simulation zone frame cache: zone_id -> geometry from previous frame
// Persists across evaluations so simulation state accumulates over time
HashMap<int, Ref<ProcGeometry>> simulation_cache;
// Persistent GPU resources (local RD + compiled pipelines survive across evaluations)
RenderingDevice *gpu_rd = nullptr;
void _ensure_gpu_device();
void _destroy_gpu_device();
// Baked output
Ref<ArrayMesh> baked_mesh;
Ref<MultiMesh> baked_multimesh;
// Cached display mesh (reused across evaluations when topology unchanged)
Ref<ArrayMesh> cached_display_mesh;
int cached_vert_count = 0;
int cached_index_count = 0;
// Child nodes for rendering
MeshInstance3D *mesh_output = nullptr;
MultiMeshInstance3D *multimesh_output = nullptr;
void _ensure_children();
void _evaluate();
Dictionary _build_overrides() const;
void _update_display();
public:
// Graph
void set_graph(const Ref<ProcNodeGraph> &p_graph);
Ref<ProcNodeGraph> get_graph() const { return graph; }
// Settings
void set_auto_update(bool p_auto) { auto_update = p_auto; }
bool get_auto_update() const { return auto_update; }
void set_live_evaluation(bool p_live) { live_evaluation = p_live; }
bool get_live_evaluation() const { return live_evaluation; }
void set_use_gpu(bool p_gpu);
bool get_use_gpu() const { return use_gpu; }
// Manual evaluation
void set_input_mesh(const Ref<Mesh> &p_mesh) { input_mesh = p_mesh; mark_dirty(); }
Ref<Mesh> get_input_mesh() const { return input_mesh; }
void set_input_mesh_data(const Dictionary &p_data) { input_mesh_data = p_data; mark_dirty(); }
Dictionary get_input_mesh_data() const { return input_mesh_data; }
// The evaluated result as a mesh, for the Godot -> Blender direction. Every
// face, edge and corner value survives, which bake_mesh() cannot carry.
Dictionary get_output_mesh_data();
void evaluate();
// Bake: evaluate once and save result
Ref<ArrayMesh> bake_mesh();
// Same evaluation, returning the ProcGeometry itself. bake_mesh() converts
// to an ArrayMesh, which is per-VERTEX only, so every corner-, face- and
// edge-domain value is lost on the way out. INSTRUMENT ONLY.
Ref<ProcGeometry> bake_geometry();
// Import graph from JSON and set it (used by blender-bridge UDS sync)
bool set_graph_from_json(const String &p_json);
// Mark for re-evaluation (emits defaults_changed for editor UI sync)
void mark_dirty();
// Mark plan as dirty (graph structure changed — recompile execution plan)
void invalidate_plan() { plan_dirty = true; needs_update = true; }
// node type -> "gpu" | "cpu..." for the last evaluation, from
// GraphExecutor::last_exec_report.
static Dictionary get_last_exec_report();
// Called when graph resource emits "changed" (e.g. node property edited in inspector)
void _on_graph_changed();
// ── Param delta queue (for blender-bridge Godot→Blender sync) ────
// Editor pushes deltas here; bridge drains them each frame.
// No signals between extensions — queue dies safely with the node.
private:
Vector<Dictionary> _param_delta_queue;
bool _suppress_param_deltas = false;
public:
void push_param_delta(int p_node_idx, int p_socket_idx, const Variant &p_value);
TypedArray<Dictionary> drain_param_deltas();
void suppress_param_deltas(bool p_suppress) { _suppress_param_deltas = p_suppress; }
ProcGeometryNode() = default;
~ProcGeometryNode();
};
} // namespace godot
#endif // PROC_NODES_PROC_GEOMETRY_NODE_H