﻿using System.Collections.Generic;
using System.Linq;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;

namespace E.Story
{
    /// <summary>
    /// 基类节点
    /// </summary>
    public class BaseNode : Node
    {
        #region UI元素

        protected StoryGraphView graphView;
        protected VisualElement customDataContainer;
        protected Foldout foldout;
        protected Port input;
        protected Port output;

        public Port Input { get => input; }

        public Port Output { get => output; }

        #endregion


        #region 通用节点属性

        /// <summary>
        /// 节点GUID
        /// </summary>
        public string GUID { get; set; }

        /// <summary>
        /// 节点类型
        /// </summary>
        public NodeType Type { get; set; }

        /// <summary>
        /// 节点标题
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// 节点备注
        /// </summary>
        public string Note { get; set; }

        /// <summary>
        /// 选项数据列表
        /// </summary>
        public List<ChoiceData> ChoiceDatas { get; set; }

        /// <summary>
        /// 所属分组
        /// </summary>
        public BaseGroup Group { get; set; }

        #endregion


        #region 重写方法

        /// <summary>
        /// 初始化节点
        /// </summary>
        /// <param name="graphView">故事视图</param>
        /// <param name="title">标题</param>
        /// <param name="position">坐标</param>
        public virtual void Init(StoryGraphView graphView, string title, Vector2 position)
        {
            // 设置字段
            this.graphView = graphView;
            SetPosition(new Rect(position, Vector2.zero));

            // 设置属性默认值
            Type = NodeType.Base;
            GUID = UnityEditor.GUID.Generate().ToString();
            Title = title;
            Note = "备注信息";
            ChoiceDatas = new() { new("下个节点") };

            // 添加USS类名
            mainContainer.AddToClassList("node__main-container");
            titleContainer.AddToClassList("node__title-container");
            inputContainer.AddToClassList("node__input-container");
            outputContainer.AddToClassList("node__output-container");
            extensionContainer.AddToClassList("node__extension-container");
        }

        /// <summary>
        /// 绘制上下文菜单
        /// </summary>
        /// <param name="evt"></param>
        public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
        {
            evt.menu.AppendAction("清除输入连接", 
                action => DisconnectedInputPorts(), 
                HasInputConnection() ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);

            evt.menu.AppendAction("清除输出连接", 
                action => DisconnectedOutputPorts(), 
                HasOutputConnection() ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);

            evt.menu.AppendAction("清除所有连接", 
                action => DisconnectedAllPorts(),
                HasAnyConnection() ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);

            evt.menu.AppendSeparator();
        }

        #endregion


        #region 绘制方法

        /// <summary>
        /// 绘制视图
        /// </summary>
        public virtual void Draw()
        {
            DrawMainContainer();
            DrawTitleContainer();
            DrawTitleButtonContainer();
            DrawTopContainer();
            DrawInputContainer();
            DrawOutputContainer();
            DrawExtensionContainer();
        }

        /// <summary>
        /// 绘制主容器
        /// </summary>
        public virtual void DrawMainContainer()
        {
        }

        /// <summary>
        /// 绘制标题容器
        /// </summary>
        public virtual void DrawTitleContainer()
        {
            // 创建标题输入框
            TextField tfdTitle = ElementUtility.CreateTextField(Title, null, callback =>
            {
                // 清除无效字符
                string temp = callback.newValue;
                if (!StoryEditorWindow.AllowWhitespacesInTitles)
                {
                    temp = temp.RemoveWhitespaces();
                }
                if (!StoryEditorWindow.AllowSpecialCharactersInTitles)
                {
                    temp = temp.RemoveSpecialCharacters();
                }

                // 检测是否允许标题重复
                if (StoryEditorWindow.AllowDuplicateTitles)
                {
                    // 更新标题
                    Title = temp;
                }
                else
                {
                    // 如果此节点未分组，更新标题和分组信息
                    if (Group == null)
                    {
                        graphView.RemoveDuplicateUngroupedNodeRecord(this);
                        Title = temp;
                        graphView.AddDuplicateUngroupedNodeRecord(this);
                        return;
                    }

                    // 如果此节点已分组，更新标题和分组信息
                    BaseGroup currrenrGroup = Group;
                    graphView.RemoveDuplicateGroupedNodeRecord(this, currrenrGroup);
                    Title = temp;
                    graphView.AddDuplicateGroupedNodeRecord(this, currrenrGroup);
                }
            });

            // 将标题输入框放在最左侧
            titleContainer.Insert(0, tfdTitle);

            // 添加USS类名
            tfdTitle.AddClasses
            (
                "textfield",
                "textfield__hidden",
                "textfield__node-title"
             );
        }

        /// <summary>
        /// 绘制标题按钮容器
        /// </summary>
        public virtual void DrawTitleButtonContainer()
        {
        }

        /// <summary>
        /// 绘制顶部容器
        /// </summary>
        public virtual void DrawTopContainer()
        {
        }

        /// <summary>
        /// 绘制输入容器
        /// </summary>
        public virtual void DrawInputContainer()
        {
            // 创建输入端口
            input = this.CreatePort("上个节点", Orientation.Horizontal, Direction.Input, Port.Capacity.Multi);
            inputContainer.Add(input);
        }

        /// <summary>
        /// 绘制输出容器
        /// </summary>
        public virtual void DrawOutputContainer()
        {
            // 遍历选项视图列表，创建对应端口
            for (int i = 0; i < ChoiceDatas.Count; i++)
            {
                ChoiceData choiceData = ChoiceDatas[i];
                output = this.CreatePort(choiceData.Text);
                output.userData = choiceData;
                outputContainer.Add(output);
            }
        }

        /// <summary>
        /// 绘制扩展容器
        /// </summary>
        public virtual void DrawExtensionContainer()
        {
            // 创建自定义容器
            customDataContainer = new();
            // 创建折叠组
            foldout = ElementUtility.CreateFoldout("节点内容");
            // 创建备注输入框
            TextField tfdNote = ElementUtility.CreateTextArea(Note, null, callback =>
            {
                Note = callback.newValue;
            });

            // 放置UI元素
            foldout.Add(tfdNote);
            customDataContainer.Add(foldout);
            extensionContainer.Add(customDataContainer);

            // 添加USS类名
            customDataContainer.AddClasses
            (
                "node__custom-data-container"
            );
            tfdNote.AddClasses
            (
                "textfield",
                "textfield__quote",
                "foldout-item"
            );

            // 此方法必须调用
            RefreshExpandedState();
        }

        #endregion


        #region 实用方法

        /// <summary>
        /// 获取节点数据
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns>节点数据</returns>
        public virtual NodeData GetNodeData()
        {
            List<ChoiceData> choiceDatas = DataUtility.CloneChoiceDatas(ChoiceDatas);

            NodeData nodeData = new()
            {
                GUID = GUID,
                Type = Type,
                Position = GetPosition().position,
                Title = Title,
                Note = Note,
                ChoiceDatas = choiceDatas,
                GroupID = Group?.ID,
            };

            return nodeData;
        }

        /// <summary>
        /// 断开所有连接
        /// </summary>
        public void DisconnectedAllPorts()
        {
            DisconnectedInputPorts();
            DisconnectedOutputPorts();
        }

        /// <summary>
        /// 断开输入连接
        /// </summary>
        private void DisconnectedInputPorts()
        {
            DisconnectPorts(inputContainer);
        }

        /// <summary>
        /// 断开输出连接
        /// </summary>
        private void DisconnectedOutputPorts()
        {
            DisconnectPorts(outputContainer);
        }

        /// <summary>
        /// 断开目标端口连接
        /// </summary>
        /// <param name="container">容器</param>
        private void DisconnectPorts(VisualElement container)
        {
            foreach (Port port in container.Children())
            {
                if (port.connected)
                {
                    graphView.DeleteEdges(port.connections.ToList());
                }
            }
        }

        /// <summary>
        /// 断开目标端口连接
        /// </summary>
        protected void DisconnectPort(Port port)
        {
            if (port.connected)
            {
                graphView.DeleteEdges(port.connections.ToList());
            }
        }

        /// <summary>
        /// 是否有连接节点
        /// </summary>
        public bool HasAnyConnection()
        {
            return HasInputConnection() || HasOutputConnection();
        }

        /// <summary>
        /// 是否有上行连接节点
        /// </summary>
        public bool HasInputConnection()
        {
            if (inputContainer.childCount == 0)
            {
                return false;
            }

            Port port = (Port)inputContainer.Children().First();
            return port.connected;
        }

        /// <summary>
        /// 是否有下行连接节点
        /// </summary>
        public bool HasOutputConnection()
        {
            if (outputContainer.childCount == 0)
            {
                return false;
            }

            foreach (Port port in outputContainer.Children().ToList())
            {
                if (port.connected)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 设置错误样式
        /// </summary>
        /// <param name="color"></param>
        public void SetErrorStyle()
        {
            titleContainer.AddClasses
            (
                "error-border"
            );
        }

        /// <summary>
        /// 重置样式
        /// </summary>
        public void ResetStyle()
        {
            titleContainer.RemoveFromClassList("error-border");
        }

        #endregion
    }
}