#include "scripts/common.txt"
#include "scripts/BP_common.txt"

enum TankStates
{
	TANK_IDLE = 0,
	TANK_TRACKING,
	TANK_FIRING,
	TANK_FIREWAIT,
	TANK_DEAD,
}

enum TankAnims
{
	TANK_ANIM_IDLE = 0,
	TANK_ANIM_FIRE,
}


const float TANK_TRACK_TIME = 2.0f; //time turret is following the target
const float TANK_FIRE_TIME = 1.0f; //time waiting to fire at target position
const float TANK_FIRE_WAIT_TIME = 0.5f; //time waiting to after firing
const float TANK_TURN_YAW = Math::Deg2Rad(10.0f);
const float TANK_STRAFE_SPEED = 4.0f;
const float TANK_FIRE_RADIUS = 192.0f * 192.0f;

class BP_Tank : ScriptObject
{
    kActor@ self;
	int state;
	float stateTime;
	float turretAngle;
	float turretTurnSpeed;
	float attackAngleRange;
	
	float targetYaw; //body
	kVec3 turretTargetPos;
	// float attackTime;
	//------------------------------------------------------------------------------------------------------------------------
    BP_Tank(kActor @actor)
	{
        @self = actor;
		self.Flags() |= AF_SOLID | AF_CANBETOUCHED | AF_ALWAYSACTIVE;
		self.ClipFlags() |= CF_IGNOREBLOCKERS | CF_CLIPEDGES | CF_DROPOFF | CF_WALKWALLS | CF_COLLIDEFLOORS | CF_NOSLOPESTEP | CF_COLLIDEHEIGHT | CF_NOFLOORADJUST | CF_NOCEILINGADJUST | CF_COLLIDEWATER;
		
		state = -1;
		turretTurnSpeed = Math::Deg2Rad(5.0f);
		attackAngleRange = Math::Deg2Rad(30.0f);
    }
	//------------------------------------------------------------------------------------------------------------------------
	void OnPostBeginLevel()
	{
		SetState(TANK_TRACKING, true);
		//SetState(TANK_IDLE, true);
		self.SetTarget(Player.Actor().CastToActor());
		SetAnimation(TANK_ANIM_IDLE);
	}
	//------------------------------------------------------------------------------------------------------------------------
    void OnSpawn() {}
	//------------------------------------------------------------------------------------------------------------------------
    void OnTick()
	{
		//not active
		if (self.Health() <= 1)
		{
			return;
		}
		
		kActor@ target = self.GetTarget();
		if (target is null)
		{
			return;
		}
		
		float targetWidth = 100.0f;
		
		if (target.Origin().x > self.Origin().x + targetWidth) //target to the right then move right
		{
			targetYaw = Math::Clampf(self.Yaw() + TANK_TURN_YAW, -TANK_TURN_YAW, TANK_TURN_YAW);
		}
		else if (target.Origin().x < self.Origin().x - targetWidth) //target to the left then move left
		{
			targetYaw = Math::Clampf(self.Yaw() - TANK_TURN_YAW, -TANK_TURN_YAW, TANK_TURN_YAW);
		}
		else
		{
			targetYaw = 0.0f;
		}
		self.Yaw() = self.Yaw().Interpolate(targetYaw, 0.01f);
		self.Velocity() = kVec3(1.0f, 0.0f, 0.0f) * ((self.Yaw() / TANK_TURN_YAW) * TANK_STRAFE_SPEED);
		self.Velocity().y = target.Velocity().y;
		
		//Tick State
		stateTime += GAME_DELTA_TIME;
		switch(state)
		{
			case TANK_IDLE:
				break;
			case TANK_TRACKING:
			{
				TurretTrackPosition(target.Origin());
				if (stateTime >= TANK_TRACK_TIME && IsFacingTarget())
				{
					turretTargetPos = target.Origin();
					SetState(TANK_FIRING);
				}
				break;
			}
			case TANK_FIRING:
			{
				turretTargetPos.y = target.Origin().y;
				TurretTrackPosition(turretTargetPos);
				
				//show target location
				// int si = self.GetSectorIndexAtLocation(turretTargetPos);
				// Game.SpawnFx("fx/explosion_small.kfx", turretTargetPos, si);
				
				if (stateTime >= TANK_FIRE_TIME)
				{
					SetState(TANK_FIREWAIT);
				}
				break;
			}
			case TANK_FIREWAIT:
			{
				if (self.AnimState().PlayingID() == TANK_ANIM_IDLE || stateTime >= TANK_FIRE_WAIT_TIME)
				{
					SetState(TANK_TRACKING);
				}
				break;
			}
			case TANK_DEAD:
				break;
		}
		
		switch (self.AnimState().PlayingID())
		{
			case TANK_ANIM_IDLE:
				break;
			case TANK_ANIM_FIRE:
			{
				if (self.AnimState().CycleCompleted())
				{
					SetAnimation(TANK_ANIM_IDLE);
				}
				break;
			}
		}
    }
	//------------------------------------------------------------------------------------------------------------------------
    void OnDeath(kActor @killer, kDictMem @damageDef)
    {
		SetState(TANK_DEAD);
    }
	//------------------------------------------------------------------------------------------------------------------------
	void SetAnimation(const int animID, const int flags = ANF_LOOP, const bool force = false)
	{
		if (self.AnimState().PlayingID() != animID || force)
		{
			self.AnimState().Blend(animID, 4.0f, 8.0f, flags);
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
	void SetState(const int newState, const bool force = false)
	{
		if (state != newState || force)
		{
			//On Exit State
			switch(state)
			{
				case TANK_FIRING:
				{
					self.PlaySound("sounds/shaders/tankfire.ksnd");
					SetAnimation(TANK_ANIM_FIRE);
					int si = self.GetSectorIndexAtLocation(turretTargetPos);
					kVec3 firePos = turretTargetPos;
					firePos.y += 200.0f;
					Game.SpawnFx("fx/explosion.kfx", firePos, si);
					kActor@ target = self.GetTarget();
					//check if target is in radius and if so then call map script to damage the player
					if (target !is null && IsActorInRadius(target, turretTargetPos, TANK_FIRE_RADIUS))
					{
						if (Game.GetCurrentMapID() == 128) // on tank tunnel chase map
						{
							Game.CallDelayedMapScript(2, self, 0.0f); //hit player
						}
					}
					break;
				}
				case TANK_FIREWAIT:
					break;
				case TANK_DEAD:
					//self.Flags() |= AF_SOLID;
					break;
			}
			
			state = newState;
			stateTime = 0.0f;
			
			//On Enter State
			switch(state)
			{
				case TANK_IDLE:
					break;
				case TANK_TRACKING:
					SetAnimation(TANK_ANIM_IDLE);
					break;
				case TANK_FIRING:
					self.RunFxEvent("TankFireFlash");
					break;
				case TANK_FIREWAIT:
					break;
				case TANK_DEAD:
					//play dead animation
					// SetAnimation(anim_aiStanding, ANF_LOOP|ANF_ROOTMOTION);
					break;
			}
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
	void TurretTrackPosition(const kVec3 &in pos)
	{
		float turretTargetAngle = self.GetTurnYaw(pos) - self.Yaw();
		kAngle ang(turretAngle);
		turretAngle = ang.Interpolate(turretTargetAngle, 0.02f);
		self.RenderModel().SetRotationOffset(1, -turretAngle, 0, 0, 1);
	}
	//------------------------------------------------------------------------------------------------------------------------
	//not used and can delete
    float TurnAngles(const float turnSpeed, const float angles)
    {
        float temp = angles;
        float ang = angles;
        
        if(ang > turnSpeed)
        {
            ang = turnSpeed;
        }
        else if(ang < -turnSpeed)
        {
            ang = -turnSpeed;
        }
        
        ang *= GAME_FRAME_TIME;
        if (Math::Fabs(ang) > Math::Fabs(temp))
        {
            ang = temp;
        }
       
		return ang;
        //self.Yaw() += ang;
    }
	//------------------------------------------------------------------------------------------------------------------------
    bool IsFacingTarget()
    {
		return (Math::Fabs(self.GetTurnYaw(self.GetTarget().Origin())) < attackAngleRange);
    }
	//------------------------------------------------------------------------------------------------------------------------
}
