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

const bool ArrowStickInActors = true;

class BP_ArrowProjectile : ScriptObject
{
    kActor @self;
	kActor @modelActor;
	bool hit;
	bool didHitActor;
	bool washitActorActive;
	kVec3 lastPos;
	kQuat lastQuat;
	float lastYaw;
	float lastPitch;
	kVec3 lastDir;
	kVec3 fxVelocity;
	kActor @hitActor;
	kVec3 hitOffset;
	kQuat hitRot;
	float hitYaw;
	float hitPitch;
	bool canPickup;
	float lifeTime;
	bool wasUnderWater;
	//------------------------------------------------------------------------------------------------------------------------
    BP_ArrowProjectile(kActor @actor)
    {
        @self = actor;
		self.Flags() |= AF_NODRAW;
		
		kVec3 origin = self.Origin();
		@modelActor = ActorFactory.Spawn("BP_ArrowProjectileModel", origin.x, origin.y, origin.z, self.Yaw(), self.SectorIndex());
		modelActor.Flags() |= AF_NODRAW;
    }
	//------------------------------------------------------------------------------------------------------------------------
	void Setup(kVec3 fxVelocity, kVec3 actorVelocity)
	{
		self.Gravity() = 0.1f;
		self.ClipFlags() = (CF_DROPOFF|CF_CLIPEDGES|CF_COLLIDEFLOORS|CF_COLLIDEHEIGHT|CF_COLLIDECORPSES|CF_COLLIDEWATER);
		self.Velocity() = actorVelocity;
		this.fxVelocity = fxVelocity;
		
		lastPos = self.Origin();
	}
	//------------------------------------------------------------------------------------------------------------------------
    void OnTick()
	{
		bool underWater = false; // self.GetWaterLevel() == WLT_UNDER;
		int areaID = self.AreaID();
		if (areaID >= 0 && (World.GetAreaFlags(areaID) & AAF_WATER) != 0)
		{
			underWater = self.Origin().z <= self.GetWaterHeight();
		}
		
		if (canPickup)
		{
			bool inRadius = false;
			if (ActorExists(hitActor) && !IsActorDead(hitActor))
			{
				inRadius = IsPlayerCenterInRadius(modelActor.Origin(), 8100.0f);
			}
			else
			{
				inRadius = IsPlayerCenterInRadius(modelActor.Origin(), 4050.0f); //6400
			}
			
			if (inRadius && Player.HasWeapon(TW_WEAPON_BOW) && Player.GetAmmo(TW_WEAPON_BOW) < MaxArrows)
			{
				Player.GiveWeapon(TW_WEAPON_BOW, 1);
				Game.PlaySound("sounds/shaders/generic_6_arrow_pickup_.ksnd");
				Game.PrintLine(LTKey(1), 0, 120);
				modelActor.Remove();
				self.Remove();
				return;
			}
			else
			{
				lifeTime += GAME_DELTA_TIME;
				if (lifeTime >= 120.0f)
				{
					modelActor.Remove();
					self.Remove();
					return;
				}
			}
		}
		else
		{
			if (Math::Vec3Approximately(self.Velocity(), 0.0f))
			{
				canPickup = true;
			}
		}
		
		if (!hit)
		{
			modelActor.Origin() = self.Origin();
			lastPos = self.Origin();
			modelActor.Rotation() = self.Movement().ToQuat();
			lastDir = self.Movement();
			lastQuat = modelActor.Rotation();
			lastYaw = modelActor.Yaw();
			lastPitch = modelActor.Pitch();
			
			if (self.GameTicks() >= 3)
			{
				//if is in water and wasn't then create splash effect at water height
				if (wasUnderWater != underWater)
				{
					kVec3 pos = self.Origin();
					//pos.z = self.GetWaterHeight();
					Game.SpawnFx("fx/watersplash.kfx", pos, self.SectorIndex());
					self.PlaySound("sounds/shaders/water_splash_4.ksnd");
				}
				
				if (self.GameTicks() % 4 == 0)
				{
					modelActor.RenderModel().AddTrailEffect("Trail_Arrow", 0);
				}
			}
		}
		else
		{
			if (didHitActor)
			{
				bool hitActorActive = ActorExists(hitActor) && !IsActorDead(hitActor);
				if (washitActorActive != hitActorActive)
				{
					if (!hitActorActive)
					{
						modelActor.Gravity() = 0.1f;
						modelActor.ClipFlags() = (CF_DROPOFF|CF_CLIPEDGES|CF_COLLIDEFLOORS|CF_COLLIDEHEIGHT|CF_COLLIDEWATER);
					}
					washitActorActive = hitActorActive;
				}
				
				if (hitActorActive)
				{
					modelActor.Origin() = hitActor.Origin() + hitOffset;
					// if (IsActorDead(hitActor))
					// {
						// modelActor.Origin().z = hitActor.FloorHeight() + 4.0f;
					// }
					//modelActor.Rotation() = lastQuat;
					// modelActor.Rotation() = lastQuat + (hitRot - hitActor.Rotation());
					modelActor.Yaw() = lastYaw + (hitActor.Yaw() - hitYaw);
					modelActor.Pitch() = lastPitch + (hitActor.Pitch() - hitPitch);
				}
			}
			else
			{
				modelActor.Rotation() = lastQuat;
			}
		}
		
		if (self.GameTicks() == 3)
		{
			modelActor.Flags() &= ~AF_NODRAW;
		}
		
		wasUnderWater = underWater;
    }
	//------------------------------------------------------------------------------------------------------------------------
    void OnSpawn()
	{
		
    }
	//------------------------------------------------------------------------------------------------------------------------
	void OnCollide(kCModel @cModel)
	{
		kActor @cActor = cModel.ContactActor();
		kActor @player = Player.Actor().CastToActor();
		if (ActorExists(cActor) && @cActor == @player)
		{
			return;
		}
		
		uint clipResult = cModel.ClipResult();
				
		kVec3 p = cModel.InterceptVector();
		//Sys.Print(CRFString("Arrow OnCollide Result: ", clipResult));
		
		hit = true;
		canPickup = true;
		self.Gravity() = 0.0f;
		self.Velocity() = Math::vecZero;
		self.ClipFlags() |= CF_NOCOLLIDEFUNC;
		
		if (self.GameTicks() == 0)
		{
			lastDir = p - player.Origin();
			lastQuat = player.Rotation();
			lastYaw = player.Yaw();
			lastPitch = player.Pitch();
		}
		
		kVec3 normDir = lastDir.Normalize();
		modelActor.Origin() = p;
		if (ActorExists(cActor))
		{
			didHitActor = true;
			// modelActor.Origin() = cActor.Origin();
			// modelActor.Origin().z = cActor.FloorHeight();
			
			//if cActor is enemy or boss then drop to floor
			// if(!(cActor.ScriptObject() is null) && !(cActor.ScriptObject().obj is null))
			// {
				// TurokEnemy @enemyObj = cast<TurokEnemy@>(cActor.ScriptObject().obj);
				// if (cActor.Health() > 0 && !(enemyObj is null))
				// {
					// //drop to floor
					// modelActor.Origin().z = modelActor.FloorHeight();
					// modelActor.RenderModel().RemoveTrailEffect();
				// }
			// }
			if (ArrowStickInActors)
			{
				modelActor.RenderModel().RemoveTrailEffect();
				@hitActor = @cActor;
				//hitOffset = p - cActor.Origin();
				hitRot = cActor.Rotation();
				hitYaw = cActor.Yaw();
				hitPitch = cActor.Pitch();
				//hitOffset = (p - cActor.Origin()) + ((cActor.Origin() - p).Normalize() * (cActor.Radius() * 0.5f));
				hitOffset = (p + (normDir * (cActor.Radius() * 1.5f))) - cActor.Origin();
				modelActor.Origin() = hitActor.Origin() + hitOffset;
			}
			else
			{
				//drop to floor
				modelActor.Origin().z = modelActor.FloorHeight();
				modelActor.RenderModel().RemoveTrailEffect();
			}
			
			Game.SpawnFx("fx/BP_Player_Arrow.kfx", player, fxVelocity, lastPos, lastQuat);
		}
		else
		{
			if ((clipResult & CRF_CEILING) != 0)
			{
				modelActor.Origin() += normDir * 55.0f;	
			}
			else if ((clipResult & (CRF_WALL | CRF_MESH | CRF_WALLRADIUS)) != 0)
			{
				modelActor.Origin() += normDir * Math::RandRange(28.0f, 45.0f);
			}
			else
			{
				modelActor.Origin() += normDir * 14.0f;
			}
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
	kStr CRFString(kStr &in s, const uint clipResult)
	{
		if ((clipResult & CRF_FLOOR) != 0)
		{
			s += "CRF_FLOOR";
		}
		if ((clipResult & CRF_CEILING) != 0)
		{
			s += ", CRF_CEILING";
		}
		if ((clipResult & CRF_WALL) != 0)
		{
			s += ", CRF_WALL";
		}
		if ((clipResult & CRF_OBJECT) != 0)
		{
			s += ", CRF_OBJECT";
		}
		if ((clipResult & CRF_MESH) != 0)
		{
			s += ", CRF_MESH";
		}
		if ((clipResult & CRF_ADJUST) != 0)
		{
			s += ", CRF_ADJUST";
		}
		if ((clipResult & CRF_WALLRADIUS) != 0)
		{
			s += ", CRF_WALLRADIUS";
		}
		return s;
	}
	//------------------------------------------------------------------------------------------------------------------------
};
