#include "MW4Headers.hpp"

#include "Sensor.hpp"
#include "Vehicle.hpp"
#include "Building.hpp"
#include "MWPlayer.hpp"
#include "SubsystemClassData.hpp"
#include "mwmission.hpp"
#include "vehicleinterface.hpp"
#include "narc.hpp"
#include "mwapplication.hpp"

#include <Adept\EntityManager.hpp>
#include <Adept\CollisionGrid.hpp>
#include <Adept\NameTable.hpp>

#ifdef NEW_SENSOR_TIMING
__int64	tSensorTime;
__int64	tSensorFillTime;
__int64	tSensorCheckTime;
__int64	tVehicleTime;
DWORD	tTotalSensorsExecuted;
#endif;

Stuff::Time Sensor::s_LastSensorCheck = 0;

// jcem - begin
#define UNLIMITED_RADAR_RANGE 6000.0f
extern bool g_bUnlimitedRadarRange;
// jcem - end
HGOSHEAP
	g_SensorHeap = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SensorData::SensorData(
	MWObject *entity,
	Scalar target_distance_squared,
	int target_alignment,
	int chasis_type
)
	: Plug(Plug::DefaultData),
	object(NULL)
{
	object.Add(entity);
	distanceSquared = target_distance_squared;
	alignment = target_alignment;
	chasisType = chasis_type;
}

SensorData::SensorData()
	: Plug (Plug::DefaultData)
	, object(NULL)
	, distanceSquared(0.0f)
	, alignment(-1)
	, chasisType(-1)
{
}

SensorData::~SensorData()
{
}

//#############################################################################
//##########################    ExecutionStateEngine    #######################
//#############################################################################

Sensor::ExecutionStateEngine::ClassData*
	Sensor::ExecutionStateEngine::DefaultData = NULL;

const StateEngine::StateEntry
	Sensor::ExecutionStateEngine::StateEntries[]=
{
	STATE_ENTRY(Sensor__ExecutionStateEngine, Active),
	STATE_ENTRY(Sensor__ExecutionStateEngine, Passive),
	STATE_ENTRY(Sensor__ExecutionStateEngine, Beagle)
};

DECLARE_TIMER(static, Sensor_Time);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::ExecutionStateEngine::InitializeClass()
{
	Check_Object(StateEngine::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			Sensor__ExecutionStateEngineClassID,
			"Sensor::ExecutionStateEngine",
			Subsystem::ExecutionStateEngine::DefaultData,
			ELEMENTS(StateEntries), StateEntries,
			(Entity::ExecutionStateEngine::Factory)Make,
			(Entity::ExecutionStateEngine::FactoryRequest::Factory)
				&FactoryRequest::ConstructFactoryRequest
		);
	Register_Object(DefaultData);

	#ifdef NEW_SENSOR_TIMING
	AddStatistic("Sensors", "%", gos_timedata, (void*)&tSensorTime, 0);
	AddStatistic("Sensor Fill", "%", gos_timedata, (void*)&tSensorFillTime, 0);
	AddStatistic("Sensor Check", "%", gos_timedata, (void*)&tSensorCheckTime, 0);
	AddStatistic("Vehicle Time", "%", gos_timedata, (void*)&tVehicleTime, 0);
	AddStatistic("Total Sensors", "%", gos_DWORD, (void*)&tTotalSensorsExecuted, Stat_AutoReset + Stat_Graph);
	#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::ExecutionStateEngine::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Sensor::ExecutionStateEngine*
	Sensor::ExecutionStateEngine::Make(
		Sensor *weapon,
		FactoryRequest *request
	)
{
	Check_Object(weapon);
	Check_Object(request);
	gos_PushCurrentHeap(Heap);
	Sensor::ExecutionStateEngine *sensor =
		new Sensor::ExecutionStateEngine(DefaultData, weapon, request);
	gos_PopCurrentHeap();
	return sensor;
		
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::ExecutionStateEngine::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//#############################################################################
//###############################    Sensor      ##############################
//#############################################################################

Sensor::ClassData*
	Sensor::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			SensorClassID,
			"MechWarrior4::Sensor",
			Subsystem::DefaultData,
			0, NULL,
			(Entity::Factory)Make,
			(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
			ExecutionStateEngine::DefaultData,
			(Entity::GameModel::Factory)GameModel::ConstructGameModel,
			NULL,
			(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
			(Entity::GameModel::ModelWrite)GameModel::WriteToText,
			(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
			(Subsystem::StreamCreate)CreateStream
		);
	Register_Object(DefaultData);

	Initialize_Timer(Sensor_Time, "Sensor Time");

	Verify(!g_SensorHeap);
	g_SensorHeap = gos_CreateMemoryHeap("Sensor");
	Check_Pointer(g_SensorHeap);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	Check_Pointer(g_SensorHeap);
	gos_DestroyMemoryHeap(g_SensorHeap);
	g_SensorHeap = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Sensor*
	Sensor::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(message);
	gos_PushCurrentHeap(Heap);
	Sensor *new_entity =
		new Sensor(DefaultData, message, base_id, NULL);
	gos_PopCurrentHeap();
	Check_Object(new_entity);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Sensor::Sensor(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Subsystem(class_data, message, base_id, element),
	m_nearestEnemy(NULL),
	m_nearestFriendly(NULL),
	m_LastTimeUpdatedBuildingData(0),
	m_ContainsEntityLastIndex(0),
	m_ShutdownRange(0),
	m_PassiveRange(0),
	m_ECMRange(0),
	m_NormalRange(0),
	m_LastUpdateTime(0),
	m_NeedsSensorRefresh(false)
{
	Check_Object(message);

	numberOfContacts = 0;
	numberOfBuildingContacts = 0;
	m_UpdateTime = 2.0f;

	gos_PushCurrentHeap(g_SensorHeap);
	for(int i=0;i<32;i++)
	{
		sensorData[i] = new SensorData();
		Register_Object(sensorData[i]);
	}
	for(i=0;i<32;i++)
	{
		buildingData[i] = new SensorData();
		Register_Object(buildingData[i]);
	}

	gos_PopCurrentHeap();

	executionTimer = Stuff::Random::GetFraction()*2.0f;
	m_maxRange = 1000.0f;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Sensor::~Sensor()
{
	for(int i=0;i<32;i++)
	{
		Unregister_Object(sensorData[i]);
		delete sensorData[i];
	}
	for(i=0;i<32;i++)
	{
		Unregister_Object(buildingData[i]);
		delete buildingData[i];
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Sensor::CheckBuilding(Entity *entity, Entity *original_caller)
{
	Check_Object(entity);
	Check_Object(original_caller);

	Sensor *sensor = Cast_Object(Sensor*, original_caller);
	MWObject *mw_object = sensor->GetParentVehicle();

	int execution_state = sensor->executionState->GetState();
	Scalar target_distance_squared = mw_object->GetDistanceSquaredFrom(entity, true);
	if((!entity->IsDestroyed()) &&
		(entity->GetAlignment() != mw_object->GetAlignment()) &&
		(entity->GetAlignment() != Entity::DefaultAlignment))
	{
		switch(execution_state)
		{
			case ExecutionStateEngine::ActiveState:
				if(target_distance_squared <= 1000000)
				{
					Verify(entity->IsDerivedFrom(Building::DefaultData));
					MWObject *building = Cast_Object(MWObject *, entity);
					AddBuildingContact(building, target_distance_squared);
				}
				break;
			case ExecutionStateEngine::BeagleState:
				if(target_distance_squared <= 1440000)
				{
					Verify(entity->IsDerivedFrom(Building::DefaultData));
					MWObject *building = Cast_Object(MWObject *, entity);
					AddBuildingContact(building, target_distance_squared);
				}
				break;
			case ExecutionStateEngine::PassiveState:
				if(target_distance_squared <= 625000)
				{
					Verify(entity->IsDerivedFrom(Building::DefaultData));
					MWObject *building = Cast_Object(MWObject *, entity);
					AddBuildingContact(building, target_distance_squared);
				}
				break;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("Subsystem::Sensor");

	//
	//--------------------------------------------
	//Need to check for vehicles within our radius
	//--------------------------------------------
	//
	Start_Timer(Sensor_Time);

	if (m_NeedsSensorRefresh == true)
	{
		if (s_LastSensorCheck != gos_GetElapsedTime())
		{
			Refresh();
			s_LastSensorCheck = gos_GetElapsedTime();
			Stop_Timer(Sensor_Time);
		}

		return;
	}
	else
	{
		if (GetParentVehicle()->IsPlayerVehicle() == false)
		{
			return;
		}
	}

	if (executionTimer < m_UpdateTime)
	{
		Stuff::Scalar time_slice = GetTimeSlice(till);
		executionTimer += time_slice;

		Stop_Timer(Sensor_Time);
		return;
	}

	Refresh();

	lastExecuted = till;
	Stop_Timer(Sensor_Time);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Sensor::Refresh()
{
	executionTimer = 0.0f;
	m_NeedsSensorRefresh = false;

	#ifdef NEW_SENSOR_TIMING
	++tTotalSensorsExecuted;
	#endif

	m_nearestEnemy.Remove();
	m_nearestEnemyRange = 0.0f;
	m_nearestFriendly.Remove();
	m_nearestFriendlyRange = 0.0f;
	numberOfContacts = 0;

	CheckForContacts_VehiclesAndTurrets();
	CheckForNarc();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const Sensor::sensorDataArray& Sensor::RefreshAndGetSensorData()
{
	Start_Timer(Sensor_Time);

	if (m_LastUpdateTime + 2.0f < (Stuff::Scalar)gos_GetElapsedTime())
	{
		MWMission *mission = Cast_Object (MWMission *,MWMission::GetInstance ());
		Check_Object (mission);
		if (mission->m_VehicleAndTurretCellMap.ChecksumMatches(GetParentVehicle()->GetLocalToWorld(),
															   GetMaxRange(),
															   m_ChecksumList) == false)
		{
			if (s_LastSensorCheck == gos_GetElapsedTime())
			{
				m_NeedsSensorRefresh = true;

				Stop_Timer(Sensor_Time);
				return (GetSensorData());
			}

			Refresh();
			s_LastSensorCheck = gos_GetElapsedTime();
		}

		m_LastUpdateTime = (Stuff::Scalar)gos_GetElapsedTime();
	}

	Stop_Timer(Sensor_Time);
	return (GetSensorData());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Sensor::CheckForNarc (void)
{
	if (!GetParentVehicle ()->vehicleInterface)
		return;
	MWMission *mission = Cast_Object (MWMission *,MWMission::GetInstance ());
	Check_Object (mission);
	Narc *narc;
	narc = mission->GetNarc(GetParentVehicle()->GetAlignment());
	if (!narc)
	{
		GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::NARC_SIGNAL_STOP);
		return;
	}
	Scalar target_distance_squared = GetParentVehicle ()->GetDistanceSquaredFrom(narc, true);
//	MSL - Changed to reflect Max range of LRM missiles
//	Also changed to give tone if Inside that range...
//	if (target_distance_squared > (GetMaxRange()*GetMaxRange())) // jcem use func instead of variable directly...
//  MSL - if there is a function that checks for Max Range of a Weapon then change 2400.0f to that function
//	checking max range of LRM
	if (target_distance_squared < (1800.0f*1800.0f)) // jcem use func instead of variable directly...
	{
		{
			GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::NARC_SIGNAL_START);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::CheckForContacts_Buildings()
{
	#ifdef NEW_SENSOR_TIMING
	__int64 start_time = GetCycles();
	#endif

	MWMission *mission = Cast_Object (MWMission *,MWMission::GetInstance ());
	Check_Object (mission);

	m_ParentVehiclePos = GetParentVehicle()->GetLocalToWorld();

	DetermineRanges();

	SensorCellMap::list_type object_list;

	#ifdef NEW_SENSOR_TIMING
	__int64 fill_start = GetCycles();
	#endif

	mission->m_BuildingCellMap.Fill(GetParentVehicle()->GetLocalToWorld(),
									GetMaxRange(),
									object_list,
									m_BuildingChecksumList);

	#ifdef NEW_SENSOR_TIMING
	tSensorFillTime += GetCycles() - fill_start;
	__int64 check_start = GetCycles();
	#endif

	{for (SensorCellMap::list_type::const_iterator i = object_list.begin();
		  i != object_list.end();
		  ++i)
	{
		CheckBuilding(*i,this);
	}}

	#ifdef NEW_SENSOR_TIMING
	tSensorCheckTime += GetCycles() - check_start;
	tSensorTime += GetCycles() - start_time;
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::CheckForContacts_VehiclesAndTurrets()
{
	Check_Object(this);
	Check_Object(NameTable::GetInstance());

	#ifdef NEW_SENSOR_TIMING
	__int64 start_time = GetCycles();
	#endif

	MWMission *mission = Cast_Object (MWMission *,MWMission::GetInstance ());
	Check_Object (mission);

	m_ParentVehiclePos = GetParentVehicle()->GetLocalToWorld();

	DetermineRanges();

	SensorCellMap::list_type object_list;

	#ifdef NEW_SENSOR_TIMING
	__int64 fill_start = GetCycles();
	#endif

	mission->m_VehicleAndTurretCellMap.Fill(GetParentVehicle()->GetLocalToWorld(),
											GetMaxRange(),
											object_list,
											m_ChecksumList);

	#ifdef NEW_SENSOR_TIMING
	tSensorFillTime += GetCycles() - fill_start;
	__int64 check_start = GetCycles();
	#endif

	{for (SensorCellMap::list_type::const_iterator i = object_list.begin();
		  i != object_list.end();
		  ++i)
	{
		CheckEntity(**i);
	}}

	#ifdef NEW_SENSOR_TIMING
	tSensorCheckTime += GetCycles() - check_start;
	tSensorTime += GetCycles() - start_time;
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::UpdateBuildingData()
{
	Check_Object(this);

	numberOfBuildingContacts = 0;
	CheckForContacts_Buildings();

	m_LastTimeUpdatedBuildingData = (Scalar)gos_GetElapsedTime();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::DestroySubsystem()
{
	Check_Object(this);
	
//	numberOfContacts = 0;
//	Subsystem::DestroySubsystem();
//	EnterNeverExecuteState();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::AddBuildingContact(MWObject *vehicle, Scalar target_distance_squared)
{
	Check_Object(this);
	Check_Object(vehicle);

	if(numberOfBuildingContacts < 32)
	{
		buildingData[numberOfBuildingContacts]->object.Remove();
		buildingData[numberOfBuildingContacts]->object.Add(vehicle);
		buildingData[numberOfBuildingContacts]->distanceSquared = target_distance_squared;
		buildingData[numberOfBuildingContacts]->alignment = vehicle->GetAlignment();
		++numberOfBuildingContacts;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::SetSensorMode(int mode, bool has_beagle)
{
	Check_Object(this);
	Check_Object(executionState);

	sensorMode = mode;

	switch(mode)
	{
	case ActiveMode:
		if(has_beagle)
		{
			executionState->RequestState(ExecutionStateEngine::BeagleState);
			m_maxRange = 1200.0f;
		}
		else
		{
			executionState->RequestState(ExecutionStateEngine::ActiveState);
			m_maxRange = 1000.0f;
		}
		break;
	case PassiveMode:	
		executionState->RequestState(ExecutionStateEngine::PassiveState);
		m_maxRange = 250.0f;
		break;
	}
}

bool Sensor::SensorDataOK(SensorData& data, int alignment)
{
	MWObject *veh;

	veh = GetParentVehicle();
	int rel_align;
	if ((data.object.GetCurrent() == 0) || (data.object.GetCurrent()->IsDestroyed()))
		return false;

	rel_align = veh->GetRelativeAlignment (data.alignment);
	return (rel_align == alignment);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity*
	Sensor::GetNextEnemy(Entity *current_enemy, bool previous)
{
	Check_Object(this);
	if (MWApplication::GetInstance()->networkingFlag)
	{
		NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();
		if ((params->m_radarMode != 0) && (params->m_radarMode != 3))
		{
			return NULL;
		}
	}
	if(current_enemy)
	{
		for(int i=0; i< numberOfContacts; i++)
		{
			if(sensorData[i]->object.GetCurrent() == current_enemy)
			{
				if (previous == false)
				{
					for(int q=(i+1); q< numberOfContacts; q++)
					{
						if (SensorDataOK(*sensorData[q],Entity::Enemy) == true)
							return sensorData[q]->object.GetCurrent();
					}
					for(int y=0; y<=i; y++)
					{
						if (SensorDataOK(*sensorData[y],Entity::Enemy) == true)
							return sensorData[y]->object.GetCurrent();
					}
				}
				else
				{
					for(int q=(i-1); q >= 0; --q)
					{
						if (SensorDataOK(*sensorData[q],Entity::Enemy) == true)
							return sensorData[q]->object.GetCurrent();
					}
					for(int y=(numberOfContacts - 1); y > i; --y)
					{
						if (SensorDataOK(*sensorData[y],Entity::Enemy) == true)
							return sensorData[y]->object.GetCurrent();
					}
				}
			}
		}
	}

	for(int i=0; i< numberOfContacts; i++)
	{
		if (SensorDataOK(*sensorData[i],Entity::Enemy) == true)
			return sensorData[i]->object.GetCurrent();
	}

	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity*
	Sensor::GetNearestEnemy()
{
	Check_Object(this);
	Entity * entity = m_nearestEnemy.GetCurrent();
	if (MWApplication::GetInstance()->networkingFlag)
	{
		NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();
		if ((params->m_radarMode != 0) && (params->m_radarMode != 3))
		{
			return NULL;
		}
	}
	if (entity)
	{
		if (entity->IsDestroyed())
		{
			m_NeedsSensorRefresh = true;
			return NULL;
		}
	}
	
	return entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity*
	Sensor::GetNextFriendly(Entity *current_friendly, bool previous)
{
	Check_Object(this);
	if (MWApplication::GetInstance()->networkingFlag)
	{
		NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();
		if (params->m_radarMode == 2)
		{
			return NULL;
		}
	}
	if(current_friendly)
	{
		for(int i=0; i< numberOfContacts; i++)
		{
			if(sensorData[i]->object.GetCurrent() == current_friendly)
			{
				if (previous == false)
				{
					for(int q=(i + 1); q< numberOfContacts; q++)
					{
						if (SensorDataOK(*sensorData[q],Entity::Player) == true)
							return sensorData[q]->object.GetCurrent();
					}
					for(int y=0; y<=i; y++)
					{
						if (SensorDataOK(*sensorData[y],Entity::Player) == true)
							return sensorData[y]->object.GetCurrent();
					}
				}
				else
				{
					for(int q=(i-1); q >= 0; --q)
					{
						if (SensorDataOK(*sensorData[q],Entity::Player) == true)
							return sensorData[q]->object.GetCurrent();
					}
					for(int y=(numberOfContacts - 1); y > i; --y)
					{
						if (SensorDataOK(*sensorData[y],Entity::Player) == true)
							return sensorData[y]->object.GetCurrent();
					}
				}
			}
		}
	}

	for(int i=0; i< numberOfContacts; i++)
	{
		if (SensorDataOK(*sensorData[i],Entity::Player) == true)
			return sensorData[i]->object.GetCurrent();
	}

	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity*
	Sensor::GetNearestFriendly()
{
	Check_Object(this);
	if (MWApplication::GetInstance()->networkingFlag)
	{
		NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();
		if (params->m_radarMode == 2)
		{
			return NULL;
		}
	}
	return m_nearestFriendly.GetCurrent();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	Sensor::GetMaxRange()
{
	Check_Object(this);
	Check_Object(executionState);

    // jcem - begin
    if (g_bUnlimitedRadarRange)
        return UNLIMITED_RADAR_RANGE;
    // jcem - end

    return m_maxRange;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	Sensor::GetLastTimeUpdatedBuildingData() const
{
	return (m_LastTimeUpdatedBuildingData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Sensor::ContainsEntity(Adept::Entity* entity)
{
	if ((m_ContainsEntityLastIndex < numberOfContacts) &&
		(sensorData[m_ContainsEntityLastIndex]->object.GetCurrent() == entity))
	{
		return (true);
	}

	{for (int i = 0;
		  i < numberOfContacts;
		  ++i)
	{
		if (sensorData[i]->object.GetCurrent() == entity)
		{
			m_ContainsEntityLastIndex = i;
			return (true);
		}
	}}

	if ((entity != 0) &&
		(entity->IsDerivedFrom(Building::DefaultData) == true))
	{
		{for (int i = 0;
			  i < numberOfBuildingContacts;
			  ++i)
		{
			if (buildingData[i]->object.GetCurrent() == entity)
			{
				m_ContainsEntityLastIndex = numberOfContacts;
				return (true);
			}
		}}
	}

	return (false);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Sensor::CanSeeEntity(MWObject& mwobject)
{
	if ((mwobject.IsDestroyed() == true) ||
		(&mwobject == GetParentVehicle()) ||
		(mwobject.GetVisibleOnSensors() == false))
	{
		return (false);
	}

	if (mwobject.IsPlayerVehicle() == true)
	{
		if (mwobject.executionState->GetState() == Entity::ExecutionStateEngine::NeverExecuteState)
		{
			return (false);
		}
	}
	else
	{
		if (mwobject.HasAI() == 0)
		{
			if ((MWApplication::GetInstance()->networkingFlag == false) ||
				(mwobject.IsDerivedFrom(Mech::DefaultData) == false))
			{
				return (false);
			}
		}
	}

	return (true);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Scalar
	Sensor::SquaredRangeToEntity(MWObject& mwobject)
{
	Scalar x_delta = mwobject.GetLocalToWorld()(W_Axis, X_Axis) - m_ParentVehiclePos.x;
	x_delta *= x_delta;

	Scalar z_delta = mwobject.GetLocalToWorld()(W_Axis, Z_Axis) - m_ParentVehiclePos.z;
	z_delta *= z_delta;

	return(x_delta + z_delta);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Scalar
	Sensor::CurrentMaxDetectionRangeTo(MWObject& mwobject)
{
	if (mwobject.vehicleShutDown == true)
	{
		return (m_ShutdownRange);
	}

	if (mwobject.GetSensorMode() == PassiveMode)
	{
		return (m_PassiveRange);
	}

	if (mwobject.DoesHaveECM() == true)
	{
		return (m_ECMRange);
	}

	return (m_NormalRange);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::CheckEntity(MWObject& mwobject)
{
	Check_Object(executionState);

	if (CanSeeEntity(mwobject) == false)
	{
		return;
	}

	Stuff::Scalar detection_range_squared = CurrentMaxDetectionRangeTo(mwobject);

	if (detection_range_squared == 0)
	{
		return;
	}

	Stuff::Scalar target_distance_squared = SquaredRangeToEntity(mwobject);

	if (target_distance_squared > detection_range_squared)
	{
		return;
	}

	if (GetParentVehicle()->vehicleInterface)
	{
		if ((mwobject.vehicleJustPoweredUp) &&
			(GetParentVehicle()->GetRelativeAlignment(mwobject.GetAlignment()) == Adept::Entity::Enemy))
		{
			mwobject.vehicleJustPoweredUp = false;
			GetParentVehicle()->vehicleInterface->ReactToEvent(VehicleInterface::NEW_VEHICLE_RADAR_POWER_UP);
		}
	}

	int mwobject_alignment(mwobject.GetAlignment());
	int my_alignment(GetParentVehicle()->GetAlignment());

	if (mwobject_alignment != my_alignment)
	{
		if (mwobject_alignment != Entity::DefaultAlignment)
		{
			if ((!m_nearestEnemy.GetCurrent()) ||
				(m_nearestEnemyRange > target_distance_squared))
			{
				m_nearestEnemy.Remove();
				m_nearestEnemy.Add((Entity *)&mwobject);
				m_nearestEnemyRange = target_distance_squared;
			}
		}
	}
	else
	{
		if ((!m_nearestFriendly.GetCurrent()) ||
			(m_nearestFriendlyRange > target_distance_squared))
		{
			m_nearestFriendly.Remove();
			m_nearestFriendly.Add((Entity *)&mwobject);
			m_nearestFriendlyRange = target_distance_squared;
		}
	}

	if (numberOfContacts < 32)
	{
		SensorData* sensor_data = sensorData[numberOfContacts];
		sensor_data->object.Remove();
		sensor_data->object.Add(&mwobject);
		sensor_data->distanceSquared = target_distance_squared;
		sensor_data->alignment = mwobject_alignment;

		++numberOfContacts;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Sensor::DetermineRanges()
{
	MWMission *mission = Cast_Object (MWMission *,MWMission::GetInstance ());
	Check_Object (mission);
    // jcem - begin
	if (g_bUnlimitedRadarRange) {
		m_PassiveRange = m_ECMRange = m_NormalRange = m_ShutdownRange = UNLIMITED_RADAR_RANGE * UNLIMITED_RADAR_RANGE;
        return;
    }
    // jcem - end
	switch (executionState->GetState())
	{
		case ExecutionStateEngine::ActiveState:
		{
			m_PassiveRange =	500.0f * 500.0f;
			m_ECMRange =		500.0f * 500.0f;
			m_NormalRange =	   1000.0f * 1000.0f;
// MSL 5.04 Modified Shutdown Range
//			m_ShutdownRange =   50.0f * 50.0f;
// MSL 5.05 Modified Shutdown Range
			m_ShutdownRange =   500.0f * 500.0f;
			break;
		}	
		case ExecutionStateEngine::BeagleState:
		{
			m_PassiveRange =	600.0f * 600.0f;
// MSL 5.04 Modified Beagle Range
			m_ECMRange =		700.0f * 700.0f;
			m_NormalRange =	   1200.0f * 1200.0f;
//			m_ShutdownRange =	100.0f * 100.0f;
// MSL 5.05 Modified Shutdown Range
			m_ShutdownRange =	600.0f * 600.0f;
			break;
		}
		case ExecutionStateEngine::PassiveState:
		{
			m_ECMRange =		100.0f * 100.0f;
			m_NormalRange =		250.0f * 250.0f;
			m_ShutdownRange = 0;
			m_PassiveRange = 0;
			break;
		}
		default:
		{
			m_ShutdownRange = 0;
			m_PassiveRange = 0;
			m_ECMRange = 0;
			m_NormalRange = 0;
			break;
		}
	}
}

