//===========================================================================//
// File:	Player.cpp														 //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/08/99  DPB  Created file                                               //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "AdeptHeaders.hpp"

#include "Player.hpp"
#include "Interface.hpp"
#include "Application.hpp"
#include "Map.hpp"
#include "EntityManager.hpp"
#include "Mission.hpp"
#include "DropZone.hpp"

//#############################################################################
//###############################    Player      ##############################
//#############################################################################

Player::ClassData*
	Player::DefaultData = NULL;


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Player::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			PlayerClassID,
			"Adept::Player",
			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);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Player::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
	GlobalPointers::ClearPointer(PlayerGlobalPointerIndex);	
}

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

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

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

	Verify(message->initialAge != 0.0f);
	//
	//------------------------------------------
	// Create the memory stream for the vehicle
	//------------------------------------------
	//
	Resource vehicle_resource(NULL);
	DynamicMemoryStream vehicle_stream;
	Check_Object(vehicle);
	vehicle->SaveMakeMessage(&vehicle_stream, NULL);
	vehicle_resource.Save(&vehicle_stream, NULL);
	STOP(("Not updated"));
	Check_Object(&vehicle_resource);
	message->vehicleResourceID = vehicle_resource.GetResourceID();
	//
	//-------------------------------------------
	// Create the memory stream for the interface
	//-------------------------------------------
	//
	Resource interface_resource(NULL);
	DynamicMemoryStream interface_stream;
	Check_Object(playerInterface);
	playerInterface->SaveMakeMessage(&interface_stream, NULL);
	interface_resource.Save(&interface_stream, NULL);
	Check_Object(&interface_resource);
	message->interfaceResourceID = interface_resource.GetResourceID();
	return message;
}

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


#if 0 // moved to mwplayer
	isDropped = false;
	

	Mission::GetInstance()->AddChild(this);

	//
	//----------------------
	// Create the interface
	//----------------------
	//
	playerInterface = NULL;
	interfaceResourceID = ResourceID::Null;
	if (GetReplicatorMode() == Replicator::ClientMasterMode || GetReplicatorMode() == Replicator::MasterMode)
	{
		Resource stream(message->interfaceResourceID);
		Verify(stream.DoesResourceExist());
		ReplicatorID id = owningConnection->GetNextReplicatorID();
		ReplicatorID interface_id = Connection::Hermit->GetNextReplicatorID();
		playerInterface = Cast_Object(Interface *, CreateEntity(&stream, &interface_id));
		Check_Object(playerInterface);
		interfaceResourceID = message->interfaceResourceID;
	}
	
	//
	//-------------------
	// Create the vehicle
	//-------------------
	//
	Resource stream(message->vehicleResourceID);
	Verify(stream.DoesResourceExist());

	vehicle = CreateEntity(&stream, base_id);
	Check_Object(vehicle);



	vehicle->SetPropType(PlayerPropType);
	Check_Object(Map::GetInstance());
	Map::GetInstance()->AddChild(vehicle);


	EntityManager::GetInstance()->RemoveMover((Mover*)vehicle);

	ReplicatorID mech_id = vehicle->GetReplicatorID();
	vehicle->instanceName = 
		Network::GetInstance()->GetConnection(mech_id.connectionID)->GetConnectionName();

	if(!vehicle->instanceName || !vehicle->instanceName.GetLength())
	{
		char buffer[10];
		sprintf(buffer, "%d::", owningConnection->GetID());
		MString new_name;
		new_name = buffer + vehicle->instanceName;
		vehicle->instanceName = new_name;
	}

	Check_Object(NameTable::GetInstance());
	vehicle->objectID = NameTable::GetInstance()->FindID(vehicle->instanceName);
	
	if(vehicle->objectID == NameTable::NullObjectID)
		NameTable::GetInstance()->AddEntry(vehicle, NameTable::VehicleArray);
	else
		NameTable::GetInstance()->SetData(vehicle);
	if (owningConnection != Connection::Local)
	{
		vehicle->SetAlignment(Entity::Enemy);
	}

	Check_Object(EntityManager::GetInstance());
	EntityManager::GetInstance()->AddMover((Mover*)vehicle);


	// connect to the drop zone

	if (message->initialAge != 0.0f)
	{
	
		ElementRenderer::Element *element = vehicle->GetElement();
		Check_Object(element);
		ReceiveDropZone(element->GetNewLocalToParent());


	}
	else
	{
		dropZone = NameTable::GetInstance()->FindID("PlayerStart");

		Stuff::LinearMatrix4D drop_location = Stuff::LinearMatrix4D::Identity;
		drop_location.BuildTranslation(Point3D::HellPoint);

		vehicle->SetNewLocalToParent(drop_location);
		vehicle->SyncMatrices(true);
		Map::GetInstance()->UpdateZone(vehicle);
	}


	//
	//----------------------------
	//Store the Vehicle ResourceID
	//----------------------------
	//
	vehicleResourceID = message->vehicleResourceID;


#endif

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Player::~Player()
{
	if (this == Player::GetInstance())
		GlobalPointers::ClearPointer(PlayerGlobalPointerIndex);	
}


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

void
	Player::Respawn(CreateMessage *message)
{
#if 0
	Check_Pointer(this);
	Check_Object(message);

	isDropped = false;
	

	//
	//----------------------
	// Respawn the interface
	//----------------------
	//

	Entity::CreateMessage *interface_respawn_message = NULL;

	
	interfaceResourceID = ResourceID::Null;
	if (GetReplicatorMode() == Replicator::ClientMasterMode || GetReplicatorMode() == Replicator::MasterMode)
	{
		Resource stream(message->interfaceResourceID);
		Verify(stream.DoesResourceExist());
		ReplicatorID id = owningConnection->GetNextReplicatorID();
		ReplicatorID interface_id = Connection::Hermit->GetNextReplicatorID();

		interface_respawn_message =
			Cast_Pointer(Entity::CreateMessage*, stream.GetPointer());

		//playerInterface->Respawn(new_message);
		//Check_Object(playerInterface);
		
	}
	
	//
	//-------------------
	// Create the vehicle
	//-------------------
	//
	Resource stream(message->vehicleResourceID);
	Verify(stream.DoesResourceExist());

	Entity::CreateMessage *new_message =
		Cast_Pointer(Entity::CreateMessage*, stream.GetPointer());
	vehicle->Respawn(new_message);
	Check_Object(vehicle);

	// we do this after the vehicle for a respawn since farther down 
	// the stream there is no reconnect
	if (interface_respawn_message != NULL)
	{	
		playerInterface->Respawn(new_message);
		Check_Object(playerInterface);
	}


	// connect to the drop zone
	if (message->initialAge != 0.0f)
	{
		ElementRenderer::Element *element = vehicle->GetElement();
		Check_Object(element);
		ReceiveDropZone(element->GetNewLocalToParent());

	}
	else
	{
		dropZone = NameTable::GetInstance()->FindID("PlayerStart");

		Stuff::LinearMatrix4D drop_location = Stuff::LinearMatrix4D::Identity;
		drop_location.BuildTranslation(Point3D::HellPoint);

		vehicle->SetNewLocalToParent(drop_location);
		vehicle->SyncMatrices(true);
		Map::GetInstance()->UpdateZone(vehicle);
	}
	if (owningConnection != Connection::Local)
	{
		vehicle->SetAlignment(Entity::Enemy);
	}


	//
	//----------------------------
	//Store the Vehicle ResourceID
	//----------------------------
	//
	vehicleResourceID = message->vehicleResourceID;
#endif

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Player::BecomeInteresting(bool render_me)
{
	Check_Object(this);
	LOAD_LOGIC("Become Interesting::Player");
	BaseClass::BecomeInteresting(render_me);

	//
	//------------------
	// Validate children
	//------------------
	//
	ChainIteratorOf<Entity*> children(&childEntityChain);
	Entity *entity;
	while ((entity = children.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		Verify(entity->GetInterestLevel() == DormantInterestLevel);
		entity->BecomeInteresting(render_me);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Player::AddChild(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);
	Verify(entity == playerInterface);
	BaseClass::AddChild(entity);
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Player::ChildPreCollisionChanged(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);
	Verify(entity == playerInterface);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Player::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("Player");

	Check_Object(this);
	oldLocalToWorld = vehicle->GetLocalToWorld();
	//
	//---------------------------------
	// Verify that this always executes
	//---------------------------------
	//
	Check_Object(executionState);
	Verify(
		executionState->GetState() != 
		ExecutionStateEngine::NeverExecuteState
	);
	BaseClass::PreCollisionExecute(till);

	//
	//-----------------
	// Execute children
	//-----------------
	//
	ChainIteratorOf<Entity*> children(&childEntityChain);
	Entity *entity;
	while ((entity = children.ReadAndNext()) != NULL)
	{
		if (entity != playerInterface)
		{
			Check_Object(entity);
			Verify(entity->GetInterestLevel() != DormantInterestLevel);
			entity->PreCollisionExecute(till);
			Verify(!entity->IsUsingPostCollision() || EntityManager::GetInstance()->IsInPostCollisionExecution(entity));
		}
		else
		{
			// make this guy have the last execute
			Check_Object(EntityManager::GetInstance());
			EntityManager::GetInstance()->RequestPostCollisionExecution(entity);

		}
	}
}




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

Stuff::LinearMatrix4D
	Player::GetEyePoint()
{
	if (playerInterface != NULL)
		return playerInterface->GetLocalToWorld();
	else
		return BaseClass::GetEyePoint();
}



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Player::ReceiveDropZone(const Stuff::LinearMatrix4D& drop_location)
{
	Check_Object(this);

	SetNewLocalToParent(drop_location);

	EntityManager::GetInstance()->SetPlayerReady(vehicle);
	vehicle->SetNewLocalToParent(drop_location);
	vehicle->SyncMatrices(true);
	Map::GetInstance()->UpdateZone(vehicle);

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void
	Player::ConnectToVehicle(void)
{
	isDropped = true;
	Check_Object(playerInterface);

	SyncMatrices(true);
	AddChild(playerInterface);

	if (Application::GetInstance()->m_localMissionParameters->m_runDedicated)
		playerInterface->BecomeInteresting(false);
	else
		playerInterface->BecomeInteresting(true);

}

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

