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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace MyBT {

#if UNITY_EDITOR
    using UnityEditor;
    using System;
    using System.Reflection;
    using System.Linq;

    [CustomEditor(typeof(TaskController)), InitializeOnLoadAttribute]
    public class TaskControllerInspector : Editor {

        public bool treeTaskViewer = false;

        public CodeInspector _codeInspector;

        #region editor gui repaint
        private int minUpdatesPerSecond = 1;
        private int maxUpdatesPerSecond = 10;

        private float myLastUiUpdateTime = 0f;
        private float nextUpdateDelay = 1.0f;

        // this is called from TaskController every time a tick happens
        public void InspectorRedrawCallback(bool forceUpdate = false) {
            //Debug.Log($"InspectorRedrawCallback {forceUpdate}");
            TaskController _taskController = (TaskController)target;

            // reduce to min time, when chages in ui
            bool hasUiUpdate = ((TaskController)target).UiChangedThisFrame(); // LastUiChangeTimeEquals(_taskController.lastTickTime);
            if (hasUiUpdate) {
                nextUpdateDelay = 1.0f / maxUpdatesPerSecond;
            }

            if ((TaskController.currentUiTime > (myLastUiUpdateTime + nextUpdateDelay)) || forceUpdate) {
                ForceInspectorRedraw();
            }
        }

        public void InspectorRegenerateCallback(bool forceUpdate = false) {
            //Debug.Log($"InspectorRegenerateCallback {forceUpdate}");
            TaskController _taskController = (TaskController)target;

            _taskController.RegenerateBehaviourTreeIfRequired(forceUpdate);
            _codeInspector.RegenerateNodeLineNumberListIfRequired(forceUpdate);

            ForceInspectorRedraw();
        }

        public void ForceInspectorRedraw () {
            //Debug.Log($"TaskControllerInspector.ForceInspectorRedraw: ui update delta {(TaskController.currentUiTime - myLastUiUpdateTime)}");

            myLastUiUpdateTime = TaskController.currentUiTime;
            if (this != null) {
                EditorUtility.SetDirty(this);
            }
            if (target != null) {
                EditorUtility.SetDirty(target);
            }
            nextUpdateDelay = 1.0f / minUpdatesPerSecond;

            Repaint();
        }

        public override bool RequiresConstantRepaint() {
            return false;
        }
        #endregion

        //TaskControllerInspector() {
        //    //Debug.Log($"TaskControllerInspector.Constructor {this.GetInstanceID()}");
        //    //EditorApplication.playModeStateChanged += LogPlayModeState;
        //}

        //private static void LogPlayModeState(PlayModeStateChange state) {
        //    switch (state) {
        //        // start playing
        //        case PlayModeStateChange.ExitingEditMode:
        //            Debug.Log("ExitingEditMode");
        //            break;
        //        case PlayModeStateChange.EnteredPlayMode:
        //            Debug.Log("EnteredPlayMode");
        //            break;

        //        // stopping playing
        //        case PlayModeStateChange.ExitingPlayMode:
        //            Debug.Log("ExitingPlayMode");
        //            break;
        //        case PlayModeStateChange.EnteredEditMode:
        //            Debug.Log("EnteredEditMode");
        //            break;
        //    }
        //}

        // http://www.voidbred.com/blog/2014/12/auto-updating-custom-inspectors-for-unity/
        public void SetInspectorCallbacks () {
#if true
            TaskController _taskController = (TaskController)target;
            _taskController.InspectorRedraw = (val) => this.InspectorRedrawCallback(val);
            _taskController.InspectorRegenerate = (val) => this.InspectorRegenerateCallback(val);
#endif
        }

        public void OnEnable() {
            TaskController _taskController = (TaskController)target;
            Debug.Log($"TaskControllerInspector.OnEnable {this.GetInstanceID()} {_taskController.name}");

            // call this once to preload it (preventing error when called not in main thread)
            GUISkin myBtGuiSkin = MyBtResources.myBtGuiSkin;

            if (_codeInspector == null) {
                _codeInspector = ScriptableObject.CreateInstance<CodeInspector>();
                _codeInspector.Init((TaskController)target);
            }

            // fixed version of generating the code after stopping the playmode
            //_taskController.ClearBinding();
            //_codeInspector.ResetNodeLinenumberList();

            EditorApplication.playModeStateChanged += PlayModeStateChanged;

            SetInspectorCallbacks();

            // update if required (forced = false)
            InspectorRegenerateCallback(false);
        }

        public void OnDisable() {
            //Debug.Log($"TaskControllerInspector.OnDisable {this.GetInstanceID()}");
            if (_codeInspector != null) {
                if (Application.isPlaying) {
                    Destroy(_codeInspector);
                } else {
                    DestroyImmediate(_codeInspector);
                }
            }
            _codeInspector = null;

            //if (target != null) { Debug.Log($"OnDisable {((TaskController)target).gameObject.name}"); }
            EditorApplication.playModeStateChanged -= PlayModeStateChanged;
        }

        // this is called when selecting the object
        //public TaskControllerInspector() {
        //    Debug.Log($"TaskControllerInspector");
        //    EditorApplication.playModeStateChanged += PlayModeStateChanged;
        //}
        //~TaskControllerInspector() {
        //    Debug.Log($"~TaskControllerInspector");
        //    EditorApplication.playModeStateChanged -= PlayModeStateChanged;
        //}  


#region generating and clearing node line dictionary
        private void PlayModeStateChanged(PlayModeStateChange playModeStateChange) {
            Debug.Log($"TaskControllerInspector.PlayModeStateChanged {playModeStateChange}");
            // lets rely on awake instead for rebinding of nodes
            //_codeInspector.ResetNodeLinenumberList();
            switch (playModeStateChange) {
                case PlayModeStateChange.EnteredEditMode:
                case PlayModeStateChange.EnteredPlayMode:
                    InspectorRegenerateCallback();
                    break;
                case PlayModeStateChange.ExitingEditMode:
                case PlayModeStateChange.ExitingPlayMode:
                    break;
            }
        }

        [SerializeField]
        FileHash btFileHash;

        public override void OnInspectorGUI() {
            //Debug.Log("TaskControllerInspector.OnInspectorGUI");

            TaskController _taskController = (TaskController)target;

            // Testing
            // if (GUILayout.Button("Test")) {
            //     _taskController.destroyNextFrame = true;
            // }

            // TODO: check, if this is needed here as well?
            // connection to TaskController calling the repaint function of this inspector
            SetInspectorCallbacks();
            // end of connection to TaskController

            bool tick = false;
            bool regenerateRequest = false;
            bool restartRequest = false;

            DrawCustomInspector(_taskController, ref tick, ref regenerateRequest, ref restartRequest);

            // if manual reset requested
            if (regenerateRequest) {
                if (_taskController.runtimeLogging) Debug.Log("TaskControllerInspector.OnInspectorGUI: reset");
                // clear
                _codeInspector.ResetNodeLinenumberList();
                _taskController.FullReset();
                // generate
                _taskController.FullGenerate(true);
                _codeInspector.GenerateNodeLineNumberList();
            }

            if (restartRequest) {
                if (_taskController.runtimeLogging) Debug.Log("TaskControllerInspector.OnInspectorGUI: restart");
                _taskController.Restart(true);
            }

            if (tick) {
                if (_taskController.runtimeLogging) Debug.Log("TaskControllerInspector.OnInspectorGUI: tick");
                // because this can be run in edit (non playing) mode, we check if the bindings are setup in depth
                _taskController.VerifyBindings();
                _codeInspector.CheckLineNumberHashOutdated();

                _taskController.TickUpdate();
            }

            if ((_codeInspector != null) && !_codeInspector.nodeLineNumberListError) {
                _codeInspector.DrawCodeInspector();
            }
        }

        Rect stateIconRect;

        public void StateButton (string label, bool state, int textWidth=20) {
            EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);

            stateIconRect = GUILayoutUtility.GetRect(GUIContent.none, new GUIStyle(), new GUILayoutOption[] {GUILayout.Width(16)});
            stateIconRect.height += 2;
            stateIconRect.y += 1;
            stateIconRect.x += 1;

            if (state) {
                GUI.color = Color.green;
                GUI.DrawTexture(stateIconRect, MyBtResources.trueIcon, ScaleMode.ScaleToFit);
            } else {
                GUI.color = Color.red;
                GUI.DrawTexture(stateIconRect, MyBtResources.falseIcon, ScaleMode.ScaleToFit);
            }
            GUI.color = Color.white;

            GUI.color = Color.white;
            EditorGUILayout.LabelField(label, GUILayout.MinWidth(textWidth));

            EditorGUILayout.EndHorizontal();
        }

        bool displayUpdateInfo = false;
        public void DrawCustomInspector (TaskController _taskController, ref bool tick, ref bool regenerateRequest, ref bool restartRequest) {
            GUI.enabled = false;
            EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Script"), true, new GUILayoutOption[0]);
            GUI.enabled = true;

            bool btSettingsChanged = false;

            EditorGUILayout.Space();
            
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("Behaviour Tree Script Files:");
            if (GUILayout.Button("+", GUILayout.Width(40))) {
                _taskController.taskScripts.Add(null);
                btSettingsChanged |= true;
            }
            EditorGUILayout.EndHorizontal();

            for (int i=0; i<_taskController.taskScripts.Count; i++) {
                TextAsset txtAss = _taskController.taskScripts[i];
                EditorGUILayout.BeginHorizontal();
                TextAsset newTxtAss = EditorGUILayout.ObjectField(txtAss, typeof(UnityEngine.TextAsset), false) as UnityEngine.TextAsset;
                if (newTxtAss != txtAss) {
                    _taskController.taskScripts[i] = newTxtAss;
                    regenerateRequest |= true;
                    btSettingsChanged |= true;
                }
                if (GUILayout.Button("-", GUILayout.Width(40))) {
                    _taskController.taskScripts.RemoveAt(i);
                    btSettingsChanged |= true;
                    break;
                }
                EditorGUILayout.EndHorizontal();
            }
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
            EditorGUILayout.LabelField("Configuration:");

            EditorGUILayout.BeginHorizontal();
            bool newRuntimeLogging = GUILayout.Toggle(_taskController.runtimeLogging, "Runtime Debug");
            if (newRuntimeLogging != _taskController.runtimeLogging) {
                _taskController.runtimeLogging = newRuntimeLogging;
                btSettingsChanged |= true;
            }

            bool newDebugLog = GUILayout.Toggle(_taskController.generatorLogging, "Generation Debug");
            if (newDebugLog != _taskController.generatorLogging){
                _taskController.generatorLogging = newDebugLog;
                btSettingsChanged |= true;
            }
            EditorGUILayout.EndHorizontal();

            //EditorGUILayout.BeginHorizontal();
            //bool newResetDataOnCodeChange = GUILayout.Toggle(_taskController.resetDataOnCodeChange, "Reset on Method Change");
            //if (newResetDataOnCodeChange != _taskController.resetDataOnCodeChange){
            //    _taskController.resetDataOnCodeChange = newResetDataOnCodeChange;
            //    btSettingsChanged |= true;
            //}
            //bool newAutoGen = GUILayout.Toggle(_taskController.runtimeAutomaticRegenerate, "Code Regeneration (at runtime)");
            //if (newAutoGen != _taskController.runtimeAutomaticRegenerate) {
            //    _taskController.runtimeAutomaticRegenerate = newAutoGen;
            //    btSettingsChanged |= true;
            //}
            //EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            TickUpdateMode newTickMode = (TickUpdateMode)EditorGUILayout.EnumPopup(new GUIContent("Update Mode", "When is the BT Executed"), _taskController.tickMode);
            if (newTickMode != _taskController.tickMode) {
                _taskController.tickMode = newTickMode;
                btSettingsChanged |= true;
            }

            //GUI.color = lineNode.debugInternalActive ? Color.red : Color.white;
            displayUpdateInfo = EditorGUILayout.Toggle(displayUpdateInfo, MyBtResources.helpButtonGuiStyle, GUILayout.Width(MyBtResources.defaultGuiIconWidth), GUILayout.Height(MyBtResources.defaultGuiHeight));
            EditorGUILayout.EndHorizontal();

            if (displayUpdateInfo) {
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                GUILayout.Label(
                    "Update in Update or LateUpdate.\n" +
                    "Standard: Run One Step\n" +
                    "Delayed: Run One Step with Interval in Seconds\n" +
                    "Maximum Time: Run Steps for a Maximum Time\n" +
                    "Fullrun: Run Steps until Root Finishes in One Frame");
                EditorGUILayout.EndVertical();
            }

            switch (_taskController.tickMode) {
                case TickUpdateMode.Update:
                case TickUpdateMode.LateUpdate:
                    break;
                case TickUpdateMode.DelayedUpdate:
                case TickUpdateMode.DelayedLateUpdate:
                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.LabelField("Tick Update Interval");
                    float newTickDelay = EditorGUILayout.FloatField(_taskController.tickDelay);
                    if (newTickDelay != _taskController.tickDelay) {
                        _taskController.tickDelay = newTickDelay;
                        btSettingsChanged |= true;
                    }
                    EditorGUILayout.EndHorizontal();
                    break;
                case TickUpdateMode.MaximumTimeLateUpdate:
                case TickUpdateMode.MaximumTimeUpdate:
                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.LabelField("Maximum Runtime");
                    float newMaximumRuntime = EditorGUILayout.FloatField(_taskController.maximumRuntime);
                    if (newMaximumRuntime != _taskController.maximumRuntime) {
                        _taskController.maximumRuntime = newMaximumRuntime;
                        btSettingsChanged |= true;
                    }
                    EditorGUILayout.EndHorizontal();
                    break;
                case TickUpdateMode.Manual:
                    break;
            }

            EditorGUILayout.BeginHorizontal();
            restartRequest |= GUILayout.Button("Restart", GUILayout.Width(80));
            regenerateRequest |= GUILayout.Button("Regenerate", GUILayout.Width(80));
            EditorGUILayout.Space();
            GUI.enabled = (_taskController.tickMode == TickUpdateMode.Manual) || !Application.isPlaying;
            tick |= GUILayout.Button("Manual Tick", GUILayout.Width(80));

            if (GUILayout.Button("Clear Console", GUILayout.Width(80))) {
                ClearLogConsole();
            }
            GUI.enabled = true;
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.Space();
            EditorGUILayout.EndVertical();

            if (_taskController.runtimeLogging) {
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);

                if (GUILayout.Button("Re-Draw", GUILayout.Width(80))) {
                    _taskController.SetUiUpdate();
                }

                bool newOverrideLogStringDisplay = EditorGUILayout.ToggleLeft("overrideLogStringDisplay (default off)", _taskController.overrideLogStringDisplay, GUILayout.MinWidth(60));
                if (newOverrideLogStringDisplay != _taskController.overrideLogStringDisplay) {
                    _taskController.overrideLogStringDisplay = newOverrideLogStringDisplay;
                    btSettingsChanged |= true;
                }

                bool newOverrideDebugChangesActive = EditorGUILayout.ToggleLeft("overrideDebugChangesActive (default off)", _taskController.overrideDebugChangesActive, GUILayout.MinWidth(60));
                if (newOverrideDebugChangesActive != _taskController.overrideDebugChangesActive) {
                    _taskController.overrideDebugChangesActive = newOverrideDebugChangesActive;
                    btSettingsChanged |= true;
                }

                bool newOverrideDebugInternalActive = EditorGUILayout.ToggleLeft("overrideDebugInternalActive (default off)", _taskController.overrideDebugInternalActive, GUILayout.MinWidth(60));
                if (newOverrideDebugInternalActive != _taskController.overrideDebugInternalActive) {
                    _taskController.overrideDebugInternalActive = newOverrideDebugInternalActive;
                    btSettingsChanged |= true;
                }

                EditorGUILayout.EndVertical();
            }

            if (!_taskController.internalAutomaticRegenerate || _taskController.generatorLogging) {
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);

                GUILayout.BeginHorizontal();
                GUI.color = Color.white;
                int numberOfNodes = _taskController.nodeCount;
                GUILayout.Label($"{numberOfNodes} nodes Generated");
                // GUILayout.TextArea(NodeGenerator.GeneratedSize(_taskController.treeRootNodes).ToString(), GUILayout.Width(30));
                if (_taskController.generationError || _taskController.bindingError) {
                    GUI.color = Color.red;
                }
                //regenerateRequest |= GUILayout.Button("Re-Generate", GUILayout.Width(80));
                //btSettingsChanged |= regenerateRequest;
                GUILayout.EndHorizontal();

                bool newInternalAutomaticRegenerate = EditorGUILayout.ToggleLeft("internalAutomaticRegenerate (default on)", _taskController.internalAutomaticRegenerate, GUILayout.MinWidth(60));
                if (newInternalAutomaticRegenerate != _taskController.internalAutomaticRegenerate) {
                    _taskController.internalAutomaticRegenerate = newInternalAutomaticRegenerate;
                    btSettingsChanged |= true;
                }

                EditorGUILayout.EndVertical();
            }

            if (_taskController.generatorLogging) {
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);

                float inspectorWidth = 0;
                inspectorWidth = EditorGUIUtility.currentViewWidth - 60;
                float halfWidth = (inspectorWidth - 1) / 2 - 2;
                float thirdWidth = 20;

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("BtScriptChangeDetected", GUILayout.MinWidth(thirdWidth))) {
                    _taskController.BtScriptChangeDetected();
                    btSettingsChanged |= true;
                }
                if (GUILayout.Button("BtMethodChanged", GUILayout.MinWidth(thirdWidth))) {
                    _taskController.ClearBindingsAndScanAndBindMethods();
                    btSettingsChanged |= true;
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("ResetNodesAndTokens", GUILayout.MinWidth(thirdWidth))) {
                    _taskController.ResetNodesAndTokens();
                    btSettingsChanged |= true;
                }
                if (GUILayout.Button("GenerateTokensAndNodes", GUILayout.MinWidth(thirdWidth))) {
                    _taskController.GenerateTokensAndNodes(true);
                    btSettingsChanged |= true;
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("ClearBinding", GUILayout.MinWidth(thirdWidth))) {
                    _taskController.ClearBinding();
                    btSettingsChanged |= true;
                }
                if (GUILayout.Button("Bind", GUILayout.MinWidth(thirdWidth))) {
                    _taskController.ScanAndBindMethods(false);
                    btSettingsChanged |= true;
                }
                if (GUILayout.Button("Bind Force", GUILayout.MinWidth(thirdWidth))) {
                    _taskController.ScanAndBindMethods(true);
                    btSettingsChanged |= true;
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("ResetNodeLinenumberList", GUILayout.MinWidth(thirdWidth))) {
                    _codeInspector.ResetNodeLinenumberList();
                    btSettingsChanged |= true;
                }
                if (GUILayout.Button("GenerateNodeLineNumberList", GUILayout.MinWidth(thirdWidth))) {
                    _codeInspector.GenerateNodeLineNumberList();
                    btSettingsChanged |= true;
                }
                EditorGUILayout.EndHorizontal();
                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("Set TaskController", GUILayout.MinWidth(thirdWidth))) {
                    _taskController.SetTaskController();
                    btSettingsChanged |= true;
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                NodeGenerator.debugActive = EditorGUILayout.ToggleLeft("NodeGenerator debugActive", NodeGenerator.debugActive, GUILayout.MinWidth(60));
                MethodScanner.debugActive = EditorGUILayout.ToggleLeft("MethodScanner debugActive", MethodScanner.debugActive, GUILayout.MinWidth(60));
                EditorGUILayout.EndHorizontal();
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField($"MethodBinder:", GUILayout.MaxWidth(80));
                MethodBinder.debug = EditorGUILayout.ToggleLeft("debug", MethodBinder.debug, GUILayout.MinWidth(40));
                MethodBinder.debugIsBound = EditorGUILayout.ToggleLeft("debugIsBound", MethodBinder.debugIsBound, GUILayout.MinWidth(60));
                MethodBinder.debugRunTreeBinding = EditorGUILayout.ToggleLeft("debugRunTreeBinding", MethodBinder.debugRunTreeBinding, GUILayout.MinWidth(60));
                MethodBinder.debugActionBinding = EditorGUILayout.ToggleLeft("debugActionBinding", MethodBinder.debugActionBinding, GUILayout.MinWidth(60));
                EditorGUILayout.EndHorizontal();

                int hashMinWidth = 60;
                EditorGUILayout.BeginHorizontal();
                StateButton("Has Script", _taskController.hasScript);
                StateButton("Has TickEnumerator", (_taskController.tickEnumerator != null));
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField($"Script Count: {_taskController.taskScripts.Count}", GUILayout.MinWidth(hashMinWidth));
                GUILayout.FlexibleSpace();
                EditorGUILayout.LabelField($"{(_taskController.textAssetHashes!=null?_taskController.textAssetHashes.ToString():"undefined")}", GUILayout.MinWidth(hashMinWidth));
                EditorGUILayout.EndHorizontal();

                // EditorGUILayout.BeginHorizontal();
                // StateButton("Tokens Generated", _taskController.tokensGenerated);
                // StateButton("Nodes Generated", _taskController.nodesGenerated);
                // StateButton("isGenerated", _taskController.isGenerated);
                // EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                StateButton("Generation Succeeded", !_taskController.generationError);
                StateButton("Generation Updated", !_taskController.CheckGenerationOutdated(false));
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                int count = _taskController.treeRootNodes!=null?_taskController.treeRootNodes.Sum(o => o.Count):0;
                EditorGUILayout.LabelField($"TreeRootNode Count: {count}", GUILayout.MinWidth(hashMinWidth));
                GUILayout.FlexibleSpace();
                EditorGUILayout.LabelField($"{(_taskController.generatedCodeBtFileHashes!=null?_taskController.generatedCodeBtFileHashes.ToString():"undefined")}", GUILayout.MinWidth(hashMinWidth));
                EditorGUILayout.EndHorizontal();

                // EditorGUILayout.BeginHorizontal();
                // StateButton("methodsScanned", _taskController.methodsScanned);
                // StateButton("methodsBound", _taskController.methodsBound);
                // StateButton("isBound", _taskController.methodsBound);
                // EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                StateButton("Binding Succeeded", !_taskController.bindingError);
                StateButton("Binding Updated", !_taskController.CheckBindingOutdated(false));
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField($"Method Count: {(_taskController.methods != null ? _taskController.methods.Count : 0)}", GUILayout.MinWidth(hashMinWidth));
                GUILayout.FlexibleSpace();
                EditorGUILayout.LabelField($"{_taskController.boundCodeBtFileHashes.ToString()}", GUILayout.MinWidth(hashMinWidth));
                EditorGUILayout.EndHorizontal();


                EditorGUILayout.BeginHorizontal();
                StateButton("NodeLineNumberList Generated", _codeInspector.nodeLineNumberListGenerated);
                StateButton("Has TaskController", _codeInspector.hasTaskControllerDefined);
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                StateButton("Code Succeeded", !_codeInspector.nodeLineNumberListError);
                StateButton("LineNumberHash Updated", !_codeInspector.CheckLineNumberHashOutdated(false));
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField($"nodeLineCount: {_codeInspector.nodeLineCount}", GUILayout.MinWidth(hashMinWidth));
                GUILayout.FlexibleSpace();
                EditorGUILayout.LabelField($"{(_codeInspector.lineNumberListBtFileHashes !=null? _codeInspector.lineNumberListBtFileHashes.ToString():"undefined")}", GUILayout.MinWidth(hashMinWidth));
                EditorGUILayout.EndHorizontal();

                // SerializedObject sObj = new SerializedObject(_taskController);
                SerializedProperty sProp = _taskController.treeRootNodesAsSerializedProperty;
                if (sProp != null) {
                    EditorGUILayout.PropertyField(sProp, true);
                }
                else {
                    EditorGUILayout.LabelField($"treeRootNodes missing", GUILayout.MinWidth(hashMinWidth));
                }

                SerializedProperty tProp = _codeInspector.openNodeLinesAsSerializedProperty;
                if (tProp != null) {
                    EditorGUILayout.PropertyField(tProp, true);
                }
                else {
                    EditorGUILayout.LabelField($"openNodeLinesAsSerializedProperty missing", GUILayout.MinWidth(hashMinWidth));
                }

                EditorGUILayout.EndVertical();
            }

            if (btSettingsChanged) {
                _codeInspector.SetDirty();
            }
        }
#endregion

        static MethodInfo _clearConsoleMethod;
        static MethodInfo clearConsoleMethod {
            get {
                if (_clearConsoleMethod == null) {
                    Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
                    Type logEntries = assembly.GetType("UnityEditor.LogEntries");
                    _clearConsoleMethod = logEntries.GetMethod("Clear");
                }
                return _clearConsoleMethod;
            }
        }
        public static void ClearLogConsole() {
            clearConsoleMethod.Invoke(new object(), null);
        }

        public static void GameViewRepaint () {
            System.Reflection.Assembly assembly = typeof(UnityEditor.EditorWindow).Assembly;
            System.Type type = assembly.GetType("UnityEditor.GameView");
            EditorWindow gameview = EditorWindow.GetWindow(type);
            gameview.Repaint();
        }
    }
#endif
        }