//===========================================================================//
// File: EntityManager.cpp                                                   //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 11/29/94  JMA  Initial coding.                                            //
// 08/25/97  JMA  Infrastructure changes									 //
// 08/25/97  ECH	Infrastructure changes									 //
//---------------------------------------------------------------------------//
// Copyright (C) 1994, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "AdeptHeaders.hpp"

#include "EntityManager.hpp"
#include "Application.hpp"
#include "Map.hpp"
#include "DropZone.hpp"
#include "Mission.hpp"
#include "Mover.hpp"
#include "CollisionGrid.hpp"
#include "Tile.hpp"
#include "DamageObject.hpp"
#include "Player.hpp"


//#############################################################################
//##########################    EntityStockpile    ############################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityStockpile::EntityStockpile():
	Plug(DefaultData),
	availableEntities(NULL)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityStockpile::~EntityStockpile()
{
	availableEntities.DeletePlugs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityStockpile::TestInstance()
{
}

//#############################################################################
//###########################    EntityManager    #############################
//#############################################################################

EntityManager::ClassData*
	EntityManager::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			EntityManagerClassID,
			"Adept::EntityManager",
			Plug::DefaultData
		);
	Register_Object(DefaultData);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityManager::EntityManager(ClassData *data):
	Plug(data),
	armory(53, NULL, true),
	deathRow(NULL),
	nameSocket(NULL, true),
	resourceIDSocket(2003, NULL, true),
	postCollisionExecutors(NULL),
	updateEntities(NULL),
	tileBoundDamage(3000>>3)
{
	Check_Pointer(this);
	isExecutionSuspended = false;

	playerReady = false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityManager::~EntityManager()
{
	//
	//------------------
	// Delete stockpiles
	//------------------
	//
	armory.DeletePlugs();
}

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

void EntityManager::SetPlayerReady(Entity *entity)
{
	playerReady = true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::StoreInArmory(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);
	const ResourceID &data_id = entity->GetDataListResourceID();
	Verify(data_id != ResourceID::Null);

	//
	//-----------------------------------
	// Verify that the entity is reusable
	//-----------------------------------
	//
	Verify(entity->IsReusable());
	Map::GetInstance()->RemoveChild(entity);
	entity->BecomeUninteresting();
	entity->owningConnection->RemoveReplicator(entity);

	//
	//--------------------------------------
	// Get the stockpile for its resource ID
	//--------------------------------------
	//
	EntityStockpile *stockpile;
	if ((stockpile = armory.Find(data_id)) == NULL)
	{
		gos_PushCurrentHeap(g_Heap);
		stockpile = new EntityStockpile;
		gos_PopCurrentHeap();
		Register_Object(stockpile);
		armory.AddValue(stockpile, data_id);
	}
	Check_Object(stockpile);
	
	//
	//--------------------------------------------
	// If it has already been entered, then return
	//--------------------------------------------
	//
	Verify(!stockpile->availableEntities.IsPlugMember(entity));
	stockpile->availableEntities.Add(entity);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity*
	EntityManager::RequestFromArmory(const ResourceID &instance_resource_ID)
{
	Check_Object(this);

	//
	//------------------------------------------------
	// Get the stockpile for this entity, if it exists
	//------------------------------------------------
	//
	EntityStockpile *stockpile;

	if ((stockpile = armory.Find(instance_resource_ID)) == NULL)
		return NULL;
	Check_Object(stockpile);
	if (stockpile->availableEntities.IsEmpty())
		return NULL;
	
	//
	//-----------------------------------------
	// Verify that an entity can be created now
	//-----------------------------------------
	//
	Check_Object(Application::GetInstance());
	Verify(!Application::GetInstance()->InPreCollisionPhase());

	//
	//-------------------------------------------
	// Grab an entity from the available entities
	//-------------------------------------------
	//
	Entity *entity;
	ChainIteratorOf<Entity*> iterator(&stockpile->availableEntities);
	entity = iterator.GetCurrent();
	Check_Object(entity);
	iterator.Remove();

	//
	//----------------------------------------------------------------
	// Otherwise, grab the oldest in use, and update position in chain
	//----------------------------------------------------------------
	//	
	Verify(entity->GetDataListResourceID() == instance_resource_ID);
	Verify(entity->IsReusable());
	return entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::EntityCreated(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	EntityManager::FryDeathRow()
{
	Check_Object(this);

	if (!deathRow.IsEmpty())
	{
		ChainIteratorOf<Entity*> iterator(&deathRow);
		Entity *entity;

		while ((entity = iterator.GetCurrent()) != NULL)
		{
			Check_Object(entity);

			//
			// If the entity is reusable, 
			// it needs to be entered into the armory
			//
			//This is armory stuff that is going to be rewritten
			if (entity->IsReusable())
			{
				{
//					LOGIC("Background Tasks::Fry Death Row::REMOVE");
					iterator.Remove();
				}
				{
//					LOGIC("Background Tasks::Fry Death Row::STORE");
					StoreInArmory(entity);
				}
			}
			else
			{
				{
//					LOGIC("Background Tasks::Fry Death Row::REMOVE");
					iterator.Remove();
				}
				{
//					LOGIC("Background Tasks::Fry Death Row::DELETE");
					Unregister_Object(entity);
					delete entity;
				}
			}
			iterator.First();
		}
		return true;
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::PrefixParentToName(
		const Stuff::MString& parent_name, 
		Entity *entity
	)
{
	Check_Object(this);
	Check_Object(entity);

	MString new_name;
	new_name = parent_name;
	new_name += "::";
	new_name += entity->instanceName;

	#if defined(_ARMOR)
		nameSocket.Remove(entity);
	#endif
	entity->instanceName = new_name;
	#if defined(_ARMOR)
		nameSocket.AddValue(entity, entity->instanceName);
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DropZone*
	EntityManager::GetDropZone(ObjectID zone_id)
{
	Check_Object(this);
	
	Entity *entity = NameTable::GetInstance()->FindData(zone_id);
	if((entity) && (entity != (Entity *)-1))
	{
		Check_Object(entity);
		Verify(entity->IsDerivedFrom(DropZone::DefaultData));
		DropZone *zone;
		zone = Cast_Object(DropZone *, entity);

		return zone;
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::AddPlayerVehicle(Mover *mover)
{
	Check_Object(this);
	Check_Object(mover);

}

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

void
	EntityManager::SetNetworkDamageBit(bool bit_value, int bit_number)
{
	
	int byte = bit_number >> 3;
	BYTE bit = (BYTE)(bit_number & 0x7);
	

	// no allocations are allowed now.  They are only allowed at start up inside
	// of BuildTileBoundDamageList

	Verify(byte+1 < tileBoundDamage.GetSize());

	BYTE *memory = (BYTE*)tileBoundDamage.GetPointer();
	
	if (bit_value)
	{
		memory[byte] |= 0x1<<bit;
	}
	else
	{
		// if bit is 3 than the third bit should be zeroed
		// 11111011
		memory[byte] &= ~(0x1<<bit);
	}

}

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

bool
	EntityManager::GetNetworkDamageBit(int bit_number)
{

	int byte = bit_number >> 3;
	BYTE bit = (BYTE)(bit_number & 0x7);
	
	Verify(byte+1 < tileBoundDamage.GetSize());

	BYTE *memory = (BYTE*)tileBoundDamage.GetPointer();


	return (memory[byte] & (0x1<<bit))?true:false;

}



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

void
	EntityManager::BuildTileBoundDamageList()
{

	Check_Object(CollisionGrid::Instance);
	
	BYTE row_size;
	BYTE column_size;
	CollisionGrid::Instance->GetSize(&row_size, &column_size);


	int current_byte = 0;


	for (WORD index = 0; index < (row_size * column_size); ++index)
	{
		
		Tile *tile = CollisionGrid::Instance->GetTile(index);
		Check_Object(tile);

		// go thru the children...
		
		int current_bit = current_byte<<3;
		int tile_entity_count = 0;

		tile->SetDamageBitStart(current_bit);

	
		Entity *entity;
		for (int i = 0; i < tile->GetDamagableEntityCount(); ++i)
		{

			entity = tile->GetDamagableEntity(i);
			Check_Object(entity);

			Verify(entity->IsDestroyable());
			
			entity->damageBit = current_bit;

			tileBoundDamage.WriteBit(entity->IsDestroyed());
			++current_bit;
			++tile_entity_count;

			if (tileBoundDamage.GetBytesRemaining() < 2)
			{
				tileBoundDamage.AllocateBytes(1000>>3);
			}
			
		}

	
		tileBoundDamage.WriteByteAlign();
		current_byte = tileBoundDamage.GetBytesUsed();

	
	}
	tileBoundDamage.Rewind();


}

void
	EntityManager::ResetTileBoundDamageList()
{

	Check_Object(CollisionGrid::Instance);
	
	BYTE row_size;
	BYTE column_size;
	CollisionGrid::Instance->GetSize(&row_size, &column_size);


	int current_byte = 0;


	tileBoundDamage.Rewind();

	for (WORD index = 0; index < (row_size * column_size); ++index)
	{
		
		Tile *tile = CollisionGrid::Instance->GetTile(index);
		Check_Object(tile);

		// go thru the children...
		
		int current_bit = current_byte<<3;
		int tile_entity_count = 0;

		tile->SetDamageBitStart(current_bit);

	
		Entity *entity;
		for (int i = 0; i < tile->GetDamagableEntityCount(); ++i)
		{

			entity = tile->GetDamagableEntity(i);
			Check_Object(entity);

			Verify(entity->IsDestroyable());

			tileBoundDamage.WriteBit(entity->IsDestroyed());
			++current_bit;
			++tile_entity_count;
			
		}

	
		tileBoundDamage.WriteByteAlign();
		current_byte = tileBoundDamage.GetBytesUsed();

	
	}
	tileBoundDamage.Rewind();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("Entity Manager");

	//
	//--------------------------------------------
	// Execute the mission if we are not suspended
	//--------------------------------------------
	//
	Check_Object(Mission::GetInstance());
	if (!isExecutionSuspended)
	{
		Mission::GetInstance()->PreCollisionExecute(till);
		PreCollisionNetworkEvents();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::PostCollisionExecute(Time till)
{
	Check_Object(this);
	POSTCOLLISION_LOGIC("Entity Manager");

	//
	//-------------------------------------------------
	// Execute all callback entities in insertion order
	//-------------------------------------------------
	//
	while (!postCollisionExecutors.IsEmpty())
	{
		ChainIteratorOf<Entity*> entities(&postCollisionExecutors);
		Entity *entity = entities.GetCurrent();
		Check_Object(entity);
		Verify(!entity->GetElement()->IsMatrixDirty());
		entity->PostCollisionExecute(till);
		Verify(!entity->newCollisions);
		Verify(!entity->GetElement()->IsMatrixDirty());
		postCollisionExecutors.RemovePlug(entity);
	}
	Verify(postCollisionExecutors.IsEmpty());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::UpdateEntities()
{
	Check_Object(this);

	return;

	// WE NO LONGER DO THIS FOR NETWORK UPDATES
	// EVERYTHING IS CLIENT SERVER BASED NOW

#if 0
	//
	//-------------------------------------------------
	// Execute all callback entities in insertion order
	//-------------------------------------------------
	//
	ChainIteratorOf<Entity*> entities(&updateEntities);
	Entity *entity;
	while ((entity = entities.GetCurrent()) != NULL)
	{
		Check_Object(entity);
		Verify(entity->GetReplicatorMode() == Replicator::MasterMode);

		//
		//---------------------------------------------------------------------
		// For each one, we need to make a stream for the message to write into
		// and then broadcast the message to the entities replicants
		//---------------------------------------------------------------------
		//
		DynamicMemoryStream stream;
		Entity::UpdateMessage *message = entity->ConstructUpdate(&stream);
		Check_Object(message);
		entity->DispatchToReplicants(message);

		//
		//---------------------------------------------------
		// Clean out each entry in the chain as it's executed
		//---------------------------------------------------
		//
		entity->updateFlags = 0;
		entities.Remove();
	}
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::RequestPostCollisionExecution(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	Verify(!postCollisionExecutors.IsPlugMember(entity));
	postCollisionExecutors.Add(entity);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::RemovePostCollisionExecution(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);
	Verify(!entity->newCollisions);

	if (entity->IsUsingPostCollision())
	{
		postCollisionExecutors.Remove(entity);
		entity->IgnorePostCollision();
	}
	Verify(!postCollisionExecutors.IsPlugMember(entity));

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::RequestUpdate(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	updateEntities.Add(entity);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	EntityManager::IsInPostCollisionExecution(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	return postCollisionExecutors.IsPlugMember(entity);
}

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

void EntityManager::ServeLocalEntities(Time till)
{
	STOP(("That's a bad little monkey!!!: EntityManager::ServeLocalEntities - VIRTUAL NOT IMPLEMENTED"));
}

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


void EntityManager::UpdateClientEntites(int connection, int message_type, Stuff::MemoryStream *message)
{
	STOP(("That's a bad little monkey!!!: EntityManager::UpdateLocalEntites - VIRTUAL NOT IMPLEMENTED"));
}

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

void
	EntityManager::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}
