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

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

namespace MyBT {
    [XmlInclude(typeof(DecoratorNode))]
    public class TreeNode : Node {
        // the "parent" run tree node, must be null in root treenode
        [SerializeField]
        public string treeNodeName;

        public void Init(int _nodeDepth, Token[] _tokens, string _name) {
            base.Init(_nodeDepth, _tokens);
            treeNodeName = _name;
        }

        public override IEnumerable<NodeResult> Tick(NodeRuntimeData parentNodeRuntimeData, int recursions) {
            if (debugInternalActive) {
                Debug.Log(NodeLogger($"Tick", $""));
            }

            // only work with 1 child && failsave in case we have an infinite loop
            if ((children.Count == 1) && (recursions < recursionLimit)) {
                using (NodeRuntimeData myNodeRuntimeData = new NodeRuntimeData(this, parentNodeRuntimeData)) {
                    myNodeRuntimeData.tickExecuteEnumerator = children[0].Tick(myNodeRuntimeData, recursions + 1).GetEnumerator();
                    myNodeRuntimeData.taskHasChanges = base.PreTick(NodeExecute.Run, myNodeRuntimeData);
                    // the second check of myNodeRuntimeData.tickExecuteEnumerator is intended, as movenext may destroy it
                    while ((myNodeRuntimeData.tickExecuteEnumerator != null) && myNodeRuntimeData.tickExecuteEnumerator.MoveNext()) {
                        // if (myNodeRuntimeData == null) {
                        //     Debug.LogError(NodeLogger("RunTreeNode.Tick", $"myNodeRuntimeData is null"));
                        // }
                        if (myNodeRuntimeData.tickExecuteEnumerator == null) {
                            Debug.LogError(NodeLogger("RunTreeNode.Tick", $"myNodeRuntimeData.tickExecuteEnumerator is null, movenext destroyed it"));
                            break;
                        }
                        myNodeRuntimeData.nodeResult = myNodeRuntimeData.tickExecuteEnumerator.Current;
                        if (debugInternalActive) Debug.Log(NodeLogger($"Tick.before", $"nodeResult {myNodeRuntimeData.nodeResult}"));
                        // return the same result as the child
                        myNodeRuntimeData.taskHasChanges |= base.PostTick(NodeExecute.Run, myNodeRuntimeData);
                        if (myNodeRuntimeData.taskHasChanges && taskController) {
                            if (debugChangesActive) Debug.Log(NodeLogger($"Tick", $"changed to {myNodeRuntimeData.nodeResult}"));
                            taskController.SetUiUpdate();
                        }
                        yield return myNodeRuntimeData.nodeResult;

                        // this should not be run again if it isnt in runnning mode anymore
                        if (myNodeRuntimeData.nodeState == NodeState.Running) {
                            if (debugChangesActive) Debug.Log(NodeLogger($"Tick.Cleanup", $"changed to {myNodeRuntimeData.nodeResult}"));
                            myNodeRuntimeData.taskHasChanges = PreTick(NodeExecute.Run, myNodeRuntimeData);
                        }
                        else {
                            if (debugChangesActive) Debug.Log(NodeLogger($"Tick.Cleanup", $"Testing, should be executed once after success of enumerator"));
                        }
                    }
                }
            }
            else {
                Debug.LogError(NodeLogger("RunTreeNode.Tick", $"recursions {recursions} childCount {children.Count}"));
                taskController.SetUiUpdate();
                yield return NodeResult.Error;
            }

            yield break;
        }

        public override string ToString() {
            return base.ToString() + " thisId: " + this.GetInstanceID();
        }

        public override Node Duplicate() {
            TreeNode tn = ScriptableObject.CreateInstance<TreeNode>();
            base.BaseDuplicate(tn);
            // TreeNode tn = base.Duplicate() as TreeNode;
            tn.treeNodeName = treeNodeName;
            return tn;
        }
    }
}