//===========================================================================//
// File:	Weapon.cpp										
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 09/09/98  JSE  Inital base class based off of Shadowrun/Adept			 //
// 09/24/98  BDB  Inital Weapon subsytem class based off of Subsystem		 //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"

#include "SearchLight.hpp"
#include "MWObject.hpp"
#include "LightEntity.hpp"
#include "SubsystemClassData.hpp"
//#include "torso.hpp"
#include <Adept\Mission.hpp>
#include <Adept\Map.hpp>
#include <Adept\Effect.hpp>
#include <Adept\CollisionGrid.hpp>
#include <Adept\Site.hpp>

//#############################################################################
//###############################    SearchLight    ###########################
//#############################################################################

SearchLight::ClassData*
	SearchLight::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SearchLight::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			SearchLightClassID,
			"MechWarrior4::SearchLight",
			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);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		SearchLight__GameModel,
		LightEntityResource,
		lightEntityResource,
		ResourceID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		SearchLight__GameModel,
		LightConeResource,
		lightConeResource,
		ResourceID
	);
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Replicator::CreateMessage*
	SearchLight::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
{
	Check_Object(this);
	Check_Object(stream);

	stream->AllocateBytes(sizeof(CreateMessage));
	Subsystem::SaveMakeMessage(stream, res_file);
	CreateMessage *message =
		Cast_Pointer(CreateMessage*, stream->GetPointer());
	message->messageLength = sizeof(*message);

	Str_Copy(message->siteName, (char *)siteName, sizeof(message->siteName));
	return message;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SearchLight::SearchLight(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Subsystem(class_data, message, base_id, element),
	lightEntity(NULL),
	lightConeEntity(NULL)
{
	Check_Pointer(this);
	Check_Object(message);

	siteName = message->siteName;
	sitePointer	= NULL; 

	CommonCreation(message);
	UsePostCollision();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SearchLight::CommonCreation(CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);

	if(!lightEntity.GetCurrent())
	{
		CreateSearchLight();
	}
	UsePostCollision();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SearchLight::Respawn(Entity::CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);
	
	Subsystem::Respawn(message);
	CreateMessage *SearchLight_message = Cast_Pointer(CreateMessage *, message);
	CommonCreation(SearchLight_message);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SearchLight::~SearchLight()
{
	HideBeam();
	if(lightEntity.GetCurrent())
	{
		lightEntity.GetCurrent()->SentenceToDeathRow();
	}
	if(lightConeEntity.GetCurrent())
	{
		lightConeEntity.GetCurrent()->SentenceToDeathRow();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SearchLight::PostCollisionExecute(Time till)
{
	Check_Object(this);
	POSTCOLLISION_LOGIC("Subsystem::Searchlight");
	//
	// [johnyo] 08/27/00 PreviousSearchLight code is experimental code that I have checked in to keep a backup
	//									 of only. It may be ripped out shortly if the effect that it was intended to have isn't
	//									 put into the game
	//

//	if(Mission::GetInstance()->DoesAllowSearchLights() && isLightOn == true)
	if(Mission::GetInstance()->DoesAllowSearchLights())
	{
		if(lightConeEntity.GetCurrent() == NULL)
		{
			const GameModel *model = GetGameModel();
			Check_Object(model);
			MWObject *parent = GetParentVehicle();
			Check_Object(parent);
			Check_Object(sitePointer);

			lightConeEntity.Remove();
			lightConeEntity.Add(
				parent->CreateEffect(model->lightConeResource, sitePointer, true));
			HideBeam();
		}

		if (
			(lightEntity.GetCurrent()) &&
			(lightEntity.GetCurrent()->lightIntensity > MidLevelRenderer::Limits::LightThreshold) &&
			(sitePointer != 0)
		)
		{
			Check_Object(sitePointer);
			Point3D new_translation;
			new_translation = sitePointer->GetLocalToWorld();
			UnitVector3D direction_vector;
			if(lightConeEntity.GetCurrent())
			{
				LinearMatrix4D new_position;
/*
				YawPitchRoll angle;
//				if (GetParentVehicle() && GetParentVehicle()->GetTorso())
//				{
					angle = lightConeEntity.GetCurrent()->effectOffset;
					angle.yaw = 0.0f;
					angle.roll = 0.0f;

//				}
//				else
//				{
//					angle = lightConeEntity.GetCurrent()->effectOffset;
//					angle.yaw = 0.0f;
//					angle.roll = 0.0f;
//					angle.pitch += GetParentVehicle()->GetTorso()->eyePitch;
//				}

				LinearMatrix4D offset = lightConeEntity.GetCurrent()->effectOffset;
//				offset.BuildRotation(angle);
				new_position.Multiply(
					offset, 
					sitePointer->GetLocalToWorld()
				);
*/
				new_position = lightConeEntity.GetCurrent()->GetLocalToWorld();
				new_translation = lightConeEntity.GetCurrent()->GetLocalToWorld();

				new_position.GetLocalLeftInWorld(&direction_vector);
			}
			else
			{
				sitePointer->GetLocalToWorld().GetLocalForwardInWorld(&direction_vector);
				direction_vector.y -= 0.2f;
			}

			Line3D target_line;
			Normal3D target_normal;
			
			target_line.SetOrigin(new_translation);
			target_line.SetDirection(direction_vector);
			target_line.m_length = 500;

			Entity::CollisionQuery target_query(&target_line, &target_normal, Entity::AlwaysCollidesMask, GetParentVehicle());
			Adept::Entity *entity_hit;
			Check_Object(CollisionGrid::Instance);
			entity_hit = CollisionGrid::Instance->ProjectLine(&target_query);
			Point3D light_point;
			target_query.m_line->FindEnd(&light_point);

			lightEntity.GetCurrent()->lightIntensity = target_query.m_line->m_length;


			light_point.y = target_query.m_line->m_length/* - 0.01f*/;
//			LinearMatrix4D new_local_to_world = sitePointer->GetLocalToWorld();

			LinearMatrix4D new_local_to_world = lightConeEntity.GetCurrent()->GetLocalToWorld();

			new_local_to_world.AlignLocalAxisToWorldVector(
				direction_vector, 
				Stuff::Z_Axis,
				Stuff::Y_Axis,
				Stuff::X_Axis
			);

//			new_local_to_world.BuildTranslation(light_point);

			lightEntity.GetCurrent()->SetNewLocalToParent(new_local_to_world);
			lightEntity.GetCurrent()->SyncMatrices(true);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool SearchLight::ConnectSubsystem()
{
	MWObject *mw_object = GetParentVehicle();
	Check_Object(mw_object);

	sitePointer = mw_object->FindSite(siteName); 
	HideBeam();

	//Must call the inherited virtual function!!!!
	return Subsystem::ConnectSubsystem();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void SearchLight::CreateSearchLight()
{
	Check_Object(this);
	const GameModel *model = GetGameModel();
	Check_Object(model);

	if(model->lightEntityResource != ResourceID::Null)
	{
		ClassID class_ID;
		class_ID = GetClassIDFromDataListID(model->lightEntityResource);
		Verify(class_ID != NullClassID);

		LightEntity::CreateMessage entity_create_message(
			sizeof(Entity::CreateMessage),
			DefaultEventPriority,
			CreateMessage::DefaultFlags,
			class_ID,
			LightEntity::DefaultFlags,
			model->lightEntityResource,
			GetLocalToWorld(),
			0.0f,
			ExecutionStateEngine::AlwaysExecuteState,
			NameTable::NullObjectID,
			Entity::DefaultAlignment
		);
		MemoryStream stream(&entity_create_message, entity_create_message.messageLength);

		Entity *entity;
		ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID();
		entity = CreateEntity(&stream, &beam_id, false);
		Check_Object(entity);
		if(Map::GetInstance())
			Map::GetInstance()->AddChild(entity);
		lightEntity.Remove();
		lightEntity.Add(Cast_Object(LightEntity *, entity));
		entity->SyncMatrices(true);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	SearchLight::TakeCriticalHit()
{
	Check_Object(this);

	//Don't do anything here
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void SearchLight::DestroySubsystem()
{
	Check_Object(this);

	Subsystem::DestroySubsystem();
	HideBeam();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void SearchLight::HideBeam()
{
	Check_Object(this);

	if(lightEntity.GetCurrent())
	{
	//	ElementRenderer::Element *element;
	//	element = lightEntity.GetCurrent()->GetElement();
	//	Check_Object(element);
	//	element->SetAlwaysCullMode();
		lightEntity.GetCurrent()->lightIntensity = 0.0f;
	}
	if(lightConeEntity.GetCurrent())
	{
		ElementRenderer::Element *element;
		element = lightConeEntity.GetCurrent()->GetElement();
		Check_Object(element);
		element->SetAlwaysCullMode();
	}

	isLightOn = false;
}	

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void SearchLight::ShowBeam()
{
	Check_Object(this);

	Check_Pointer(Mission::GetInstance());

	if(Mission::GetInstance()->DoesAllowSearchLights())
	{
		if(lightEntity.GetCurrent())
		{
			lightEntity.GetCurrent()->lightIntensity = 1.0f;
		}
		if(lightConeEntity.GetCurrent())
		{
			ElementRenderer::Element *element;
			element = lightConeEntity.GetCurrent()->GetElement();
			Check_Object(element);
			element->SetNeverCullMode();
			
		}

		isLightOn = true;
	}
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SearchLight::CreateStream(ResourceID data_list, MemoryStream *stream)
{
	Check_Pointer(stream);

	stream->AllocateBytes(sizeof(SearchLight__CreateMessage));
	Subsystem::CreateStream(data_list, stream);

	SearchLight__CreateMessage *create_message = Cast_Pointer(
		SearchLight__CreateMessage *, stream->GetPointer());

	Str_Copy(create_message->siteName, _strlwr("site_light"), sizeof(create_message->siteName));
}
