#include "MW4Headers.hpp"

#include "AI_CombatTacticInterface.hpp"

#include "AI_Weapons.hpp"
#include "Mech.hpp"
#include "MWMission.hpp"
#include "Airplane.hpp"
#include "AI_FireStyle.hpp"
#include "AI_Tactics.hpp"
#include "Mech.hpp"
#include "JumpJet.hpp"
#include "AI_Groups.hpp"
#include "AI_Damage.hpp"
#include "ShooterAI.hpp"



using namespace MW4AI;

extern __int64 tCombatAITime_ShotWithin;

bool CombatTacticInterface::g_DisableMovement = false;
bool CombatTacticInterface::g_DisableFiring = false;

const Stuff::Scalar min_move_reissuance_distance = 8.0f;

CombatTacticInterface::CombatTacticInterface(MechWarrior4::CombatAI& combat_ai)
	: m_CombatAI(combat_ai)
	, m_LastMoveDestScore(0)
{
}

void CombatTacticInterface::MoveTo(const Point3D& point, bool move_forward, bool throttle_override, MWObject* ram_target)
{
	#ifdef LAB_ONLY
	if (ram_target == 0)
	{
		Verify(PointIsValid(point,true));
	}

	if (g_DisableMovement == true)
	{
		Stop();
		return;
	}
	#endif

	// TODO: move this somewhere else; also take move_forward and throttle_override into account
	if ((m_CombatAI.m_TimeNotMoving == 0) &&
		(GetLengthSquared(m_CombatAI.m_LastMoveDest,point) < (min_move_reissuance_distance * min_move_reissuance_distance)))
	{
		return;
	}

	m_CombatAI.MoveToPoint(point,move_forward,throttle_override,ram_target);

	m_LastMoveDestScore = 0;
}

Stuff::Point3D CombatTacticInterface::GetAimPoint() const
{
	return (m_CombatAI.m_LastAimPoint);
}

void CombatTacticInterface::TrackTo(const Stuff::Point3D& point)
{
	m_CombatAI.TrackToPoint(point);
}

void CombatTacticInterface::TurnTo(const Stuff::Point3D& point, bool must_be_torso_twisted)
{
	m_CombatAI.TurnToPoint(point,must_be_torso_twisted);
}

void CombatTacticInterface::PitchTo(const Stuff::Point3D& point)
{
	m_CombatAI.PitchToPoint(point);
}

void CombatTacticInterface::Stop()
{
	m_CombatAI.StopMoving();
	m_LastMoveDestScore = 0;
}

void CombatTacticInterface::Fire(FireStyles::FireStyleID style_id)
{
	#ifdef LAB_ONLY
	if (g_DisableFiring == true)
	{
		return;
	}
	#endif

	m_CombatAI.TryToFire(style_id);
}

bool CombatTacticInterface::HasPath() const
{
	if (m_CombatAI.m_CurPath != 0)
	{
		return (true);
	}

	return (false);
}

bool CombatTacticInterface::Moving() const
{
	return (m_CombatAI.m_TimeNotMoving == 0);
}

Stuff::Scalar CombatTacticInterface::WeaponRange(WR_WHO who, WR_QUALIFIER qualifier) const
{
	MWObject* x = 0;

	if (who == TacticInterface::SELF)
	{
		x = &(m_CombatAI.GetSelf());
	}
	else
	{
		x = &(m_CombatAI.GetTarget());
	}

	if (qualifier == TacticInterface::MIN)
	{
		return (GetMinWeaponDistance(*x));
	}

	return (GetMaxWeaponDistance(*x));
}

bool CombatTacticInterface::PointIsValid(const Point3D& point, bool ignore_mission_bounds, bool ignore_squads) const
{
	return (m_CombatAI.PointIsValid(point,ignore_mission_bounds,ignore_squads));
}

MechWarrior4::MWObject& CombatTacticInterface::GetSelf() const
{
	return (m_CombatAI.GetSelf());
}

MechWarrior4::Vehicle* CombatTacticInterface::GetSelf_AsVehicle() const
{
	return (m_CombatAI.GetSelf_AsVehicle());
}

MechWarrior4::MWObject& CombatTacticInterface::GetTarget() const
{
	return (m_CombatAI.GetTarget());
}

MechWarrior4::Vehicle* CombatTacticInterface::GetTarget_AsVehicle() const
{
	return (m_CombatAI.GetTarget_AsVehicle());
}

FireData::HIT_RESULT CombatTacticInterface::GetLastHitResult() const
{
	return (m_CombatAI.m_HitResult);
}

Stuff::Scalar CombatTacticInterface::GetDistanceFromTargetSquared() const
{
	return (m_CombatAI.m_DistanceFromTargetSquared);
}

Moods::ID CombatTacticInterface::GetMood() const
{
	int current_mood = (int)m_CombatAI.CurrentMood();
	return ((Moods::ID)current_mood);
}

Stuff::Scalar CombatTacticInterface::GetDistanceFromDestinationSquared() const
{
	if ((m_CombatAI.m_CurMove == 0) ||
		(m_CombatAI.m_CurMove->Done() == true))
	{
		return (0);
	}

	if (m_CombatAI.m_LastMoveDest.GetLengthSquared() == 0)
	{
		return (0);
	}

	Stuff::Vector3D delta;
	delta.Subtract((Stuff::Point3D)m_CombatAI.GetSelf().GetLocalToWorld(),
				   m_CombatAI.m_LastMoveDest);

	return (delta.GetLengthSquared());
}

bool CombatTacticInterface::GetLastMoveDest(Stuff::Point3D& dest, bool ignore_movement) const
{
	if (ignore_movement == false)
	{
		if ((m_CombatAI.m_CurMove == 0) ||
			(m_CombatAI.m_CurMove->Done() == true))
		{
			return (false);
		}
	}

	dest = m_CombatAI.m_LastMoveDest;
	return (true);
}

void CombatTacticInterface::ClearMoveOrder()
{
	m_CombatAI.ClearMoveOrder();
	m_LastMoveDestScore = 0;
}

bool CombatTacticInterface::ProjectileApproaching(Stuff::Scalar interval)
{
	const Stuff::Scalar min_reaction_delay = 0.2f;
	Verify(interval > min_reaction_delay);

	if ((m_CombatAI.m_LastTimeProjectileShotAtMe == 0) ||
		(gos_GetElapsedTime() < m_CombatAI.m_LastTimeProjectileShotAtMe + min_reaction_delay))
	{
		return (false);
	}

	return (gos_GetElapsedTime() < m_CombatAI.m_LastTimeProjectileShotAtMe + interval);
}

const Stuff::Point3D& CombatTacticInterface::GetProjectileOrigin()
{
	return (m_CombatAI.m_LastProjectileOrigin);
}

void CombatTacticInterface::Stand()
{
	if (m_CombatAI.GetSelf_AsVehicle() == 0)
	{
		return;
	}

	if (m_CombatAI.GetSelf_AsVehicle()->IsDerivedFrom(Mech::DefaultData) == true)
	{
		Mech* mech = Cast_Object(Mech*,m_CombatAI.GetSelf_AsVehicle());

		mech->GetUpRequestRequest();
	}
}

void CombatTacticInterface::Crouch()
{
	m_CombatAI.Crouch();
}

bool CombatTacticInterface::Crouching() const
{
	return (m_CombatAI.Crouching());
}

void CombatTacticInterface::ThrottleOverride(Stuff::Scalar duration, bool force)
{
	m_CombatAI.ThrottleOverride(duration,force);
}

bool CombatTacticInterface::ShotWithin(Stuff::Scalar lastvalid) const
{
	TIME_FUNCTION(tCombatAITime_ShotWithin);

	if (m_CombatAI.ShotBy(gos_GetElapsedTime() - lastvalid) != 0)
	{
		return (true);
	}

	return (false);
}

bool CombatTacticInterface::FiredWithin(Stuff::Scalar lastvalid) const
{
	if (gos_GetElapsedTime() < m_CombatAI.GetLastFiredTime() + lastvalid)
	{
		return (true);
	}

	return (false);
}

void CombatTacticInterface::Jump()
{
	if (CanJump() == false)
	{
		return;
	}

	m_CombatAI.OrderJump();
}

void CombatTacticInterface::Jump(Stuff::Scalar duration)
{
	if (CanJump() == false)
	{
		return;
	}

	m_CombatAI.OrderJump(duration);
}

void CombatTacticInterface::StopJumping()
{
	m_CombatAI.OrderStopJumping();
}

bool CombatTacticInterface::GetUnableToFireDuration(Stuff::Scalar duration) const
{
	return (m_CombatAI.GetUnableToFireDuration(duration));
}

bool CombatTacticInterface::ShouldForceDestinationRecalc() const
{
	return (m_CombatAI.ShouldForceDestinationRecalc());
}

MechWarrior4::MWObject* CombatTacticInterface::GetNearest(bool fMustBeEnemy)
{
	return (m_CombatAI.GetNearest(fMustBeEnemy));
}

int CombatTacticInterface::GetEliteLevel() const
{
	return (m_CombatAI.EliteSkill());
}

Stuff::Scalar CombatTacticInterface::GetTimeTargetedByEnemy() const
{
	return (m_CombatAI.GetTimeTargetedByEnemy());
}

Stuff::Scalar CombatTacticInterface::GetAttackThrottle() const
{
	return (m_CombatAI.GetAttackThrottle());
}

void CombatTacticInterface::ForceDestinationRecalc()
{
	m_CombatAI.ForceDestinationRecalc();
}

Stuff::Point3D CombatTacticInterface::GetMoveDest() const
{
	return (m_CombatAI.GetMoveDest());
}

Stuff::Scalar CombatTacticInterface::GetLastTimeRammedTarget() const
{
	return (m_CombatAI.GetLastTimeRammedTarget());
}

Stuff::Scalar CombatTacticInterface::GetLastTimeJumped() const
{
	return (m_CombatAI.GetLastTimeJumped());
}

Stuff::LinearMatrix4D CombatTacticInterface::GetAttackOrderPosition() const
{
	if (m_CombatAI.m_LastAttackOrderPosition.GetPointer() == 0)
	{
		return (m_CombatAI.GetSelf().GetLocalToWorld());
	}

	return (*(m_CombatAI.m_LastAttackOrderPosition));
}

bool CombatTacticInterface::GetEscapeRegionFocusIfAvailable(Stuff::Point3D& focus) const
{
	return (m_CombatAI.GetEscapeRegionFocusIfAvailable(focus));
}

void CombatTacticInterface::SurrenderPose()
{
	m_CombatAI.SurrenderPose();
}

void CombatTacticInterface::SetSpeed(Stuff::Scalar speed)
{
	m_CombatAI.SetSpeed(speed);
}

bool CombatTacticInterface::SuicideIsOK() const
{
	return (m_CombatAI.SuicideIsOK());
}

unsigned int CombatTacticInterface::NumLegsDestroyed() const
{
	return (m_CombatAI.m_NumLegsBlownOff);
}

Stuff::Scalar CombatTacticInterface::GetMovementScore() const
{
	return (m_LastMoveDestScore);
}

void CombatTacticInterface::SetMovementScore(Stuff::Scalar score)
{
	m_LastMoveDestScore = score;
}

bool CombatTacticInterface::PointIsInBadNeighborhood(const Stuff::Point3D& point) const
{
	return (m_CombatAI.PointIsInBadNeighborhood(point));
}

bool CombatTacticInterface::ShouldRun() const
{
	if (m_CombatAI.m_PerWeaponRayCasting == true)
	{
		return (true);
	}

	return (m_CombatAI.ShouldRun(true));
}

Stuff::Scalar CombatTacticInterface::GetCombatRadiusForRange(Stuff::Scalar percentile_range)
{
	return (m_CombatAI.GetCombatRadiusForRange(percentile_range));
}

Stuff::LinearMatrix4D CombatTacticInterface::GetLastMoveOrderPosition() const
{
	if (m_CombatAI.m_LastMoveOrderPosition.GetPointer() == 0)
	{
		return (m_CombatAI.GetSelf().GetLocalToWorld());
	}

	return (*(m_CombatAI.m_LastMoveOrderPosition));
}

Stuff::Scalar CombatTacticInterface::GetNormalizedJumpJetFuel()
{
	if (GetSelf().IsDerivedFrom(Mech::DefaultData) == false)
	{
		return (0);
	}

	Mech* mech = Cast_Object(Mech*,&(GetSelf()));
	Check_Object(mech);

	if ((mech->GetJumpJet() == 0) ||
		(mech->GetJumpJet()->GetGameModel() == 0) ||
		(mech->GetJumpJet()->GetGameModel()->m_burnTime <= 0))
	{
		return (0);
	}

	return (mech->GetJumpJet()->CurrentCharge() / mech->GetJumpJet()->GetGameModel()->m_burnTime);
}

bool CombatTacticInterface::CanRetreat() const
{
	std::vector<MWObject*> lancemates;
	Groups::GetLancemates(lancemates);

	if (std::find(lancemates.begin(),lancemates.end(),&(GetSelf())) != lancemates.end())
	{
		return (false);
	}

	return (true);
}

Stuff::Scalar CombatTacticInterface::GetCurrentVulnerableComponent()
{
	return (m_CombatAI.GetCurrentVulnerableComponent());
}

bool CombatTacticInterface::CanJump() const
{
	if (GetSelf().IsDerivedFrom(Mech::DefaultData) == false)
	{
		return (false);
	}

	Mech* mech = Cast_Object(Mech*,&GetSelf());

	if ((mech->GetJumpJet() == 0) ||
		(mech->GetJumpJet()->IsDestroyed() == true))
	{
		return (false);
	}

	return (true);
}

bool CombatTacticInterface::CanCrouch() const
{
	return (GetSelf().IsDerivedFrom(Mech::DefaultData));
}

void CombatTacticInterface::FloatToPoint(const Stuff::Point3D& point)
{
	m_CombatAI.FloatToPoint(point);
}

bool CombatTacticInterface::IsShootOnlyAI() const
{
	return (m_CombatAI.IsDerivedFrom(ShooterAI::DefaultData));
}

void CombatTacticInterface::SetFallDampingEnabled(bool enabled)
{
	m_CombatAI.SetFallDampingEnabled(enabled);
}

Adept::ObjectID CombatTacticInterface::GetObjectID() const
{
	return (m_CombatAI.GetSelf().objectID);
}

Stuff::Scalar CombatTacticInterface::GetMaxFireCheatAngle() const
{
	return (m_CombatAI.GetMaxFireCheatAngle());
}

bool CombatTacticInterface::LineMightHitFriendlyUnits(const Stuff::Line3D& line) const
{
	return (m_CombatAI.LineMightHitFriendlyUnits(line));
}

bool CombatTacticInterface::MovedWithin(Stuff::Scalar duration) const
{
	return (m_CombatAI.MovedWithin(duration));
}

void CombatTacticInterface::ContinuousFlyBy(const Stuff::Point3D& loc)
{
	m_CombatAI.ContinuousFlyBy(loc);
}

bool CombatTacticInterface::RecentPathsFailed() const
{
	return (m_CombatAI.RecentPathsFailed());
}

bool CombatTacticInterface::CanMove() const
{
	return (m_CombatAI.CanMove());
}

bool CombatTacticInterface::MoveRequestPending() const
{
	return (m_CombatAI.MoveRequestPending());
}

bool CombatTacticInterface::CanTrack() const
{
	return (m_CombatAI.CanTrack());
}

bool CombatTacticInterface::CanTurn() const
{
	return (m_CombatAI.CanTurn());
}

Stuff::Scalar CombatTacticInterface::DamageTakenSinceAttackOrder() const
{
	Stuff::Scalar damage = MW4AI::Damage::GetDamageRating(m_CombatAI.GetSelf());

	return (Stuff::Fabs(damage - m_CombatAI.m_LastAttackOrderDamageRating));
}

Stuff::Point3D CombatTacticInterface::GetLeashCenter() const
{
	return (m_CombatAI.m_CombatLeash);
}

Stuff::Scalar CombatTacticInterface::GetLeashRadius() const
{
	return (m_CombatAI.m_CombatLeashRadius);
}
