#include "MW4Headers.hpp"

#include "Explosive.hpp"
#include "MWDamageObject.hpp"
#include "MWApplication.hpp"

#include <Adept\Effect.hpp>
#include <Adept\Map.hpp>
#include <Adept\EntityManager.hpp>
#include <Adept\CollisionGrid.hpp>
#include <Adept\CollisionVolume.hpp>

//#############################################################################
//###############################    Explosive   ##############################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Explosive::ClassData*
	Explosive::DefaultData = NULL;

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

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Explosive__GameModel,
		TimerLength,
		timerLength,
		Scalar
	);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Explosive*
	Explosive::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(message);
	gos_PushCurrentHeap(Heap);
	Check_Object(EntityManager::GetInstance());
	Explosive *new_entity;
	Entity *entity = EntityManager::GetInstance()->RequestFromArmory(message->dataListID);
	if (entity)
	{
		new_entity = Cast_Object(Explosive*, entity);
		new_entity->Reuse(message, base_id);
	}
	else
		new_entity = new Explosive(DefaultData, message, base_id, NULL);
	gos_PopCurrentHeap();
	Check_Object(new_entity);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Explosive::Explosive(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	WeaponMover(class_data, message, base_id, element)
{
	Check_Pointer(this);
	Check_Object(message);

	lastParameterization = gos_GetElapsedTime();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Explosive::~Explosive()
{
	DESTRUCTOR("Explosive");
	Check_Object(this);
	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Explosive::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("Explosive");
	
	UsePostCollision();			
	BaseClass::PreCollisionExecute(till);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Explosive::PostCollisionExecute(Time till)
{
	Check_Object(this);
	POSTCOLLISION_LOGIC("Explosive");

	WeaponMover::PostCollisionExecute(till);

	const GameModel *model = GetGameModel();
	Check_Object(model);

	if(GetTimeParameter(till) >= model->timerLength)
	{
		//
		//----------------------
		//We blow up and go boom
		//----------------------
		//
		DynamicArrayOf<CollisionData> collision_data(1);
		collision_data[0].m_timeSlice = 0.0f;
		collision_data[0].m_worldIntersectionPoint = GetLocalToWorld();
		collision_data[0].m_normal.x = 0.0f;
		collision_data[0].m_normal.y = 1.0f;
		collision_data[0].m_normal.z = 0.0f;
		collision_data[0].m_otherNormal.x = 0.0f;
		collision_data[0].m_otherNormal.y = 1.0f;
		collision_data[0].m_otherNormal.z = 0.0f;
		collision_data[0].m_otherEntity = NULL;
		PostCollision(&collision_data);
		SentenceToDeathRow();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void Explosive::DealSplashDamage(const CollisionData *data)
{
	Check_Object(this);
	Check_Object(CollisionGrid::Instance);
	
    extern g_nMR;
    if (g_nMR == 2)
        return;

	if (!Network::GetInstance()->AmIServer())
		return;

	Stuff::Sphere sphere;
	sphere.center = GetLocalToWorld();
	sphere.radius = splashRadius;

	CollisionGrid::Instance->FindEntitiesWithin(sphere, (Adept::CollisionGrid::WithinCallback)SplashCallBack, this);
	
	if (splashSize == 0)
		return;

	// iterate the list	

	ChainIteratorOf<Entity *> splashed_entities(&splashedEntities);
	Entity *hit_entity;
	while((hit_entity = splashed_entities.ReadAndNext()) != NULL)
	{	
		if (hit_entity->damageObject)
			hit_entity->damageObject->ClearTakenDamageFromThisEvent();
	}
	// Set the damage object that received direct damage so it won't take more.
	splashed_entities.First();

	while((hit_entity = splashed_entities.GetCurrent()) != NULL)
	{	
		if (hit_entity->damageObject)
		{
			//
			// If there is a damage object verify we aren't hitting it twice
			//
			if (!hit_entity->damageObject->GetTakenDamageFromThisEvent())
			{
				hit_entity->damageObject->SetTakenDamageFromThisEvent() ;
			}
			else
			{
			  splashed_entities.Remove();
			  continue;
			}
		}

// MSL 5.06 Armor Mode
		MWApplication *m_App;
		m_App = MWApplication::GetInstance ();
		m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn;

		Verify(splashSize != 0);
		Adept::Entity__TakeDamageMessage message(
			hit_entity->GetReplicatorID(),
			inflictingEntityID,
			damageAmount,
			m_ArmorMode,
			SplashDamageType,
			data->m_normal,
			data->m_worldIntersectionPoint,
			heatAmount,
			false,
			itemID
		);
		hit_entity->Receive(&message);
		splashed_entities.Remove();
	}
}

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


