///An editor for BBParameter type to let user choose either a constant value or link to a Blackboard Variable.
        public static BBParameter ParameterField(string name, BBParameter bbParam, bool blackboardOnly = false, UnityEngine.Object context = null)
        {
            var info = new InspectedFieldInfo();

            info.unityObjectContext = context;
            return(ParameterField(string.IsNullOrEmpty(name) ? GUIContent.none : EditorUtils.GetTempContent(name), bbParam, blackboardOnly, false, info));
        }
        ///An editor for BBParameter type to let user choose either a constant value or link to a Blackboard Variable.
        public static BBParameter ParameterField(GUIContent content, BBParameter bbParam, bool blackboardOnly = false, bool required = false, InspectedFieldInfo info = default(InspectedFieldInfo))
        {
            if (bbParam == null)
            {
                EditorGUILayout.LabelField(content, EditorUtils.GetTempContent("BBParameter is null"));
                return(null);
            }

            //ensure we use blackboard if 'blackboardOnly'
            if (blackboardOnly && !bbParam.useBlackboard)
            {
                bbParam.useBlackboard = true;
            }

            GUILayout.BeginVertical();
            {
                GUILayout.BeginHorizontal();
                {
                    if (bbParam.useBlackboard)
                    {
                        ParameterDropDown(content, bbParam);
                    }
                    else
                    {
                        //we mutate inspected info field and parentInstance, but keep attributes and of course unity object context
                        //this way, attributes work on internal field
                        info.field = bbParam.GetType().RTGetField("_value");
                        info.wrapperInstanceContext = info.parentInstanceContext;
                        info.parentInstanceContext  = bbParam;
                        if (info.attributes != null)
                        {
                            info.attributes = info.attributes.Where(a => !(a is DrawerAttribute) || !((DrawerAttribute)a).isDecorator).ToArray();
                        }
                        GUILayout.BeginVertical();
                        bbParam.value = EditorUtils.ReflectedFieldInspector(content, bbParam.value, bbParam.varType, info);
                        GUILayout.EndVertical();
                    }

                    if (!blackboardOnly)
                    {
                        bbParam.useBlackboard = EditorGUILayout.Toggle(bbParam.useBlackboard, EditorStyles.radioButton, GUILayout.Width(18));
                    }
                }
                GUILayout.EndHorizontal();


                //warn/error marks
                if (content != GUIContent.none)
                {
                    if (bbParam.varRef == null && !string.IsNullOrEmpty(bbParam.name))
                    {
                        if (!bbParam.isPresumedDynamic)
                        {
                            EditorUtils.MarkLastFieldError($"Missing Variable. Type of '{bbParam.varType.FriendlyName()}'");
                        }
                        if (bbParam.isPresumedDynamic)
                        {
                            EditorUtils.MarkLastFieldWarning($"Dynamic Variable. Type of '{bbParam.varType.FriendlyName()}'");
                        }
                    }
                    else
                    {
                        if (required && bbParam.isNone)
                        {
                            EditorUtils.MarkLastFieldError("An instance is required");
                        }
                        if (required && !string.IsNullOrEmpty(bbParam.name) && bbParam.isNull)
                        {
                            EditorUtils.MarkLastFieldWarning("An instance is required but currently resolves to null. If it is set in runtime you can ignore this warning.");
                        }
                    }
                }


                string textInfo = null;
                if (bbParam.useBlackboard)
                {
                    if (bbParam.bb == null)
                    {
                        textInfo = "<i>No current Blackboard reference</i>";
                    }
                    else
                    if (bbParam.isNone)
                    {
                        textInfo = "Select a '" + bbParam.varType.FriendlyName() + "' Assignable Blackboard Variable";
                    }
                    else
                    if (bbParam.varRef != null && bbParam.varType != bbParam.refType)
                    {
                        var setPossible = bbParam.varRef.CanConvertFrom(bbParam.varType);
                        textInfo = string.Format("AutoConvert: ({0} ➲ {1}){2}", bbParam.refType.FriendlyName(), bbParam.varType.FriendlyName(), setPossible ? string.Empty : " [GET ONLY]");
                    }
                }

                if (textInfo != null)
                {
                    GUI.color = Color.white.WithAlpha(0.5f);
                    GUILayout.BeginVertical(GUI.skin.textField);
                    GUILayout.Label(textInfo, GUILayout.Width(0), GUILayout.ExpandWidth(true));
                    GUILayout.EndVertical();
                    GUILayout.Space(2);
                    GUI.color = Color.white;
                }
            }
            GUILayout.EndVertical();
            return(bbParam);
        }
        //...
        void DoExposedVariablesMapping()
        {
            if (owner.graph == null)
            {
                return;
            }

            var separatorDrawn   = false;
            var subTreeVariables = owner.graph.blackboard.variables.Values;

            foreach (var variable in subTreeVariables)
            {
                if (variable is Variable <VariableSeperator> )
                {
                    continue;
                }
                if (!variable.isExposedPublic || variable.isPropertyBound)
                {
                    continue;
                }

                if (!separatorDrawn)
                {
                    separatorDrawn = true;
                    EditorUtils.Separator();
                    EditorGUILayout.HelpBox("Exposed Graph Variables. Use the arrows button to override/parametrize the variable.\nDoing this will not change the graph serialization. Prefab overrides are also supported.", MessageType.None);
                }

                if (owner.exposedParameters == null)
                {
                    owner.exposedParameters = new System.Collections.Generic.List <ExposedParameter>();
                }
                var exposedParam = owner.exposedParameters.Find(x => x.targetVariableID == variable.ID);
                if (exposedParam == null)
                {
                    GUILayout.BeginHorizontal();
                    GUI.enabled = false;
                    EditorUtils.DrawEditorFieldDirect(new GUIContent(variable.name, "This is an Exposed Public variable of the graph local blackboard. You can use the arrows button on the right side to override/parametrize the default value."), variable.value, variable.varType, default(InspectedFieldInfo));
                    GUI.enabled = true;
                    if (GUILayout.Button(EditorUtils.GetTempContent("▽△", null, "Override Variable"), Styles.centerLabel, GUILayout.Width(24)))
                    {
                        UndoUtility.RecordObject(owner, "Add Override");
                        exposedParam = ExposedParameter.CreateInstance(variable);
                        owner.exposedParameters.Add(exposedParam);
                        exposedParam.Bind(owner.graph.blackboard);
                        UndoUtility.SetDirty(owner);
                    }
                    EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link);
                    GUILayout.EndHorizontal();
                    continue;
                }

                GUILayout.BeginHorizontal();
                var info = new InspectedFieldInfo();
                info.unityObjectContext = owner;
                exposedParam.valueBoxed = EditorUtils.DrawEditorFieldDirect(new GUIContent(variable.name), exposedParam.valueBoxed, variable.varType, info);
                if (GUILayout.Button(EditorUtils.GetTempContent("▼▲", null, "Remove Override"), Styles.centerLabel, GUILayout.Width(24)))
                {
                    UndoUtility.RecordObject(owner, "Remove Override");
                    exposedParam.UnBind(owner.graph.blackboard);
                    owner.exposedParameters.Remove(exposedParam);
                    UndoUtility.SetDirty(owner);
                    continue;
                }
                EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link);
                GUILayout.EndHorizontal();

                var index            = owner.exposedParameters.IndexOf(exposedParam);
                var serProp          = exposeParamsProp.GetArrayElementAtIndex(index);
                var isPrefabOverride = serProp.prefabOverride;
                if (isPrefabOverride)
                {
                    var rect = GUILayoutUtility.GetLastRect();
                    EditorUtils.MarkLastFieldOverride();
                    if (rect.Contains(Event.current.mousePosition) && Event.current.type == EventType.ContextClick)
                    {
                        var prefabAssetPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(owner);
                        var asset           = AssetDatabase.LoadAssetAtPath <UnityEngine.Object>(prefabAssetPath);
                        var menu            = new GenericMenu();
                        menu.AddItem(new GUIContent($"Apply to Prefab '{asset.name}'"), false, () =>
                        {
                            UndoUtility.RecordObject(owner, "Apply Exposed Parameter");
                            UndoUtility.RecordObject(asset, "Apply Exposed Parameter");
                            PrefabUtility.ApplyPropertyOverride(serProp, prefabAssetPath, InteractionMode.UserAction);
                            UndoUtility.SetDirty(owner);
                            UndoUtility.SetDirty(asset);
                        });
                        menu.AddItem(new GUIContent("Revert"), false, () =>
                        {
                            UndoUtility.RecordObject(owner, "Revert Exposed Parameter");
                            PrefabUtility.RevertPropertyOverride(serProp, InteractionMode.UserAction);
                            UndoUtility.SetDirty(owner);
                        });
                        menu.ShowAsContext();
                    }
                }
            }

            if (separatorDrawn)
            {
                EditorUtils.Separator();
            }

            //cleanup
            if (owner.exposedParameters != null)
            {
                for (var i = owner.exposedParameters.Count; i-- > 0;)
                {
                    var exposedParam = owner.exposedParameters[i];
                    var variable     = owner.graph.blackboard.GetVariableByID(exposedParam.targetVariableID);
                    if (variable == null || !variable.isExposedPublic || variable.isPropertyBound)
                    {
                        owner.exposedParameters.RemoveAt(i);
                        UndoUtility.SetDirty(owner);
                    }
                }
            }
        }