namespace BP
{
	const int AF_EASY = 1 << 10;
	const int AF_NORMAL = 1 << 11;
	const int AF_HARD = 1 << 12;
	const int AF_HARDCORE = 1 << 13;
	
	namespace Actor
	{
		kActor@ Null = null; //for setting SetTarget() to null
		//------------------------------------------------------------------------------------------------------------------------
		kActor@ FindActorWithTID(const int tagID)
		{
			kActorIterator iter;
			kActor@ actor;
			while ((@actor = iter.GetNext()) !is null)
			{
				if (actor.TID() == tagID)
				{
					return @actor;
				}
			}
			
			return null;
		}
		//------------------------------------------------------------------------------------------------------------------------
		array<kActor@> FindActorsWithTID(const int tagID)
		{
			array<kActor@> result;
			kActorIterator iter;
			kActor@ actor;
			while ((@actor = iter.GetNext()) !is null)
			{
				if (actor.TID() == tagID)
				{
					result.insertLast(@actor);
				}
			}
			
			return result;
		}
		//------------------------------------------------------------------------------------------------------------------------
		// returns the first actor that has the same Type
		//------------------------------------------------------------------------------------------------------------------------
		kActor@ FindActorWithType(const int typeID)
		{
			array<kActor@> result;
			kActorIterator iter;
			kActor@ actor;
			while ((@actor = iter.GetNext()) !is null)
			{
				if (actor.Type() == typeID)
				{
					return @actor;
				}
			}
			
			return null;
		}
		//------------------------------------------------------------------------------------------------------------------------
		// returns a list of all the actors in that have the same Type
		//------------------------------------------------------------------------------------------------------------------------
		array<kActor@> FindActorsWithType(const int typeID)
		{
			array<kActor@> result;
			kActorIterator iter;
			kActor@ actor;
			while ((@actor = iter.GetNext()) !is null)
			{
				if (actor.Type() == typeID)
				{
					result.insertLast(@actor);
				}
			}
			return result;
		}
		//------------------------------------------------------------------------------------------------------------------------
		void AIMoveToTarget(kActor@ actor, kActor @target)
		{
			DebugAssert(actor !is null, "[BP::Actor::AIMoveToTarget] actor is null");
			DebugAssert(actor.EnemyAIComponent() !is null, "[BP::Actor::AIMoveToTarget] actor doesn't have EnemyAIComponent");
			actor.EnemyAIComponent().PathTarget().SetTarget(@target);
		}
		//------------------------------------------------------------------------------------------------------------------------
		void AIAttackTarget(kActor@ actor, kActor @target)
		{
			DebugAssert(actor !is null, "[BP::Actor::AIAttackTarget] actor is null");
			DebugAssert(actor.EnemyAIComponent() !is null, "[BP::Actor::AIAttackTarget] actor doesn't have EnemyAIComponent");
			actor.EnemyAIComponent().SightTarget().SetTarget(@target);
		}
		//------------------------------------------------------------------------------------------------------------------------
		void AIIgnorePlayer(kActor@ actor, bool ignore)
		{
			DebugAssert(actor !is null, "[BP::Actor::AIIgnorePlayer] actor is null");
			DebugAssert(actor.EnemyAIComponent() !is null, "[BP::Actor::AIIgnorePlayer] actor doesn't have EnemyAIComponent");
			actor.EnemyAIComponent().SetNoUpdateTarget(ignore, ignore);
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns the actors teamID or -1 actor if not part of the teams
		//------------------------------------------------------------------------------------------------------------------------
		int GetTeamID(kActor@ actor)
		{
			//check if the player is the actor
			if (@actor == @BP::LocalPlayer::Actor)
			{
				return BP::LocalPlayer::Script.teamID;
			}
			
			//check if any bot in any team is the actor
			int teamsLength = int(BP::teams.length());
			for (int i = 0; i < teamsLength; i++)
			{
				BP::Team@ team = @BP::teams[i];
				int botsLength = int(team.bots.length());
				for (int j = 0; j < botsLength; j++)
				{
					BP::BotInfo bot = team.bots[j];
					if (@bot.sActor.actor == @actor)
					{
						return bot.teamID;
					}
				}
			}
			
			return -1;
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns true if actor is on the ground
		//------------------------------------------------------------------------------------------------------------------------
		bool IsGrounded(kActor@ actor)
		{
			DebugAssert(actor !is null, "[BP::Actor::IsGrounded] actor is null");
			DebugAssert(actor.MovementComponent() !is null, "[BP::Actor::IsGrounded] actor doesn't have a movement component");
			return (actor.MovementComponent().Flags() & MCF_FALLING) == 0;
			
			//Also works (using WorldComponent)
			// DebugAssert(actor !is null, "[BP::Actor::IsGrounded] actor is null");
			// DebugAssert(actor.WorldComponent() !is null, "[BP::Actor::IsGrounded] actor doesn't have a world component");
			// return Math::Approximately(actor.Origin().z, actor.WorldComponent().FloorHeight());
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns the forward direction of the actor
		//------------------------------------------------------------------------------------------------------------------------
		kVec3 Forward(kActor@ actor)
		{
			return kVec3(0.0f, 1.0f, 0.0f) * kQuat(actor.Pitch(), 0.0f, actor.Yaw());
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns the right direction of the actor
		//------------------------------------------------------------------------------------------------------------------------
		kVec3 Right(kActor@ actor)
		{
			return kVec3(1.0f, 0.0f, 0.0f) * kQuat(actor.Pitch(), 0.0f, actor.Yaw());
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns the up direction of the actor
		//------------------------------------------------------------------------------------------------------------------------
		kVec3 Up(kActor@ actor)
		{
			return kVec3(0.0f, 0.0f, 1.0f) * kQuat(actor.Pitch(), 0.0f, actor.Yaw());
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns the up direction of the actor
		//------------------------------------------------------------------------------------------------------------------------
		kVec3 Direction(kActor@ actor, float x, float y, float z)
		{
			return kVec3(x, y, z) * kQuat(actor.Pitch(), 0.0f, actor.Yaw());
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns the center position of the actor
		//------------------------------------------------------------------------------------------------------------------------
		kVec3 CenterOrigin(kActor @actor, float heightRatio = 0.5f)
		{
			return kVec3(actor.Origin().x, actor.Origin().y, actor.Origin().z + (actor.WorldComponent().Height() * heightRatio) + actor.WorldComponent().HeightOffset());
		}
		//------------------------------------------------------------------------------------------------------------------------
		void ToggleFlag(kActor @actor, const int bit)
		{
			if ((actor.Flags() & bit) != 0)
			{
				actor.Flags() &= ~bit;
			}
			else
			{
				actor.Flags() |= bit;
			}
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool InPlayerRadius(kActor @actor, const float radius)
		{
			return BP::LocalPlayer::Actor.Origin().DistanceSq(actor.Origin()) < radius;
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool InRadius(kActor @actor, kActor @targetActor, const float radius)
		{
			return actor.Origin().DistanceSq(targetActor.Origin()) < radius;
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool InRadius(kActor @actor, const kVec3& in pos, const float radius)
		{
			return actor.Origin().DistanceSq(pos) < radius;
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool InRadius(const kVec3& in point, const kVec3& in pos, const float radius)
		{
			return point.DistanceSq(pos) < radius;
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns true if the actor is not stale, not dead, not Hidden
		//------------------------------------------------------------------------------------------------------------------------
		bool IsActive(kActor @actor)
		{
			return !actor.IsStale() && !IsDead(@actor) && !IsHidden(@actor);
		}
		//------------------------------------------------------------------------------------------------------------------------
		// Returns true if actor is not null and not stale
		//------------------------------------------------------------------------------------------------------------------------
		bool Exists(kActor @actor)
		{
			return @actor != null && !actor.IsStale();
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool IsDead(kActor @actor)
		{
			return (actor.Flags() & AF_DEAD) != 0;
		}
		//------------------------------------------------------------------------------------------------------------------------
		bool IsHidden(kActor @actor)
		{
			return (actor.Flags() & AF_HIDDEN) != 0;
		}
		//------------------------------------------------------------------------------------------------------------------------
	}
}
