using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using XNode;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
#endif
namespace XNodeEditor
{
    ///  Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. 
    [CustomNodeEditor(typeof(XNode.Node))]
    public class NodeEditor : XNodeEditor.Internal.NodeEditorBase
    {
        private readonly Color DEFAULTCOLOR = new Color32(90, 97, 105, 255);
        ///  Fires every whenever a node was modified through the editor 
        public static Action onUpdateNode;
        public readonly static Dictionary portPositions = new Dictionary();
#if ODIN_INSPECTOR
        protected internal static bool inNodeEditor = false;
#endif
        public virtual void OnHeaderGUI()
        {
            GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
        }
        ///  Draws standard field editors for all public fields 
        public virtual void OnBodyGUI()
        {
#if ODIN_INSPECTOR
            inNodeEditor = true;
#endif
            // Unity specifically requires this to save/update any serial object.
            // serializedObject.Update(); must go at the start of an inspector gui, and
            // serializedObject.ApplyModifiedProperties(); goes at the end.
            serializedObject.Update();
            string[] excludes = { "m_Script", "graph", "position", "ports" };
#if ODIN_INSPECTOR
            InspectorUtilities.BeginDrawPropertyTree(objectTree, true);
            GUIHelper.PushLabelWidth(84);
            objectTree.Draw(true);
            InspectorUtilities.EndDrawPropertyTree(objectTree);
            GUIHelper.PopLabelWidth();
#else
            Node.CreateNodeMenuAttribute createNodeMenuAttribute = this.target.GetType().GetCustomAttribute(typeof(Node.CreateNodeMenuAttribute)) as Node.CreateNodeMenuAttribute;
            if (createNodeMenuAttribute != null)
            {
                if (GUILayout.Button("帮助"))
                {
                    if (EditorUtility.DisplayDialog("帮助", createNodeMenuAttribute.tooltip, "确定")) //显示对话框
                    {
                        
                    }
                }
            }
       
            // Iterate through serialized properties and draw them like the Inspector (But with ports)
            SerializedProperty iterator = serializedObject.GetIterator();
            XNode.Node rootNode = serializedObject.targetObject as XNode.Node;
            bool enterChildren = true;
            while (iterator.NextVisible(enterChildren))
            {
                enterChildren = false;
                if (excludes.Contains(iterator.name)) continue;
                NodeEditorGUILayout.PropertyField(iterator, rootNode, true);
            }
#endif
            // Iterate through dynamic ports and draw them in the order in which they are serialized
            foreach (XNode.NodePort dynamicPort in target.DynamicPorts)
            {
                if (NodeEditorGUILayout.IsDynamicPortListPort(dynamicPort)) continue;
                NodeEditorGUILayout.PortField(dynamicPort);
            }
            serializedObject.ApplyModifiedProperties();
#if ODIN_INSPECTOR
            // Call repaint so that the graph window elements respond properly to layout changes coming from Odin
            if (GUIHelper.RepaintRequested) {
                GUIHelper.ClearRepaintRequest();
                window.Repaint();
            }
#endif
#if ODIN_INSPECTOR
            inNodeEditor = false;
#endif
        }
        public virtual int GetWidth()
        {
            Type type = target.GetType();
            int width;
            if (type.TryGetAttributeWidth(out width)) return width;
            else return 208;
        }
        ///  Returns color for target node 
        public virtual Color GetTint()
        {
            // Try get color from [NodeTint] attribute
            Type type = target.GetType();
            Color color;
            if (type.TryGetAttributeTint(out color)) return color;
            // Return default color (grey)
            else return DEFAULTCOLOR;
        }
        public virtual GUIStyle GetBodyStyle()
        {
            return NodeEditorResources.styles.nodeBody;
        }
        public virtual GUIStyle GetBodyHighlightStyle()
        {
            return NodeEditorResources.styles.nodeHighlight;
        }
        ///  Add items for the context menu when right-clicking this node. Override to add custom menu items. 
        public virtual void AddContextMenuItems(GenericMenu menu)
        {
            bool canRemove = true;
            // Actions if only one node is selected
            if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node)
            {
                XNode.Node node = Selection.activeObject as XNode.Node;
                menu.AddItem(new GUIContent("Move To Top"), false, () => NodeEditorWindow.current.MoveNodeToTop(node));
                menu.AddItem(new GUIContent("Rename"), false, NodeEditorWindow.current.RenameSelectedNode);
                canRemove = NodeGraphEditor.GetEditor(node.graph, NodeEditorWindow.current).CanRemove(node);
            }
            // Add actions to any number of selected nodes
            menu.AddItem(new GUIContent("Copy"), false, NodeEditorWindow.current.CopySelectedNodes);
            menu.AddItem(new GUIContent("Duplicate"), false, NodeEditorWindow.current.DuplicateSelectedNodes);
            if (canRemove) menu.AddItem(new GUIContent("Remove"), false, NodeEditorWindow.current.RemoveSelectedNodes);
            else menu.AddItem(new GUIContent("Remove"), false, null);
            // Custom sctions if only one node is selected
            if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node)
            {
                XNode.Node node = Selection.activeObject as XNode.Node;
                menu.AddCustomContextMenuItems(node);
            }
        }
        ///  Rename the node asset. This will trigger a reimport of the node. 
        public void Rename(string newName)
        {
            if (newName == null || newName.Trim() == "") newName = NodeEditorUtilities.NodeDefaultName(target.GetType());
            target.name = newName;
            OnRename();
            AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
        }
        ///  Called after this node's name has changed. 
        public virtual void OnRename()
        {
        }
        [AttributeUsage(AttributeTargets.Class)]
        public class CustomNodeEditorAttribute : Attribute,
            XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib
        {
            private Type inspectedType;
            ///  Tells a NodeEditor which Node type it is an editor for 
            /// Type that this editor can edit
            public CustomNodeEditorAttribute(Type inspectedType)
            {
                this.inspectedType = inspectedType;
            }
            public Type GetInspectedType()
            {
                return inspectedType;
            }
        }
    }
}