Code/UI/Spatial/Node.Recreation.cs
namespace Nodebox.UI.Spatial;

public partial class Node {
    public Action OnRecreated { get; set; }
    public void Recreate() {
        if (RecreationSuppressed > 0) { return; }
        if (!Enabled) { return; }
        TypeLastHashCode = Type?.GetHashCode();
        PolymorphSuppressed++;
        IReadOnlyList<IReadOnlySet<Nodebox.Wire>> oldInputWires = [];
        IReadOnlyList<IReadOnlySet<Nodebox.Wire>> oldOutputWires = [];

        if (_inner.IsValid()) {
            oldInputWires = _inner.OutputWires;
            oldOutputWires = _inner.InputWires;

            _inner?.Destroy();
            NodePanelRoot.Node = null;
            _inner = null;

            foreach (var wireSet in oldInputWires) {
                foreach (var wire in wireSet) {
                    using var _ = new SuppressPolymorph(wire.Source.Spatial);
                    var spatialWire = wire.Spatial;
                    wire.Spatial.Disconnect();
                }
            }

            foreach (var wireSet in oldOutputWires) {
                foreach (var wire in wireSet) {
                    using var _ = new SuppressPolymorph(wire.Destination.Spatial);
                    var spatialWire = wire.Spatial;
                    wire.Spatial.Disconnect();
                }
            }
        }

        var type = _serializableNode.Type;
        if (type == null) {
            return;
        }

        if (!type.IsAssignableTo(typeof(Nodebox.Node))) {
            return;
        }

        if (type.IsGenericType && !TypeLibrary.IsConstructedGenericType(type)) {
            return;
        }

        _inner = _serializableNode.Create();
        if (!_inner.IsValid()) {
            Log.Warning($"Couldn't create node {_serializableNode}..?");
            return;
        }

        _inner.Spatial = this;
        NodePanelRoot.Node = _inner;
        _inner.PinsChanged += OnInnerPinsChanged;
        _inner.InputsChanged += OnInnerInputsChanged;
        _inner.OnPolymorph += OnInnerPolymorph;
        RegisterInner();

        // foreach (var wireSet in oldInputWires) {
        //     foreach (var wire in wireSet) {
        //         wire.Spatial.Connect();
        //     }
        // }

        // foreach (var wireSet in oldOutputWires) {
        //     foreach (var wire in wireSet) {
        //         wire.Spatial.Connect();
        //     }
        // }

        OnRecreated?.Invoke();
        PolymorphSuppressed--;
    }

    private void RecreateSerializableNode() {
        _serializableNode = new Nodebox.Node.Serializable() {
            SerializableType = Type,
            Inputs = Inputs?.Select(x => new SerializableObject(x)).ToList() ?? [],
            Properties = Properties?.Select(x => (x.Key, new SerializableObject(x.Value))).ToDictionary() ?? []
        };

        if (OverridePins) {
            _serializableNode.Pins = (InputPins?.ToList() ?? [], OutputPins?.ToList() ?? []);
        }

        Recreate();
    }

    private void OnInnerPinsChanged() {
        using var _ = new SuppressRecreation(this);

        OverridePins = true;

        InputPins.Clear();
        foreach (var pin in _inner.InputPins) {
            InputPins.Add(pin);
        }

        OutputPins.Clear();
        foreach (var pin in _inner.OutputPins) {
            OutputPins.Add(pin);
        }
    }

    private void OnInnerInputsChanged() {
        using var _ = new SuppressRecreation(this);

        Inputs.Clear();
        foreach (var value in _inner.Inputs) {
            Inputs.Add(value);
        }
    }


    private Type QueuedPolymorph { get; set; }
    private void OnInnerPolymorph(Type newType) {
        if (PolymorphSuppressed > 0) { return; }
        Assert.IsNull(QueuedPolymorph);
        QueuedPolymorph = newType;
        _ = Task.FrameEnd().ContinueWith((_) => ApplyPolymorph());
    }
    private void ApplyPolymorph() {
        Type = QueuedPolymorph;
        QueuedPolymorph = null;
    }

    private uint RecreationSuppressed { get; set; } = 0;
    public sealed class SuppressRecreation : IDisposable {
        private Node Node { get; set; }
        public SuppressRecreation(Node node) {
            Node = node;
            Node.RecreationSuppressed += 1;
        }
        public void Dispose() {
            Node.RecreationSuppressed -= 1;
        }
    }

    private uint PolymorphSuppressed { get; set; } = 0;
    public sealed class SuppressPolymorph : IDisposable {
        private Node Node { get; set; }
        public SuppressPolymorph(Node node) {
            Node = node;
            Node.PolymorphSuppressed += 1;
        }
        public void Dispose() {
            Node.PolymorphSuppressed -= 1;
        }
    }
}