#include "MW4Headers.hpp"

#include "AI_SituationalAnalysis.hpp"

#include "Vehicle.hpp"
#include "Sensor.hpp"
#include "Torso.hpp"
#include "MWMission.hpp"
#include "rail_move.hpp"
#include "Mech.hpp"
#include "AI_Action.hpp"
#include "Airplane.hpp"



const unsigned int max_enemies_list_size = 7;
const Stuff::Scalar prediction_time = 2.0f;
const Stuff::Scalar max_height_differential = 0.45f;

extern __int64 tCombatAITime_SituationalAnalysis;
extern __int64 tCombatAITime_SituationalAnalysis_Generate;
extern __int64 tCombatAITime_SituationalAnalysis_Evaluate;
extern __int64 tCombatAITime_SituationalAnalysis_PointRemoval;

using namespace MW4AI;
using namespace MW4AI::SituationalAnalysis;

bool MW4AI::SituationalAnalysis::PointEvaluator::m_PostEvaluationState = false;


bool PointIsOutsideMissionBounds(const Stuff::Point3D& point)
{
	Verify(Adept::Mission::GetInstance() != 0);
	Verify(Adept::Mission::GetInstance()->IsDerivedFrom(MechWarrior4::MWMission::DefaultData) == true);
	MechWarrior4::MWMission* mission = Cast_Object(MechWarrior4::MWMission*,Adept::Mission::GetInstance());

	if (mission->GetMissionArea(point) != MWMission::InMissionArea)
	{
		return (true);
	}

	return (false);
}

inline void GetEnemiesList(MechWarrior4::MWObject& self, enemies_list& enemies)
{
	Verify(&self != 0);

	if (self.GetSensor() == 0)
	{
		return;
	}

	const MechWarrior4::Sensor::sensorDataArray& sensed_data = self.GetSensor()->RefreshAndGetSensorData();

	{for (int i = 0;
		  i < self.GetSensor()->numberOfContacts;
		  ++i)
	{
		MechWarrior4::SensorData* d = sensed_data[i];

		if ((d != 0) &&
			(d->object.GetCurrent() != 0) &&
			(d->object.GetCurrent()->objectID != self.objectID) &&
			(d->object.GetCurrent()->GetAlignment() != self.GetAlignment()) &&
			(d->object.GetCurrent()->IsDerivedFrom(MWObject::DefaultData)))
		{
			enemies.push_back(d->object.GetCurrent());
		}
	}}
}

inline void LimitEnemiesListSize(enemies_list& enemies, enemies_list::difference_type max_size)
{
	while (enemies.size() > max_size)
	{
		enemies_list::iterator element_to_erase = enemies.begin();
		element_to_erase += gos_rand() % enemies.size();

		enemies.erase(element_to_erase);
	}
}

inline bool PointIsValid(TacticInterface& iface, const Stuff::Point3D& my_pos, const Stuff::Point3D& point, bool ignore_mission_bounds)
{
	if ((ignore_mission_bounds == false) &&
		(iface.PointIsInBadNeighborhood(point) == true))
	{
		return (false);
	}

	return (iface.PointIsValid(point,ignore_mission_bounds));
}

inline bool PointShouldBeRemoved(TacticInterface& iface, const Stuff::Point3D& point, Stuff::Scalar minimum_distance, bool ignore_mission_bounds = false)
{
	const Stuff::Point3D my_pos(iface.GetSelf().GetLocalToWorld());
	minimum_distance *= minimum_distance;
	
	if ((minimum_distance != 0) &&
		(GetLengthSquared(point,my_pos) < minimum_distance))
	{
		return (true);
	}

	if (PointIsValid(iface,my_pos,point,ignore_mission_bounds) == false)
	{
		return (true);
	}

	return (false);
}

inline bool ShouldConsiderWater(TacticInterface& iface)
{
	if (UserConstants::Instance() == 0)
	{
		return (false);
	}

	if ((iface.GetEliteLevel() >= UserConstants::Instance()->Get(UserConstants::min_prefer_water)) &&
		(iface.GetEliteLevel() <= UserConstants::Instance()->Get(UserConstants::max_prefer_water)) &&
		(iface.GetSelf().IsDerivedFrom(MechWarrior4::Mech::DefaultData) == true))
	{
		return (true);
	}

	return (false);
}

inline Fuzzy MW4AI::SituationalAnalysis::GetWaterValue(const Stuff::Point3D& point)
{
	if (g_MissionGraph != 0)
	{
		if ((point.x <= MW4AI::MinX) ||
			(point.x >= MW4AI::MaxX) ||
			(point.z <= MW4AI::MinZ) ||
			(point.z >= MW4AI::MaxZ))
		{
			return (0.5f);
		}

		unsigned short passability = g_MissionGraph->Passable(point.x,point.z);
		int water = (passability & WATER_MASK) >> WATER_SHIFT;

		switch (water)
		{
			case NO_WATER:
			{
				return (0.5f);
			}
			case SHALLOW_WATER:
			{
				return (0.8f);
			}
			case MEDIUM_WATER:
			{
				return (1.0f);
			}
			case DEEP_WATER:
			{
				return (0);
			}
		}
	}

	return (1);
}

inline Fuzzy MW4AI::SituationalAnalysis::EvaluatePoint(TacticInterface& iface,
												const Stuff::Point3D& point,
												const PointEvaluator& evaluator,
												const enemies_list* enemies)
{
	COMBAT_LOGIC("Update Attacking::Situation Analysis::Evaluate");
	TIME_FUNCTION(tCombatAITime_SituationalAnalysis_Evaluate);

	bool consider_water(ShouldConsiderWater(iface));

	Fuzzy rv;
	
	if (enemies != 0)
	{
		rv = evaluator.Evaluate(iface,*enemies,point);
	}
	else
	{
		enemies_list found_enemies;
		GetEnemiesList(iface.GetSelf(),found_enemies);
		LimitEnemiesListSize(found_enemies,max_enemies_list_size);

		rv = evaluator.Evaluate(iface,found_enemies,point);
	}

	if (consider_water == true)
	{
		rv *= GetWaterValue(point);
	}

	Stuff::Point3D target_pos(iface.GetTarget().GetLocalToWorld());

	if ((Stuff::Fabs(point.y - target_pos.y) > 8.0f) &&		// fudge factor to decide when height might matter
		(iface.GetSelf().GetTorso() != 0) &&
		(iface.GetSelf().GetTorso()->GetGameModel() != 0))
	{
		const MechWarrior4::Torso::GameModel* model = iface.GetSelf().GetTorso()->GetGameModel();

		Stuff::Scalar pitch_angle(iface.GetMaxFireCheatAngle() + model->pitchRadius);
		if (MatrixFacesPoint_PitchOnly(iface.GetTarget().GetLocalToWorld(),point,pitch_angle) == false)
		{
			return (0);
		}
	}

	Stuff::Point3D my_pos(iface.GetSelf().GetLocalToWorld());
	Stuff::Scalar height_differential(Stuff::Fabs(my_pos.y - point.y));
	my_pos.y = point.y;
	Stuff::Scalar distance_to_point(GetApproximateLength(my_pos,point));

	if (height_differential > distance_to_point * max_height_differential)
	{
		return (rv * 0.1f);
	}

	const Stuff::Scalar hide_direction = iface.GetCurrentVulnerableComponent();

	if (hide_direction != 0)
	{
		// TODO: should actually evaluate for all enemies -- also, target may be friendly (defend tactic)!!!
		const Stuff::Scalar yaw_to_point = YawToPoint(iface.GetSelf().GetLocalToWorld(),point);
		const Stuff::Scalar yaw_to_target = YawToPoint(iface.GetSelf().GetLocalToWorld(),target_pos);

		Stuff::Scalar diff = yaw_to_point - yaw_to_target;
		while (diff > (Stuff::Pi * 2))
		{
			diff -= (Stuff::Pi * 2);
		}

		while (diff < -(Stuff::Pi * 2))
		{
			diff += (Stuff::Pi * 2);
		}

		if (Actions::ShouldMoveForwardToPoint(iface,point) == true)
		{
			if (Sign(diff) == Sign(hide_direction))
			{
				rv *= 1.5f;
			}
			else
			{
				rv *= 0.75f;
			}
		}
		else
		{
			if (Sign(diff) != Sign(hide_direction))
			{
				rv *= 1.5f;
			}
			else
			{
				rv *= 0.75f;
			}
		}
	}

	Clamp(rv,0,1);

	return (rv);
}

inline void RankPoints(TacticInterface& iface,
					   const PointEvaluator& evaluator,
					   Stuff::Scalar minimum_distance,
					   const point_list& points,
					   ranked_point_list& map_of_points)
{
	enemies_list enemies;
	GetEnemiesList(iface.GetSelf(),enemies);
	LimitEnemiesListSize(enemies,max_enemies_list_size);

	{for (point_list::const_iterator i = points.begin();
		  i != points.end();
		  ++i)
	{
		ranked_point p(EvaluatePoint(iface,*i,evaluator,&enemies),*i);
		map_of_points.insert(p);
	}}
}

std::pair<bool,Stuff::Point3D> TryGenerator(TacticInterface& iface,
											const RegionGenerator& search_region_generator,
											const PointEvaluator& evaluator,
											Stuff::Scalar minimum_distance,
											int generation_iteration,
											bool ignore_mission_bounds)
{
	point_list points;

	{
		COMBAT_LOGIC("Update Attacking::Situation Analysis::Generate");
		TIME_FUNCTION(tCombatAITime_SituationalAnalysis_Generate);
		search_region_generator.Generate(iface,points,generation_iteration);
	}

	ranked_point_list map_of_points;
	RankPoints(iface,evaluator,minimum_distance,points,map_of_points);

	{
		COMBAT_LOGIC("Update Attacking::Situation Analysis::Point Removal");
		TIME_FUNCTION(tCombatAITime_SituationalAnalysis_PointRemoval);

		{for (ranked_point_list::reverse_iterator i = map_of_points.rbegin();
			  i != map_of_points.rend();
			  ++i)
		{
			if (PointShouldBeRemoved(iface,(*i).second,minimum_distance,ignore_mission_bounds) == false)
			{
				return (std::pair<bool,Stuff::Point3D>(true,(*i).second));
			}
		}}
	}

	return (std::pair<bool,Stuff::Point3D>(false,Stuff::Point3D::Identity));
}

Stuff::Point3D MW4AI::SituationalAnalysis::Evaluate(TacticInterface& iface,
													const RegionGenerator& search_region_generator,
													const PointEvaluator& evaluator,
													Stuff::Scalar minimum_distance)
{
	COMBAT_LOGIC("Update Attacking::Situation Analysis");
	TIME_FUNCTION(tCombatAITime_SituationalAnalysis);

	{for (int i = 0;
		  i < search_region_generator.Iterations();
		  ++i)
	{
		std::pair<bool,Stuff::Point3D> result(TryGenerator(iface,
														   search_region_generator,
														   evaluator,
														   minimum_distance,
														   i,
														   search_region_generator.IgnoreMissionBounds(iface)));
		if (result.first == true)
		{
			return (result.second);
		}
	}}

	Evaluator_DistanceFrom special_evaluator(FROM_TARGET,20,3000,NEAREST);

	Stuff::Point3D point;
	if (iface.GetEscapeRegionFocusIfAvailable(point) == true)
	{
		Generator_Circle circle(point,125);

		std::pair<bool,Stuff::Point3D> result(TryGenerator(iface,
														   circle,
														   special_evaluator,
														   minimum_distance,
														   0,
														   false));
		if (result.first == true)
		{
			return (result.second);
		}
	}

	Generator_EscapeRegion escape_region;

	std::pair<bool,Stuff::Point3D> result(TryGenerator(iface,
													   escape_region,
													   special_evaluator,
													   minimum_distance,
													   0,
													   true));
	if (result.first == true)
	{
		return (result.second);
	}

	return ((Stuff::Point3D)iface.GetSelf().GetLocalToWorld());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// helper functions
//

inline Stuff::Scalar GetNormalizedBoundedDistance(Stuff::Scalar value, Stuff::Scalar min, Stuff::Scalar max)
{
	if (value < min)
	{
		return (0);
	}

	if (value > max)
	{
		return (1);
	}

	return ((value - min) / (max - min));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_DistanceFrom
//

void MW4AI::SituationalAnalysis::PointEvaluator::SetEvaluationState(bool post_evaluation)
{
	m_PostEvaluationState = post_evaluation;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_DistanceFrom
//

Evaluator_DistanceFrom::Evaluator_DistanceFrom(FromWho who,
											   Stuff::Scalar min,
											   Stuff::Scalar max,
											   DistanceQualifier want_nearest)
	: m_Min(min)
	, m_Max(max)
	, m_Who(who)
	, m_WantNearest(want_nearest)
{
	Verify(m_Min < m_Max);
	Verify((m_WantNearest == NEAREST) || (m_WantNearest == FARTHEST));
}

Fuzzy Evaluator_DistanceFrom::Evaluate(TacticInterface& i,
									   const enemies_list& enemies,
									   const Stuff::Point3D& point) const
{
	Stuff::Point3D src;

	Fuzzy rv = 0;

	if (m_Who == FROM_ALL_ENEMIES)
	{
		rv = 1;

		if (enemies.size() > 0)
		{
			{for (enemies_list::const_iterator i = enemies.begin();
				  i != enemies.end();
				  ++i)
			{
				Stuff::Point3D future_pos;
				future_pos = (*i)->EstimateFuturePosition(&future_pos,prediction_time,false);

				Fuzzy d = GetNormalizedBoundedDistance(GetApproximateLength(future_pos,point),m_Min,m_Max);

				if (d < rv)
				{
					rv = d;
				}
			}}
		}
	}
	else
	{
		Stuff::Point3D other_point;

		switch (m_Who)
		{
			case FROM_SELF:
			{
				if (GetEvaluationState() == true)
				{
					other_point = (Stuff::Point3D)i.GetLastMoveOrderPosition();
				}
				else
				{
					other_point = (Stuff::Point3D)i.GetSelf().GetLocalToWorld();
				}

				break;
			}

			case FROM_TARGET:
			{
				other_point = (Stuff::Point3D)i.GetTarget().EstimateFuturePosition(&other_point,prediction_time,false);
				break;
			}

			case FROM_NEAREST_ENEMY:
			{
				if (i.GetNearest(true) == 0)
				{
					return (0.5f);	// TODO: what to return when there is no nearest?
				}

				other_point = (Stuff::Point3D)i.GetNearest()->EstimateFuturePosition(&other_point,prediction_time,false);
				break;
			};

			default:
			{
				Verify("Not supported." == 0);
				other_point = (Stuff::Point3D)i.GetSelf().GetLocalToWorld();
				break;
			}
		}

		rv = GetNormalizedBoundedDistance(GetApproximateLength(other_point,point),m_Min,m_Max);
	}

	Verify(rv >= 0);
	Verify(rv <= 1);

	if (m_WantNearest == NEAREST)
	{
		return (1 - rv);
	}

	return (rv);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_WeightedAverage
//

Evaluator_WeightedAverage::Evaluator_WeightedAverage(Stuff::Scalar weight1, PointEvaluator& evaluator1,
												     Stuff::Scalar weight2, PointEvaluator& evaluator2)
{
	m_Weights.push_back(weight1);
	m_Evaluators.push_back(&evaluator1);

	m_Weights.push_back(weight2);
	m_Evaluators.push_back(&evaluator2);
}

Evaluator_WeightedAverage::Evaluator_WeightedAverage(Stuff::Scalar weight1, PointEvaluator& evaluator1,
												     Stuff::Scalar weight2, PointEvaluator& evaluator2,
												     Stuff::Scalar weight3, PointEvaluator& evaluator3)
{
	m_Weights.push_back(weight1);
	m_Evaluators.push_back(&evaluator1);

	m_Weights.push_back(weight2);
	m_Evaluators.push_back(&evaluator2);

	m_Weights.push_back(weight3);
	m_Evaluators.push_back(&evaluator3);
}

Fuzzy Evaluator_WeightedAverage::Evaluate(TacticInterface& i,
										  const enemies_list& enemies,
										  const Stuff::Point3D& point) const
{
	Verify(m_Evaluators.size() > 0);
	Verify(m_Weights.size() == m_Evaluators.size());

	Fuzzy running_total = 0;
	Stuff::Scalar sum_of_weights = 0;

	{for (int index = 0;
		  index < m_Evaluators.size();
		  ++index)
	{
		Verify(m_Weights[index] > 0);

		running_total += (m_Evaluators[index]->Evaluate(i,enemies,point) * m_Weights[index]);
		sum_of_weights += m_Weights[index];
	}}

	return (running_total / sum_of_weights);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_InFrontOf -- used as a last resort when we can't find a decent spot to move to
//

Fuzzy Evaluator_Random::Evaluate(TacticInterface& i,
						   const enemies_list& enemies,
						   const Stuff::Point3D& point) const
{
	return (Stuff::Random::GetFraction());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_InFrontOf -- used as a last resort when we can't find a decent spot to move to
//

Evaluator_InFrontOf::Evaluator_InFrontOf(FromWho who,
										 bool in_front,
										 bool use_torso)
	: m_Who(who)
	, m_InFront(in_front)
	, m_UseTorso(use_torso)
{
	Verify(m_Who != FROM_ALL_ENEMIES);
	Verify(m_Who != FROM_NEAREST_ENEMY);
}

Fuzzy Evaluator_InFrontOf::Evaluate(TacticInterface& i,
									const enemies_list& enemies,
									const Stuff::Point3D& point) const
{
	Stuff::LinearMatrix4D relative_to;

	if (m_Who == FROM_SELF)
	{
		if (GetEvaluationState() == true)
		{
			relative_to = i.GetLastMoveOrderPosition();
		}
		else
		{
			if ((m_UseTorso == true) &&
				(i.GetSelf().GetTorso() != 0) &&
				(i.GetSelf().GetTorso()->twistJoint != 0))
			{
				Check_Object(i.GetSelf().GetTorso());
				relative_to = i.GetSelf().GetTorso()->twistJoint->GetLocalToWorld();
			}
			else
			{
				relative_to = i.GetSelf().GetLocalToWorld();
			}
		}
	}
	else
	{
		if ((m_UseTorso == true) &&
			(i.GetTarget().GetTorso() != 0) &&
			(i.GetTarget().GetTorso()->twistJoint != 0))
		{
			Check_Object(i.GetTarget().GetTorso());
			relative_to = i.GetTarget().GetTorso()->twistJoint->GetLocalToWorld();
		}
		else
		{
			relative_to = i.GetTarget().GetLocalToWorld();
		}

		Stuff::Point3D future_pos;
		i.GetTarget().EstimateFuturePosition(&future_pos,prediction_time,false);
		relative_to.BuildTranslation(future_pos);
	}

	Stuff::Point3D delta;
	delta.Subtract(point,(Stuff::Point3D)relative_to);

	if (delta.GetLengthSquared() == 0)
	{
		return (1);
	}

	Stuff::UnitVector3D delta_normalized(delta);

	Stuff::UnitVector3D local_forward;
	relative_to.GetLocalForwardInWorld(&local_forward);

	Stuff::Scalar bearing = delta_normalized * local_forward;
	bearing = (bearing + 1) * 0.5f;

	if (m_InFront == false)
	{
		bearing = 1 - bearing;
	}

	if (bearing > 1)
	{
		return (1);
	}

	if (bearing < 0)
	{
		return (0);
	}

	return (bearing);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_45DegreesInFront -- used as a last resort when we can't find a decent spot to move to
//

Evaluator_45DegreesInFront::Evaluator_45DegreesInFront(bool random)
	: m_Random(random)
{
}

Fuzzy Evaluator_45DegreesInFront::Evaluate(TacticInterface& i,
										   const enemies_list& enemies,
										   const Stuff::Point3D& point) const
{
	Stuff::LinearMatrix4D relative_to;

	if (GetEvaluationState() == true)
	{
		relative_to = i.GetLastMoveOrderPosition();
	}
	else
	{
		relative_to = i.GetSelf().GetLocalToWorld();
	}

	Stuff::Point3D delta;
	delta.Subtract(point,(Stuff::Point3D)relative_to);

	if (delta.GetLengthSquared() == 0)
	{
		return (1);
	}

	Stuff::UnitVector3D delta_normalized(delta);

	Stuff::UnitVector3D local_forward;
	relative_to.GetLocalForwardInWorld(&local_forward);

	Stuff::Scalar bearing = delta_normalized * local_forward;
	bearing = (bearing + 1) * 0.5f;

	if (bearing > 0.8f)
	{
		if (m_Random == true)
		{
			return (Stuff::Random::GetFraction());
		}

		return (1);
	}

	return (0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_ToSideOf -- used as a last resort when we can't find a decent spot to move to
//

Evaluator_ToSideOf::Evaluator_ToSideOf(FromWho who,
									   bool to_side)
	: m_Who(who)
	, m_ToSide(to_side)
{
	Verify(m_Who != FROM_ALL_ENEMIES);
	Verify(m_Who != FROM_NEAREST_ENEMY);
}

Fuzzy Evaluator_ToSideOf::Evaluate(TacticInterface& i,
								   const enemies_list& enemies,
								   const Stuff::Point3D& point) const
{
	Stuff::LinearMatrix4D relative_to;

	if (m_Who == FROM_SELF)
	{
		if (GetEvaluationState() == true)
		{
			relative_to = i.GetLastMoveOrderPosition();
		}
		else
		{
			relative_to = i.GetSelf().GetLocalToWorld();
		}
	}
	else
	{
		relative_to = i.GetTarget().GetLocalToWorld();
	
		Stuff::Point3D future_pos;
		i.GetTarget().EstimateFuturePosition(&future_pos,prediction_time,false);
		relative_to.BuildTranslation(future_pos);
	}

	Stuff::Point3D delta;
	delta.Subtract(point,(Stuff::Point3D)relative_to);

	if (delta.GetLengthSquared() == 0)
	{
		return (1);
	}

	Stuff::UnitVector3D delta_normalized(delta);

	Stuff::UnitVector3D local_forward;
	relative_to.GetLocalForwardInWorld(&local_forward);

	// TODO: is this correct, or the inverse?
	Stuff::Scalar bearing = delta_normalized * local_forward;

	if (bearing < 0)
	{
		bearing = -bearing;
	}

	if (m_ToSide == true)
	{
		bearing = 1 - bearing;
	}

	if (bearing > 1)
	{
		return (1);
	}

	if (bearing < 0)
	{
		return (0);
	}

	return (bearing);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Generator_EscapeRegion -- used as a last resort when we can't find a decent spot to move to
//

unsigned int Generator_EscapeRegion::Iterations() const
{
	return (0);
}

void Generator_EscapeRegion::Generate(TacticInterface& i,
									  point_list& points,
									  int iteration) const
{
	Stuff::Point3D my_pos = (Stuff::Point3D)i.GetSelf().GetLocalToWorld();

	{for (int i = 0;
		  i < 40;
		  ++i)
	{
		Stuff::Point3D p = my_pos;
		p.x += (Stuff::Random::GetFraction() * 200) - 100;
		p.y += (Stuff::Random::GetFraction() * 200) - 100;
		p.z += (Stuff::Random::GetFraction() * 200) - 100;

		points.push_back(p);
	}}
}

bool Generator_EscapeRegion::IgnoreMissionBounds(TacticInterface& i) const
{
	return (true);
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Generator_Circle
//

Generator_Circle::Generator_Circle(FromWho who,
								   Stuff::Scalar magnitude,
								   Stuff::Scalar points_on_circumference,
								   Stuff::Scalar shrink_rate)
	: m_Who(who)
	, m_Origin(0,0,0)
	, m_Magnitude(magnitude)
	, m_ShrinkRate(0.8f)
	, m_Points(points_on_circumference)
{
	Stuff::Point3D pos;

	Verify(m_Points > 0);
	Verify(who != FROM_ALL_ENEMIES);
}

Generator_Circle::Generator_Circle(const Stuff::Point3D& origin,
								   Stuff::Scalar magnitude,
								   Stuff::Scalar points_on_circumference,
								   Stuff::Scalar shrink_rate)
	: m_Who(INVALID)		// TODO: fix this -- it's a kludge
	, m_Origin(origin)
	, m_Magnitude(magnitude)
	, m_ShrinkRate(shrink_rate)
	, m_Points(points_on_circumference)
{
	Stuff::Point3D pos;

	Verify(m_Points > 0);
}

unsigned int Generator_Circle::Iterations() const
{
	return (3);
}

Stuff::Point3D GetCircleOrigin(TacticInterface& i, FromWho who, const Stuff::Point3D& alternate_origin)
{
	switch (who)
	{
		case FROM_SELF:
		{
			return ((Stuff::Point3D)i.GetSelf().GetLocalToWorld());
		}

		case FROM_TARGET:
		{
			Stuff::Point3D rv;
			rv = i.GetTarget().EstimateFuturePosition(&rv,prediction_time,false);
			return (rv);
		}

		case FROM_NEAREST_ENEMY:
		{
			MechWarrior4::MWObject* nearest = i.GetNearest(true);
			if (nearest == 0)
			{
				return ((Stuff::Point3D)i.GetSelf().GetLocalToWorld());
			}

			Stuff::Point3D rv;
			rv = nearest->EstimateFuturePosition(&rv,prediction_time,false);
			return (rv);
		}
	}

	return (alternate_origin);
}

void Generator_Circle::Generate(TacticInterface& i,
								point_list& points,
								int iteration) const
{
	Stuff::Point3D pos = GetCircleOrigin(i,m_Who,m_Origin);

	Stuff::Point3D forward = pos;
	forward.x += 1;

	Stuff::Scalar increment = (Stuff::Pi * 2) / (Stuff::Scalar)m_Points;

	Stuff::Scalar magnitude = m_Magnitude;
	
	{for (int i = 0;
		  i < iteration;
		  ++i)
	{
		magnitude *= m_ShrinkRate;
	}}

	Stuff::Scalar random_angle_offset = Stuff::Random::GetFraction() * Stuff::Pi;

	{for (Stuff::Scalar i = 0;
		  i <= Stuff::Pi * 2;
		  i += increment)
	{
		Stuff::Point3D new_point(RotateVector(pos,forward,i + random_angle_offset,magnitude));
		points.push_back(new_point);
	}}
}

bool Generator_Circle::IgnoreMissionBounds(TacticInterface& i) const
{
	return (PointIsOutsideMissionBounds(GetCircleOrigin(i,m_Who,m_Origin)));
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Generator_InFrontOf
//

Generator_InFrontOf::Generator_InFrontOf(FromWho who,
										 Stuff::Scalar length,
										 Stuff::Scalar random_skew)
	: m_Who(who)
	, m_Length(length)
	, m_RandomSkew(random_skew)
{
}

unsigned int Generator_InFrontOf::Iterations() const
{
	return (1);
}

void Generator_InFrontOf::Generate(TacticInterface& i,
								   point_list& points,
								   int iteration) const
{
	Stuff::Point3D pos;
	Stuff::UnitVector3D local_forward;
	
	if (m_Who == TacticInterface::SELF)
	{
		pos = (Stuff::Point3D)i.GetSelf().EstimateFuturePosition(&pos,prediction_time,false);
		i.GetSelf().GetLocalToWorld().GetLocalForwardInWorld(&local_forward);
	}
	else
	{
		pos = (Stuff::Point3D)i.GetTarget().EstimateFuturePosition(&pos,prediction_time,false);
		i.GetTarget().GetLocalToWorld().GetLocalForwardInWorld(&local_forward);
	}

	{for (Stuff::Scalar index = 0;
		  index <= 1;
		  index += 0.1f)
	{
		Stuff::Point3D p(local_forward);
		p *= m_Length * index;
		p += pos;

		OffsetTargetPoint(p,m_RandomSkew);

		points.push_back(p);
	}}
}

bool Generator_InFrontOf::IgnoreMissionBounds(TacticInterface& i) const
{
	Stuff::Point3D pos;

	if (m_Who == TacticInterface::SELF)
	{
		return (PointIsOutsideMissionBounds((Stuff::Point3D)i.GetSelf().GetLocalToWorld()));
	}

	return (PointIsOutsideMissionBounds((Stuff::Point3D)i.GetTarget().GetLocalToWorld()));
}