TreeGraphNode DeserializeNode(TreeNodeData data)
        {
            NodeInfo      info = serializedNameToInfo.TryGetValue(data.nodeTypeSerializableName) ?? throw new Exception($"Unexpected serialized name {data.nodeTypeSerializableName}");
            TreeGraphNode node = graphView.CreateNewNode(info, data.position, data.parameters);

            node.GUID = data.GUID;

            //Children
            TreeGraphNode child = null;

            for (int i = 0; i < data.children.Length; i++)
            {
                child = DeserializeNode(allData[data.children[i]]);
                graphView.AddElement(child.ParentPort.ConnectTo(node.ChildrenPort));
            }

            child?.RecalculateOrder();             //This will calculate the order of its siblings too
            return(node);
        }
        int SerializeNode(TreeGraphNode node)
        {
            var data = new TreeNodeData
            {
                position = node.GetPosition().position,                                                 //Position
                nodeTypeSerializableName = node.Info.serializedName,                                    //Node type name
                parameters = node.Parameters                                                            //Parameters
            };

            //Children
            if (node.ChildrenPort == null || !node.ChildrenPort.connected)
            {
                data.children = Array.Empty <int>();
            }
            else
            {
                var childIndices = new List <int>();

                foreach (Edge connection in node.ChildrenPort.connections)
                {
                    if (!(connection.input.node is TreeGraphNode child))
                    {
                        continue;
                    }

                    while (child.Order >= childIndices.Count)
                    {
                        childIndices.Add(-1);
                    }
                    childIndices[child.Order] = SerializeNode(child);
                }

                data.children = childIndices.ToArray();
            }

            //Finalize
            data.GUID = allData.Count;

            allData.Add(data);
            return(data.GUID);
        }
        /// <summary>
        /// Recalculate this node's order in parent.
        /// If <paramref name="propagateToSiblings"/> is true, then the order of all its siblings will be recalculated too
        /// </summary>
        public void RecalculateOrder(bool propagateToSiblings = true)
        {
            Port parentInputPort = ParentPort?.connections?.FirstOrDefault()?.output;

            if (parentInputPort == null || parentInputPort.capacity == Port.Capacity.Single)
            {
                SetEmpty();
            }
            else
            {
                TreeGraphNode parentNode = (TreeGraphNode)parentInputPort.node;

                if (GraphView.IsRemovingElement(this))
                {
                    if (propagateToSiblings)
                    {
                        //Loop through all parent's children connections
                        foreach (Edge edge in parentNode.ChildrenPort.connections)
                        {
                            if (edge.input.node is TreeGraphNode node && node != this)
                            {
                                node.RecalculateOrder(false);                                                                                    //Important false to avoid infinite recursion
                            }
                        }
                    }

                    SetEmpty();
                    return;
                }

                int   order = 0;
                float y     = GetPositionImmediate().y;

                //Loop through all parent's children connections
                foreach (Edge edge in parentNode.ChildrenPort.connections)
                {
                    if (!(edge.input.node is TreeGraphNode node) || node == this)
                    {
                        continue;
                    }
                    if (node.GetPositionImmediate().y < y && !GraphView.IsRemovingElement(node))
                    {
                        order++;
                    }

                    if (propagateToSiblings)
                    {
                        node.RecalculateOrder(false);                                          //NOTE: This false is really important to avoid infinite recursions!
                    }
                }

                orderLabel.text = order.ToString();                 //This looks better than "Order 1", however if it gets too difficult to understand we can change it back
                Order           = order;
            }

            void SetEmpty()
            {
                if (orderLabel != null)
                {
                    orderLabel.text = "";
                }
                Order = 0;
            }
        }