// 24Feb2013-08:10UTC chinajade
        public override void OnFinished()
        {
            // Defend against being called multiple times (just in case)...
            if (!IsOnFinishedRun)
            {
                if (Targeting.Instance != null)
                {
                    Targeting.Instance.IncludeTargetsFilter -= TargetFilter_IncludeTargets;
                    Targeting.Instance.RemoveTargetsFilter  -= TargetFilter_RemoveTargets;
                    Targeting.Instance.WeighTargetsFilter   -= TargetFilter_WeighTargets;
                }


                // NB: we don't unhook _behaviorTreeHook_Main
                // This was installed when HB created the behavior, and its up to HB to unhook it

                BehaviorHookRemove("Combat_Main", ref _behaviorTreeHook_CombatMain);
                BehaviorHookRemove("Combat_Only", ref _behaviorTreeHook_CombatOnly);
                BehaviorHookRemove("Death_Main", ref _behaviorTreeHook_DeathMain);
                BehaviorHookRemove("Questbot_Main", ref _behaviorTreeHook_QuestbotMain);

                // Remove temporary blackspots...
                if (_temporaryBlackspots != null)
                {
                    BlackspotManager.RemoveBlackspots(_temporaryBlackspots.GetBlackspots());
                    _temporaryBlackspots = null;
                }

                // Restore configuration...
                if (_configMemento != null)
                {
                    _configMemento.Dispose();
                    _configMemento = null;
                }

                // Make sure we don't leave stale POIs set after finishing a QB.
                // This could happen if the user used a TerminateWhen to finish it
                // for example.
                BotPoi.Clear("Finished " + GetType().Name);

                TreeRoot.GoalText   = string.Empty;
                TreeRoot.StatusText = string.Empty;

                // Report the behavior run time...
                if (_behaviorRunTimer.IsRunning)
                {
                    _behaviorRunTimer.Stop();
                    QBCLog.DeveloperInfo("Behavior completed in {0}", Utility.PrettyTime(_behaviorRunTimer.Elapsed));
                }

                base.OnFinished();
                QBCLog.BehaviorLoggingContext = null;
                IsOnFinishedRun = true;
            }
        }
        // 24Feb2013-08:10UTC chinajade
        protected void Dispose(bool isExplicitlyInitiatedDispose)
        {
            if (!_isDisposed)
            {
                // NOTE: we should call any Dispose() method for any managed or unmanaged
                // resource, if that resource provides a Dispose() method.

                // Clean up managed resources, if explicit disposal...
                if (isExplicitlyInitiatedDispose)
                {
                    // empty, for now
                }

                // Clean up unmanaged resources (if any) here...

                // NB: we don't unhook _behaviorTreeHook_Main
                // This was installed when HB created the behavior, and its up to HB to unhook it

                if (_behaviorTreeHook_CombatMain != null)
                {
                    TreeHooks.Instance.RemoveHook("Combat_Main", _behaviorTreeHook_CombatMain);
                    _behaviorTreeHook_CombatMain = null;
                }

                if (_behaviorTreeHook_CombatOnly != null)
                {
                    TreeHooks.Instance.RemoveHook("Combat_Only", _behaviorTreeHook_CombatOnly);
                    _behaviorTreeHook_CombatOnly = null;
                }

                if (_behaviorTreeHook_DeathMain != null)
                {
                    TreeHooks.Instance.RemoveHook("Death_Main", _behaviorTreeHook_DeathMain);
                    _behaviorTreeHook_DeathMain = null;
                }

                BotEvents.OnBotStop -= BotEvents_OnBotStop;

                // Restore configuration...
                if (_configMemento != null)
                {
                    _configMemento.Dispose();
                    _configMemento = null;
                }

                TreeRoot.GoalText   = string.Empty;
                TreeRoot.StatusText = string.Empty;

                BehaviorLoggingContext = null;
                base.Dispose();
            }

            _isDisposed = true;
        }
        /// <summary>
        /// <para>This reports problems, and stops BT processing if there was a problem with attributes...
        /// We had to defer this action, as the 'profile line number' is not available during the element's
        /// constructor call.</para>
        /// <para>It also captures the user's configuration, and installs Behavior Tree hooks.  The items will
        /// be restored when the behavior terminates, or Honorbuddy is stopped.</para>
        /// </summary>
        /// <param name="extraGoalTextDescription"></param>
        protected void OnStart_QuestBehaviorCore(string extraGoalTextDescription = null)
        {
            UsageCheck_SemanticCoherency(Element,
                                         ((QuestObjectiveIndex > 0) && (QuestId <= 0)),
                                         context => string.Format("QuestObjectiveIndex of '{0}' specified, but no corresponding QuestId provided",
                                                                  QuestObjectiveIndex));
            EvaluateUsage_SemanticCoherency(Element);

            // Deprecated attributes...
            // TODO: Do this later, after we've made a sweep through Kick's profiles...
            EvaluateUsage_DeprecatedAttributes(Element);

            // This reports problems, and stops BT processing if there was a problem with attributes...
            // We had to defer this action, as the 'profile line number' is not available during the element's
            // constructor call.
            OnStart_HandleAttributeProblem();

            // If the quest is complete, this behavior is already done...
            // So we don't want to falsely inform the user of things that will be skipped.
            if (!IsDone)
            {
                // The ConfigMemento() class captures the user's existing configuration.
                // After its captured, we can change the configuration however needed.
                // When the memento is dispose'd, the user's original configuration is restored.
                // More info about how the ConfigMemento applies to saving and restoring user configuration
                // can be found here...
                //     http://www.thebuddyforum.com/mediawiki/index.php?title=Honorbuddy_Programming_Cookbook:_Saving_and_Restoring_User_Configuration
                _configMemento = new ConfigMemento();

                BotEvents.OnBotStop += BotEvents_OnBotStop;

                PlayerQuest quest = StyxWoW.Me.QuestLog.GetQuestById((uint)QuestId);

                TreeRoot.GoalText = string.Format(
                    "{1}: \"{2}\"{0}{3}{0}{0}{4}",
                    Environment.NewLine,
                    GetType().Name,
                    ((quest != null)
                        ? string.Format("\"{0}\" (QuestId: {1})", quest.Name, QuestId)
                        : "In Progress (no associated quest)"),
                    (extraGoalTextDescription ?? string.Empty),
                    GetProfileReference(Element));

                _behaviorTreeHook_CombatMain = new ExceptionCatchingWrapper(this, CreateBehavior_CombatMain());
                TreeHooks.Instance.InsertHook("Combat_Main", 0, _behaviorTreeHook_CombatMain);
                _behaviorTreeHook_CombatOnly = new ExceptionCatchingWrapper(this, CreateBehavior_CombatOnly());
                TreeHooks.Instance.InsertHook("Combat_Only", 0, _behaviorTreeHook_CombatOnly);
                _behaviorTreeHook_DeathMain = new ExceptionCatchingWrapper(this, CreateBehavior_DeathMain());
                TreeHooks.Instance.InsertHook("Death_Main", 0, _behaviorTreeHook_DeathMain);
            }
        }
        /// <summary>
        /// <para>This reports problems, and stops BT processing if there was a problem with attributes...
        /// We had to defer this action, as the 'profile line number' is not available during the element's
        /// constructor call.</para>
        /// <para>It also captures the user's configuration, and installs Behavior Tree hooks.  The items will
        /// be restored when the behavior terminates, or Honorbuddy is stopped.</para>
        /// </summary>
        /// <return>true, if the behavior should run; false, if it should not.</return>
        /// <param name="extraGoalTextDescription"></param>
        protected bool OnStart_QuestBehaviorCore(string extraGoalTextDescription = null)
        {
            // Semantic coherency / covariant dependency checks...
            UsageCheck_SemanticCoherency(Element, QuestObjectiveIndex > 0 && !VariantQuestIds.Any(),
                                         context => $"QuestObjectiveIndex of '{QuestObjectiveIndex}' specified, but no corresponding QuestId provided");

            UsageCheck_SemanticCoherency(Element,
                                         _providedQuestIdAndQuestVariantIds,
                                         context => "Cannot provide both a QuestId and VariantQuestIds at same time.");

            EvaluateUsage_SemanticCoherency(Element);

            // Deprecated attributes...
            EvaluateUsage_DeprecatedAttributes(Element);

            // This reports problems, and stops BT processing if there was a problem with attributes...
            // We had to defer this action, as the 'profile line number' is not available during the element's
            // constructor call.
            OnStart_HandleAttributeProblem();

            var questId = GetQuestId();

            // If the quest is complete, this behavior is already done...
            // So we don't want to falsely inform the user of things that will be skipped.
            // NB: Since the IsDone property may skip checking the 'progress conditions', we need to explicltly
            // check them here to see if we even need to start the behavior.
            if (!(IsDone || !UtilIsProgressRequirementsMet(questId, QuestRequirementInLog, QuestRequirementComplete)))
            {
                this.UpdateGoalText(questId, extraGoalTextDescription);

                // Start the timer to measure the behavior run time...
                _behaviorRunTimer.Restart();

                // Monitored Behaviors...
                if (QuestBehaviorCoreSettings.Instance.MonitoredBehaviors.Contains(GetType().Name))
                {
                    QBCLog.Debug("MONITORED BEHAVIOR: {0}", GetType().Name);
                    AudibleNotifyOn(true);
                }

                _configMemento = CreateConfigMemento();

                if (Targeting.Instance != null)
                {
                    Targeting.Instance.IncludeTargetsFilter += TargetFilter_IncludeTargets;
                    Targeting.Instance.RemoveTargetsFilter  += TargetFilter_RemoveTargets;
                    Targeting.Instance.WeighTargetsFilter   += TargetFilter_WeighTargets;
                }

                Query.InCompetitionReset();
                Utility.BlacklistsReset();

                _behaviorTreeHook_CombatMain   = BehaviorHookInstall("Combat_Main", CreateBehavior_CombatMain());
                _behaviorTreeHook_CombatOnly   = BehaviorHookInstall("Combat_Only", CreateBehavior_CombatOnly());
                _behaviorTreeHook_DeathMain    = BehaviorHookInstall("Death_Main", CreateBehavior_DeathMain());
                _behaviorTreeHook_QuestbotMain = BehaviorHookInstall("Questbot_Main", CreateBehavior_QuestbotMain());

                BlackspotManager.AddBlackspots(_temporaryBlackspots.GetBlackspots());

                if (_temporaryAvoidMobs != null)
                {
                    foreach (var avoidMobId in _temporaryAvoidMobs.GetAvoidMobIds())
                    {
                        // NB: ProfileManager.CurrentProfile.AvoidMobs will never be null
                        if (!ProfileManager.CurrentProfile.AvoidMobs.Contains(avoidMobId))
                        {
                            ProfileManager.CurrentProfile.AvoidMobs.Add(avoidMobId);
                        }
                    }
                }

                return(true);    // behavior should run
            }

            return(false);   // behavior should NOT run
        }