using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

using Levelbot.Actions.Combat;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.POI;
using Styx.CommonBot.Profiles;
using Styx.CommonBot.Routines;
using Styx.Helpers;
using Styx.Pathing;
using Styx.WoWInternals.WoWObjects;
using Styx.TreeSharp;
using Action = Styx.TreeSharp.Action;
using Sequence = Styx.TreeSharp.Sequence;
using Styx.WoWInternals;

namespace Styx.Bot.CustomBots
{
	public class CombatBot : BotBase
	{
		[DllImport("user32.dll")]
		static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey); 

		#region Overrides of BotBase

		public override bool RequiresProfile { get { return false; } }

		public override string Name
		{
			get { return "Combat Bot"; }
		}

		private Composite _root;
		public override Composite Root
		{
			get
			{
				return _root ?? (_root = 
					new PrioritySelector(
						CreateCombatBehavior()
						//,
						//CreateFollowBehavior()
						)
					);
			}
		}

		public override void Initialize()
		{
			BotEvents.Player.OnMapChanged += Player_OnMapChanged;
		}

		private void Player_OnMapChanged(BotEvents.Player.MapChangedEventArgs args)
		{
			_root = null;
		}

		public override void Pulse()
		{
			//if ((GetAsyncKeyState(Keys.NumPad0) & 1) != 0)
			//    BotPoi.Current = new BotPoi(StyxWoW.Me.CurrentTarget, PoiType.Kill);
		}

		public override PulseFlags PulseFlags
		{
			get { return PulseFlags.All & ~(PulseFlags.Looting | PulseFlags.CharacterManager); }
		}

		private bool _oldLogoutForInactivity;
		public override void Start()
		{
			Targeting.Instance.IncludeTargetsFilter += IncludeTargetsFilter;
			_oldLogoutForInactivity = GlobalSettings.Instance.LogoutForInactivity;
			GlobalSettings.Instance.LogoutForInactivity = false;

			// Load an empty profile
			//ProfileManager.LoadEmpty();
		}

		public override void Stop()
		{
			Targeting.Instance.IncludeTargetsFilter -= IncludeTargetsFilter;
			GlobalSettings.Instance.LogoutForInactivity = _oldLogoutForInactivity;
		}

		#endregion

		#region Targeting Filter

		private static void IncludeTargetsFilter(List<WoWObject> incomingUnits, HashSet<WoWObject> outgoingUnits)
		{
			if (StyxWoW.Me.GotTarget && StyxWoW.Me.CurrentTarget.Attackable)
			{
				outgoingUnits.Add(StyxWoW.Me.CurrentTarget);
			}
		}

		#endregion

		#region Behaviors

		#region Combat Behavior

		private static bool NeedPull(object context)
		{
			var target = StyxWoW.Me.CurrentTarget;

			if (target == null)
				return false;

			if (!target.InLineOfSight)
				return false;

			if (target.Distance > Targeting.PullDistance)
				return false;

			return true;
		}

		private static Composite CreateCombatBehavior()
		{
			return new PrioritySelector(

				new Decorator(ret => !StyxWoW.Me.Combat,
					new PrioritySelector(

						#region Rest

						new PrioritySelector(
						// Use the bt
						new Decorator(ctx => RoutineManager.Current.RestBehavior != null,
							RoutineManager.Current.RestBehavior),

							// new ActionDebugString("[Combat] Rest -> Old Behavior"),
							// don't use the bt
							new Decorator(ctx => RoutineManager.Current.NeedRest,
								new Sequence(
									new Action(ret => TreeRoot.StatusText = "Resting"),
									new Action(ret => RoutineManager.Current.Rest())))
									),

						#endregion

						#region PreCombatBuffs

						new PrioritySelector(
							// new ActionDebugString("[Combat] Checking PCBBehavior"),
							// Use the bt
							new Decorator(ctx => RoutineManager.Current.PreCombatBuffBehavior != null,
								RoutineManager.Current.PreCombatBuffBehavior),

							// don't use the bt
							// new ActionDebugString("[Combat] Checking PCBOld"),
							new Decorator(
								ctx => RoutineManager.Current.NeedPreCombatBuffs,
								new Sequence(
									new Action(ret => TreeRoot.StatusText = "Applying pre-combat buffs"),
									new Action(ret => RoutineManager.Current.PreCombatBuff())
									))),

						#endregion

						#region Pull

						// new ActionDebugString("[Combat] Pull"),
						// Don't pull, unless we've decided to pull already.
						new Decorator(ret => BotPoi.Current.Type == PoiType.Kill,
							new PrioritySelector(

								// Make sure we have a valid target list.
								new Decorator(ret => Targeting.Instance.TargetList.Count != 0,
									// Force the 'correct' POI to be our target.
									new Decorator(ret => BotPoi.Current.AsObject != Targeting.Instance.FirstUnit &&
										BotPoi.Current.Type == PoiType.Kill,
										new Sequence(

											new Action(ret => BotPoi.Current = new BotPoi(Targeting.Instance.FirstUnit, PoiType.Kill)),
											new Action(ret => BotPoi.Current.AsObject.ToUnit().Target())))),

										new Decorator(NeedPull,
											new PrioritySelector(
												new Decorator(ctx => RoutineManager.Current.PullBuffBehavior != null,
													RoutineManager.Current.PullBuffBehavior),

												new Decorator(ctx => RoutineManager.Current.PullBehavior != null,
													RoutineManager.Current.PullBehavior),

													new ActionPull())))))),
						#endregion

				new Decorator(ret => StyxWoW.Me.Combat,

					new PrioritySelector(

						#region Heal

						new PrioritySelector(
						// Use the Behavior
							new Decorator(ctx => RoutineManager.Current.HealBehavior != null,
								new Sequence(
									RoutineManager.Current.HealBehavior,
									new Action(delegate { return RunStatus.Success; })
									)),

							// Don't use the Behavior
							new Decorator(ctx => RoutineManager.Current.NeedHeal,
								new Sequence(
									new Action(ret => TreeRoot.StatusText = "Healing"),
									new Action(ret => RoutineManager.Current.Heal())
									))),

						#endregion

						#region Combat Buffs

						new PrioritySelector(
						// Use the Behavior
							new Decorator(ctx => RoutineManager.Current.CombatBuffBehavior != null,
										new Sequence(
											RoutineManager.Current.CombatBuffBehavior,
											new Action(delegate { return RunStatus.Success; })
											)
								),

							// Don't use the Behavior
							new Decorator(ctx => RoutineManager.Current.NeedCombatBuffs,
										new Sequence(
											new Action(ret => TreeRoot.StatusText = "Applying Combat Buffs"),
											new Action(ret => RoutineManager.Current.CombatBuff())
											))),

						#endregion

						#region Combat

						new PrioritySelector(
							// Use the Behavior
							new Decorator(ctx => RoutineManager.Current.CombatBehavior != null,
								new PrioritySelector(
									RoutineManager.Current.CombatBehavior,
									new Action(delegate { return RunStatus.Success; })
									)),

							// Don't use the Behavior
							new Sequence(
								new Action(ret => TreeRoot.StatusText = "Combat"),
								new Action(ret => RoutineManager.Current.Combat())))

						#endregion

						)));
		}

		#endregion

		#region Follow Behavior

		private static WoWUnit _followMe;
		private static bool _isInitialized;
		private static WoWUnit FollowMe
		{
			get
			{
				if(!_isInitialized && _followMe != null)
					_followMe.OnInvalidate += new ObjectInvalidateDelegate(_followMe_OnInvalidate);

				if (_followMe == null || !_followMe.IsValid)
				{
					if (StyxWoW.Me.IsInInstance)
					{
						for (int i = 1; i < 5; i++)
						{
							string role = Lua.GetReturnVal<string>(string.Format("return UnitGroupRolesAssigned('party{0}')", i), 0);
							if (role == "TANK")
								_followMe = ObjectManager.GetObjectByGuid<WoWPlayer>(StyxWoW.Me.GetPartyMemberGuid(i - 1));
						}
					}
					else
					{
						_followMe = RaFHelper.Leader ?? StyxWoW.Me.PartyMembers.FirstOrDefault();
					}
					RaFHelper.SetLeader(_followMe.Guid);
				}

				if (_followMe.Guid != RaFHelper.Leader.Guid)
					_followMe = RaFHelper.Leader;


				if (_followMe == null)
					Logging.Write("Could not find suitable unit to follow!");

				return _followMe;
			}
		}

		static void _followMe_OnInvalidate()
		{
			_followMe = null;
		}

		private static Composite CreateFollowBehavior()
		{
			return new PrioritySelector(
				
				new Decorator(ret => StyxWoW.Me.GroupInfo.IsInParty && (FollowMe != null && FollowMe.Distance > 10 || FollowMe != null && !FollowMe.InLineOfSight),
					new Action(ret => Navigator.MoveTo(FollowMe.Location))
					)
				);
		}

		#endregion

		#endregion
	}
}
