using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XNode;
using RPGCreationKit.BehaviourTree;
using UnityEditor;

namespace RPGCreationKit.BehaviourTree
{
    [CreateNodeMenu("RPGCK_BehaviourTree/Random Selector", order = 1)]
    [System.Serializable]
    public class RandomSelectorNode : BTNode
    {
        [Output(ShowBackingValue.Never, ConnectionType.Multiple, TypeConstraint.None)] public BTNode outputs;

        // A list of indexes of every child task. This list is used by the Fischer-Yates shuffle algorithm.
        private List<int> possibilities = new List<int>();
        // The random child index execution order.
        private Stack<int> orderStack = new Stack<int>();

        public BTNode nodeExecuting;

        // Use this for initialization
        protected override void Init()
        {
            base.Init();
        }

        [ContextMenu("Work")]
        public override void ReorderChild()
        {
            List<NodePort> endPorts = GetOutputPort("outputs").GetConnections();

            List<BTNode> nodes = new List<BTNode>();

            for (int i = 0; i < endPorts.Count; i++)
                nodes.Add((endPorts[i].node as BTNode));

            nodes.Sort(SortInGraphYPos);

            for (int i = 0; i < nodes.Count; i++)
            {
                BTNode btNode = nodes[i] as BTNode;
                btNode.indexInSequence = i;
            }
        }

        // Return the correct value of an output port when requested
        public override object GetValue(NodePort port)
        {
            return null; // Replace this
        }

        public override NodeState Execute()
        {
            if (m_NodeState == NodeState.Success || m_NodeState == NodeState.Failure)
                if (hasEvaluated == true)
                    return m_NodeState;

            if (!STARTED)
                OnStart();

            List<NodePort> endPorts = GetOutputPort("outputs").GetConnections();

            if (nodeExecuting != null && nodeExecuting.m_NodeState != NodeState.Failure && nodeExecuting.m_NodeState != NodeState.Success)
            {
                switch (nodeExecuting.Execute())
                {
                    case NodeState.Failure:
                        break;

                    case NodeState.Success:
                        m_NodeState = NodeState.Success;
                        hasEvaluated = true;
                        return m_NodeState;

                    case NodeState.Running:
                        m_NodeState = NodeState.Running;
                        return m_NodeState;

                    default:
                        break;
                }
            }
            else if(nodeExecuting != null && nodeExecuting.m_NodeState == NodeState.Success)
            {
                m_NodeState = NodeState.Success;
                hasEvaluated = true;
                return m_NodeState;
            }

            // Pop the top index from the stack and set the execution status.
            while (orderStack.Count > 0)
            {
                nodeExecuting = (endPorts[orderStack.Pop()].node as BTNode);
                return NodeState.Running;
            }

            // If we reach this far we've failed
            m_NodeState = NodeState.Failure;
            hasEvaluated = true;
            return m_NodeState;
        }

        public override void ReEvaluate()
        {
            if (m_NodeState != NodeState.Running)
            {
                base.ReEvaluate();
                hasEvaluated = false;
                nodeExecuting = null;
                OnStart();

                List<NodePort> endPorts = GetOutputPort("outputs").GetConnections();

                for (int i = 0; i < endPorts.Count; i++)
                {
                    (endPorts[i].node as BTNode).ReEvaluate();
                }
            }
        }

        public override void OnCreateConnection(NodePort from, NodePort to)
        {
            base.OnCreateConnection(from, to);

#if UNITY_EDITOR
            EditorApplication.delayCall += ReorderChild;
#endif
        }

        public override void OnRemoveConnection(NodePort port)
        {
            base.OnRemoveConnection(port);

            if (port.fieldName == "input")
                indexInSequence = -1;
            else if (port.fieldName == "outputs")
            {
#if UNITY_EDITOR
                EditorApplication.delayCall += ReorderChild;
#endif
            }
        }

        public override void OnStart()
        {
            possibilities.Clear();
            for (int i = 0; i < GetOutputPort("outputs").GetConnections().Count; ++i)
                possibilities.Add(i);

            // Randomize the indecies
            RandomizeIndicies();

            STARTED = true;
        }

        private void RandomizeIndicies()
        {
            orderStack.Clear();
            // Use Fischer-Yates shuffle to randomize the child index order.
            for (int i = possibilities.Count; i > 0; --i)
            {
                int j = Random.Range(0, i);
                int index = possibilities[j];
                orderStack.Push(index);
                possibilities[j] = possibilities[i - 1];
                possibilities[i - 1] = index;
            }
        }
    }
}