//============= Copyright (c) Ludic GmbH, All rights reserved. ==============
//
// Purpose: Part of the My Behaviour Tree Code
//
//=============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using UnityEngine;

namespace MyBT {
    [System.Serializable]
    public class NodeRuntimeData : IDisposable {
        [NonSerialized]
        protected Node node;

        [NonSerialized]
        protected NodeRuntimeData parentNodeRuntimeData;

        [NonSerialized]
        protected List<NodeRuntimeData> childNodeRuntimeData = new List<NodeRuntimeData>();

        [SerializeField]
        public string logString = "";

        [NonSerialized]
        public bool taskHasChanges = false;

        public IEnumerator<NodeResult> tickExecuteEnumerator = null;

        // only used for debugging, no actual function
        public int currentIndex = -1;

#if UNITY_EDITOR && false
        // debug version of result & state, slower
        [SerializeField]
        private NodeResult _nodeResult;
        public NodeResult nodeResult {
            get {
                return _nodeResult;
            }
            set {
                _nodeResult = value;
                if (node != null)
                    if (node.debugInternalActive)
                        Debug.Log(node.NodeLogger("nodeResultSet", $"set Node Result '{value}'"));
            }
        }

        [SerializeField]
        private NodeState _nodeState = NodeState.NotRunning;
        public NodeState nodeState {
            get {
                return _nodeState;
            }
            set {
                _nodeState = value;
                if (node != null)
                    if (node.debugInternalActive)
                        Debug.Log(node.NodeLogger("nodeStateSet", $"set Node State '{value}'"));
            }
        }
#else
        // more performant solution for runtime
        public NodeResult nodeResult;
        public NodeState nodeState = NodeState.NotRunning;
#endif
        public IEnumerator<NodeResult>[] subTickExecuteEnumerators = null;
        public NodeResult[] childResults;

        public NodeRuntimeData (Node _node, NodeRuntimeData _parentNodeRuntimeData) {
            node = _node;
            parentNodeRuntimeData = _parentNodeRuntimeData;
            if (node.debugChangesActive) UnityEngine.Debug.Log(node.NodeLogger("NodeRuntimeData.Constructor", $""));
            node.nodeRuntimeDataList.Add(this);
            // root has no parent
            if (parentNodeRuntimeData != null) { 
                parentNodeRuntimeData.childNodeRuntimeData.Add(this);
            }
        }

        ~NodeRuntimeData() {
            if (node.debugChangesActive) UnityEngine.Debug.Log(node.NodeLogger("NodeRuntimeData.Destructor", $""));
            Dispose(false);
        }

        public virtual void Destroy() {
            if ((node != null) && (node.debugInternalActive)) { Debug.Log($"NodeRuntimeData.Destroy {node}"); } else { }; // node can be null

            if (childNodeRuntimeData != null) { 
                for (int i= childNodeRuntimeData.Count-1; i>-1; i--) {
                    childNodeRuntimeData[i].Destroy();
                }
            }

            if (node != null) { 
                switch (node.GetType().ToString()) {
                    case "MyBT.RunTreeNode":
                        node.PreTick(NodeExecute.Abort, this);
                        //cn2.node.Execute(this, NodeExecute.Abort);
                        tickExecuteEnumerator.MoveNext();
                        if (node.debugInternalActive) Debug.Log(node.NodeLogger($"NodeRuntimeData.Destroy", $"childNode result {tickExecuteEnumerator.Current}"));
                        node.PostTick(NodeExecute.Abort, this);
                        break;
                    case "MyBT.ActionNode":
                        node.Execute(this, NodeExecute.Abort);
                        break;
                    case "MyBT.CompositeNode":
                        node.PreTick(NodeExecute.Abort, this);
                        if (tickExecuteEnumerator != null) {
                            tickExecuteEnumerator.MoveNext();
                        }
                        else if ((subTickExecuteEnumerators != null) && (subTickExecuteEnumerators.Length > 0)) {
                            for (int j = 0; j < subTickExecuteEnumerators.Length; j++) {
                                subTickExecuteEnumerators[j].MoveNext();
                            }
                        }
                        else {
                            //Debug.LogWarning($"{node} has no tick or subTick enumerator");
                        }
                        if (node.debugInternalActive) Debug.Log(node.NodeLogger($"NodeRuntimeData.Destroy", $""));
                        node.PostTick(NodeExecute.Abort, this);
                        break;
                    default:
                        break;
                }
                node.nodeRuntimeDataList.Remove(this);
            }

            tickExecuteEnumerator = null;
            subTickExecuteEnumerators = null;
            nodeResult = NodeResult.Undefined;
            logString = "";
            parentNodeRuntimeData = null;
            childNodeRuntimeData = new List<NodeRuntimeData>();
            node = null;
        }

        public virtual void Clear() {
            logString = "";
        }

        // Flag: Has Dispose already been called?
        [System.NonSerialized]
        bool disposed = false;

        // Public implementation of Dispose pattern callable by consumers.
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // Protected implementation of Dispose pattern.
        protected virtual void Dispose(bool disposing) {
            if (disposed)
                return;

            if (disposing) {
                // Free any other managed objects here.
                Destroy();
            }

            // Free any unmanaged objects here.
            //

            disposed = true;
        }

    }
}