using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using System.Collections.Generic;
using System.Linq;
namespace GraphProcessor
{
    /// 
    /// Stack node view implementation, can be used to stack multiple node inside a context like VFX graph does.
    /// 
    public class BaseStackNodeView : StackNode
    {
        public delegate void ReorderNodeAction(BaseNodeView nodeView, int oldIndex, int newIndex);
    
        /// 
        /// StackNode data from the graph
        /// 
        protected internal BaseStackNode    stackNode;
        protected BaseGraphView             owner;
        readonly string                     styleSheet = "GraphProcessorStyles/BaseStackNodeView";
        /// Triggered when a node is re-ordered in the stack.
        public event ReorderNodeAction      onNodeReordered;
        public BaseStackNodeView(BaseStackNode stackNode)
        {
            this.stackNode = stackNode;
            styleSheets.Add(Resources.Load(styleSheet));
        }
        /// 
        protected override void OnSeparatorContextualMenuEvent(ContextualMenuPopulateEvent evt, int separatorIndex)
        {
            // TODO: write the context menu for stack node
        }
        /// 
        /// Called after the StackNode have been added to the graph view
        /// 
        public virtual void Initialize(BaseGraphView graphView)
        {
            owner = graphView;
            headerContainer.Add(new Label(stackNode.title));
            SetPosition(new Rect(stackNode.position, Vector2.one));
            InitializeInnerNodes();
        }
        void InitializeInnerNodes()
        {
            int i = 0;
            // Sanitize the GUID list in case some nodes were removed
            stackNode.nodeGUIDs.RemoveAll(nodeGUID =>
            {
                if (owner.graph.nodesPerGUID.ContainsKey(nodeGUID))
                {
                    var node = owner.graph.nodesPerGUID[nodeGUID];
                    var view = owner.nodeViewsPerNode[node];
                    view.AddToClassList("stack-child__" + i);
                    i++;
                    AddElement(view);
                    return false;
                }
                else
                {
                    return true; // remove the entry as the GUID doesn't exist anymore
                }
            });
        }
        /// 
        public override void SetPosition(Rect newPos)
        {
            base.SetPosition(newPos);
            stackNode.position = newPos.position;
        }
        /// 
        protected override bool AcceptsElement(GraphElement element, ref int proposedIndex, int maxIndex)
        {
            bool accept = base.AcceptsElement(element, ref proposedIndex, maxIndex);
            if (accept && element is BaseNodeView nodeView)
            {
                var index = Mathf.Clamp(proposedIndex, 0, stackNode.nodeGUIDs.Count - 1);
                int oldIndex = stackNode.nodeGUIDs.FindIndex(g => g == nodeView.nodeTarget.GUID);
                if (oldIndex != -1)
                {
                    stackNode.nodeGUIDs.Remove(nodeView.nodeTarget.GUID);
                    if (oldIndex != index)
                        onNodeReordered?.Invoke(nodeView, oldIndex, index);
                }
                stackNode.nodeGUIDs.Insert(index, nodeView.nodeTarget.GUID);
            }
            return accept;
        }
        public override bool DragLeave(DragLeaveEvent evt, IEnumerable selection, IDropTarget leftTarget, ISelection dragSource)
        {
            foreach (var elem in selection)
            {
                if (elem is BaseNodeView nodeView)
                    stackNode.nodeGUIDs.Remove(nodeView.nodeTarget.GUID);
            }
            return base.DragLeave(evt, selection, leftTarget, dragSource);
        }
    }
}