Code/UI/Spatial/Graph.cs
using Nodebox.Nodes;

namespace Nodebox.UI.Spatial;

[Title("Nodebox Spatial Graph")]
[Category("Nodebox")]
public class Graph : Component, IMeta {
    private Nodebox.Graph _inner;
    public Nodebox.Graph Inner => _inner; // TODO: make readonly (make a IReadOnlyGraph interface)
    public Dictionary<string, object> Meta => Inner.Meta;

    protected override void OnEnabled() {
        base.OnEnabled();
        _inner = new() {
            Spatial = this
        };

        foreach (var node in GetComponentsInChildren<Node>(false, false)) {
            if (node.Inner.IsValid()) {
                _inner.Add(node.Inner);
            }
        }

        foreach (var wire in GetComponentsInChildren<Wire>(false, false)) {
            if (wire.Inner.IsValid()) {
                _inner.Add(wire.Inner);
            }
        }
    }

    protected override void OnDisabled() {
        foreach (var node in GetComponentsInChildren<Node>(false, false)) {
            if (node.Inner.IsValid()) {
                _inner.Remove(node.Inner);
            }
        }

        foreach (var wire in GetComponentsInChildren<Wire>(false, false)) {
            if (wire.Inner.IsValid()) {
                _inner.Remove(wire.Inner);
            }
        }

        _inner?.Destroy();
        _inner = null;
        base.OnDisabled();
    }

    protected override void OnRefresh() {
        base.OnRefresh();

    }

    // [Advanced]
    [Button(icon: "copy")]
    public void CopyGraphVizToClipboard() {
        Clipboard.SetText(GraphViz.GraphExtensions.ToGraphVizString(Inner));
    }


    private Node Previous { get; set; }
    [Button(icon: "add")]
    public void DebugMakeItGrow() {
        var go = new GameObject(GameObject, name: "Node");
        if (Previous.IsValid()) {
            go.LocalPosition = Previous.LocalPosition + new Vector3(0f, 30f, 0f);
        }

        var node = go.AddComponent<Node>();
        node.GraphStyle = GraphStyle.DefaultForExecutionContext();
        node.SerializableNode = Nodebox.Node.Serializable.From<Add<float>>([
            Random.Shared.Float(),
            Random.Shared.Float(),
        ]);

        go.NetworkSpawn(null);

        if (Previous.IsValid()) {
            var wireGo = new GameObject(Previous.GameObject, name: "Wire");
            var wireOut = wireGo.AddComponent<Wire>();
            wireOut.GraphStyle = GraphStyle.DefaultForExecutionContext();
            wireOut.SourceIndex = 0;
            wireOut.Destination = node;
            wireOut.DestinationIndex = (byte)Random.Shared.Int(1);

            wireGo.NetworkSpawn(null);
        }

        Previous = node;
    }

    // // [Advanced]
    // [Button(icon: "add")]
    // public void DebugAddNode() {
    //     var go = new GameObject(GameObject, name: "Node");
    //     var node = go.AddComponent<Node>();
    //     node.Inner = new Nodes.Add<float>();
    // }

    // // [Advanced]
    // [Button(icon: "remove")]
    // public void DebugRemoveLastNode() {
    //     var child = GameObject.Children.LastOrDefault();
    //     if (child.IsValid()) { return; }
    //     child.Destroy();
    // }

    // // [Advanced]
    // [Button(icon: "add")]
    // public void DebugAddGraph() {
    //     var go = new GameObject(GameObject, name: "Graph");
    //     var graph = go.AddComponent<Graph>();
    // }
}