#include "munga.h"
#pragma hdrstop

#include "registry.h"
#include "player.h"
#include "mission.h"
#include "terrain.h"
#include "cultural.h"
#include "dropzone.h"
#include "explode.h"
#include "doorfram.h"
#include "eyecandy.h"
#include "director.h"
#include "camship.h"
#include "app.h"
#include "hostmgr.h"

//#############################################################################
//#############################    Registry    ################################
//#############################################################################

//#############################################################################
// Message Support
//
const Receiver::HandlerEntry
	Registry::MessageHandlerEntries[]=
{
	{
		Registry::MakeEntityMessageID,
		"MakeEntity",
		(Registry::Handler)&Registry::MakeEntityMessageHandler
	}
};

Registry::MessageHandlerSet& Registry::GetMessageHandlers()
{
	static Registry::MessageHandlerSet messageHandlers(ELEMENTS(Registry::MessageHandlerEntries), Registry::MessageHandlerEntries, Receiver::GetMessageHandlers());
	return messageHandlers;
}

//#############################################################################
// Shared Data support
//
Derivation* Registry::GetClassDerivations()
{
	static Derivation classDerivations(Receiver::GetClassDerivations(), "Registry");
	return &classDerivations;
}

Registry::SharedData
	Registry::DefaultData(
		Registry::GetClassDerivations(),
		Registry::GetMessageHandlers()
	);

//
//#############################################################################
// Registry
//#############################################################################
//
Registry::Registry(
	ClassID class_ID,
	SharedData &shared_data
):
	Receiver(class_ID, shared_data)
{
}

//
//#############################################################################
// Registry
//#############################################################################
//
Registry::Registry():
	Receiver(TrivialReceiverClassID, DefaultData)
{
}

//
//#############################################################################
// ~Registry
//#############################################################################
//
Registry::~Registry()
{
}

//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
	Registry::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}

//
//#############################################################################
// MakeEntity
//#############################################################################
//
Entity*
	Registry::MakeEntity(Entity::MakeMessage *message)
{
   Check(this);
   Check(message);

   Entity::SharedData *static_data = GetStaticData(message->classToCreate);

   if (static_data != NULL)
   {
   	Check(static_data);
      Check_Pointer(static_data->makeHandler);
      return (*static_data->makeHandler)(message);
   }
   return NULL;
}

//
//#############################################################################
// MakeTransferedEntity
//#############################################################################
//
Entity*
	Registry::MakeTransferedEntity(
		HostID,
		EntityID,
		void*
	)
{
	Fail("Registry::MakeTransferedEntity - under construction");
	return NULL;
}

//
//#############################################################################
// MakeEntityReplacement
//#############################################################################
//
Entity*
	Registry::MakeEntityReplacement(
		HostID,
		EntityID
	)
{
	Fail("Registry::MakeEntityReplacement - under construction");
	return NULL;
}

//
//#############################################################################
// GetStaticData
//#############################################################################
//
Entity::SharedData*
	Registry::GetStaticData(ClassID class_ID)
{
	switch (class_ID)
	{
	case TrivialEntityClassID:
		return &Entity::DefaultData;
	case DropZoneClassID:
		return &DropZone::DefaultData;
	case TerrainClassID:
		return &Terrain::DefaultData;
	case CulturalIconClassID:
		return &CulturalIcon::DefaultData;
	case LandmarkClassID:
		return &Landmark::DefaultData;
	case TeamClassID:
		return &Team::DefaultData;
	case UnscalableTerrainClassID:
		return &UnscalableTerrain::DefaultData;
	case ExplosionClassID:
		return &Explosion::DefaultData;
	case PlayerClassID:
		return &Player::DefaultData;
	case DoorFrameClassID:
		return &DoorFrame::DefaultData;
	case EyeCandyClassID:
		return &EyeCandy::DefaultData;
	case CameraShipClassID:
		return &CameraShip::DefaultData;
	case CameraDirectorClassID:
		return &CameraDirector::DefaultData;
	default:
		return NULL;
	}
}

//
//#############################################################################
// GetStaticData
//#############################################################################
//
Player*
	Registry::MakePlayer(Mission *mission)
{
	Check(mission);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// See if we are a CameraShip
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	Enumeration player_flags = Player::DefaultFlags;
	if (!strcmp(mission->GetGameModel(), "camera"))
	{
		player_flags |= Player::CameraShipPlayerFlag;
	}
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// See if we are a MissionReview, CameraShipHost
	// GameMachine CameraShip or regular Player!
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	Check(application);
	HostManager *host_mgr = application->GetHostManager();
	Host *host_ptr = host_mgr->GetLocalHost();
	Check(host_ptr);
	HostType host_type = host_ptr->GetHostType();

	switch(host_type)
	{
		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Make A Player Which is a GameMachine Host
		// i.e. VTV, Mech, CameraShip w/controls
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		case GameMachineHostType:
		{
			Player::MakeMessage make_player(
				Player::MakeMessageID,
				sizeof(Player::MakeMessage),
				PlayerClassID,
				EntityID::Null,
				ResourceDescription::NullResourceID,
				player_flags,
				Origin::Identity,
				mission->GetPlayerBitmapIndex()
			);
			return new Player(&make_player);
		}
		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Make A CameraShip Or Mission Review
		// Not a CameraShip to Run on a POD!
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		case CameraShipHostType:
		case MissionReviewHostType:
		{
			//
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			// Add any extra flags for a director
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			//
			player_flags |= CameraDirector::DefaultFlags;
			CameraDirector::MakeMessage create_director(
				CameraDirector::MakeMessageID,
				sizeof(CameraDirector::MakeMessage),
				CameraDirectorClassID,
				EntityID::Null,
				ResourceDescription::NullResourceID,
				player_flags,
				Origin::Identity,
				mission->GetPlayerBitmapIndex()
			);
			return new CameraDirector(&create_director);
		}
	}
   return NULL;
}

//
//#############################################################################
// MakeEntityMessageHandler
//#############################################################################
//
void
	Registry::MakeEntityMessageHandler(MakeEntityMessage *message)
{
	Check(this);
	Check(message);
   Verify(message->messageID == MakeEntityMessageID);

   //
	// Extract the make message
   //
   MemoryStream
   	memory_stream(
      	message,
         message->messageLength
      );
   Entity::MakeMessage
   	*make_message;

   memory_stream.AdvancePointer(sizeof(MakeEntityMessage));
   make_message = Cast_Object(Entity::MakeMessage*, memory_stream.GetPointer());
   Check(make_message);

	//
   // Make the entity
   //
#if DEBUG_LEVEL>0
	Entity *entity = MakeEntity(make_message);
	if (entity)
	{
		Register_Object(entity);
	}
#else
	MakeEntity(make_message);
#endif
}

//
//#############################################################################
// CreateStaticObjectStreamResource
//#############################################################################
//
ResourceDescription::ResourceID
	Registry::CreateStaticObjectStreamResource(
		NotationFile *notation_file,
		ResourceFile *resource_file
	)
{
	Check(notation_file);
   Check(resource_file);

	//
	//--------------------------------------------------------------------------
	// Build the static object stream
	//--------------------------------------------------------------------------
	//
	RegistryObjectStream static_object_stream;

	Check(&static_object_stream);
	static_object_stream.BuildFromNotationFile(
		notation_file,
      resource_file
	);

	//
	//--------------------------------------------------------------------------
   // Add the resource
	//--------------------------------------------------------------------------
	//
	ResourceDescription *res_description;

	res_description =
		resource_file->AddResourceMemoryStream(
			"RegistryStaticObjectStream",
			ResourceDescription::RegistryStaticObjectStreamResourceType,
			1,
			ResourceDescription::Preload,
			&static_object_stream
		);
	Check(res_description);
	return res_description->resourceID;
}

//
//#############################################################################
// LoadStaticObjectStreamResource
//#############################################################################
//
void
	Registry::LoadStaticObjectStreamResource()
{
	Check(this);

	//
	//--------------------------------------------------------------------------
	// Create static objects
	//--------------------------------------------------------------------------
	//

#if 0
	//
	// Get static object stream resource
	//
	ResourceDescription *resource_description;

	Check(application);
	Check(application->GetResourceFile());
	resource_description =
		application->GetResourceFile()->FindResourceDescription(
			"RegistryStaticObjectStream",
			ResourceDescription::RegistryStaticObjectStreamResourceType
		);

	if (resource_description != NULL)
	{
		Check(resource_description);

		//
		// Parse the static object stream
		//
		RegistryObjectStream static_object_stream(resource_description);

		Check(&static_object_stream);
		static_object_stream.CreateObjects();
	}
#else
	//
	// Load all static object streams
	//
	ResourceFile *resource_file;
	ResourceDescription::ResourceID index, max_resource_ID;

	Check(application);
	resource_file = application->GetResourceFile();
	Check(resource_file);
	max_resource_ID = resource_file->GetMaxResourceID();

	for (index = 0; index <= max_resource_ID; index++)
	{
		ResourceDescription *resource_description =
			resource_file->FindResourceDescription(index);
		if (resource_description)
		{
			Check(resource_description);

			//
			// Load the static object streams
			//
			if (
				resource_description->resourceType ==
				ResourceDescription::RegistryStaticObjectStreamResourceType
			)
			{
				resource_description->Lock();
				//
				// Parse the static object stream
				//
				RegistryObjectStream static_object_stream(resource_description);

				Check(&static_object_stream);
				static_object_stream.CreateObjects();
				resource_description->Unlock();
			}
		}
	}
	resource_file->ReleaseUnlockedResources();
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~ Registry__MakeEntityMessage ~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
// Registry__MakeEntityMessage
//#############################################################################
//
Registry__MakeEntityMessage::Registry__MakeEntityMessage(size_t total_length):
   Receiver::Message(
      Registry::MakeEntityMessageID,
      total_length
   )
{
}

//
//#############################################################################
// Make
//#############################################################################
//
Registry__MakeEntityMessage*
	Registry__MakeEntityMessage::Make(Entity::MakeMessage *make_message)
{
	Check(make_message);

	//
   // Allocate memory for the make message
   //
	size_t
   	total_length;
	void
   	*buffer;

	total_length =
   	sizeof (Registry__MakeEntityMessage) + make_message->messageLength;
	buffer = new unsigned char[total_length];
   Check_Pointer(buffer);

	//
   // Build the make message
   //
   MemoryStream
		memory_stream(buffer, total_length);
	Registry__MakeEntityMessage
   	*make_entity_message;

   make_entity_message =
   	new (memory_stream.GetPointer()) Registry__MakeEntityMessage(total_length);
	Check(make_entity_message);

   memory_stream.AdvancePointer(sizeof(Registry__MakeEntityMessage));
   Mem_Copy(
   	memory_stream.GetPointer(),
      make_message,
      make_message->messageLength,
      memory_stream.GetBytesRemaining()
   );

   return make_entity_message;
}

//#############################################################################
//#######################    RegistryObjectStream    ##########################
//#############################################################################

//
//#############################################################################
//#############################################################################
//
RegistryObjectStream::RegistryObjectStream(
	ResourceDescription *resource_description
):
	PlugStream(resource_description)
{
}

//
//#############################################################################
//#############################################################################
//
RegistryObjectStream::RegistryObjectStream()
{
}

//
//#############################################################################
//#############################################################################
//
RegistryObjectStream::~RegistryObjectStream()
{
}

//
//#############################################################################
//#############################################################################
//
Logical
	RegistryObjectStream::TestInstance() const
{
	PlugStream::TestInstance();
	return True;
}

//
//#############################################################################
//#############################################################################
//
RegisteredClass*
	RegistryObjectStream::MakeObjectImplementation(Enumeration class_ID)
{
	Check(this);
	Verify(class_ID != RegisteredClass::NullClassID);

	RegisteredClass *object;

	switch (class_ID)
	{
	//
	// Static resources ...
	//
	case RegisteredClass::ExplosionResourceTableClassID:
		object = new ExplosionResourceTable(this);
		break;

	//
	// Inherited behavior
	//
	default:
		object = PlugStream::MakeObjectImplementation(class_ID);
		break;
	}
	return object;
}

//
//#############################################################################
//#############################################################################
//
void
	RegistryObjectStream::CreatedObjectImplementation(
		RegisteredClass *object,
		ObjectID object_ID
	)
{
	Check(this);
	Check(object);
   Verify(object_ID != NullObjectID);

	//
	// Decide which index to add object to
	//
   switch (object->GetClassID())
   {
	//
	// Static resources ...
	//
	case RegisteredClass::ExplosionResourceTableClassID:
		AddGlobalPlug(Cast_Object(Plug*, object), object_ID);
		break;

	default:
		break;
	}
}

//
//#############################################################################
//#############################################################################
//
void
	RegistryObjectStream::BuildFromPageImplementation(
      NameList *name_list,
      Enumeration class_ID_enumeration,
      ObjectID object_ID
   )
{
	Check(this);
   Verify(class_ID_enumeration != RegisteredClass::NullClassID);
	Verify(object_ID != NullObjectID);

	RegisteredClass::ClassID
		class_ID = (RegisteredClass::ClassID)class_ID_enumeration;

	switch (class_ID)
	{
	//
	// Static resources ...
	//
	case RegisteredClass::ExplosionResourceTableClassID:
		ExplosionResourceTable::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	//
	// Inherited behavior
	//
	default:
		PlugStream::BuildFromPageImplementation(name_list, class_ID, object_ID);
		break;
	}
}

//
//#############################################################################
//#############################################################################
//
void
	RegistryObjectStream::BuiltFromPageImplementation(
		Enumeration class_ID,
		ObjectID object_ID,
		const CString &object_name
	)
{
	Check(this);
   Verify(class_ID != RegisteredClass::NullClassID);
   Verify(object_ID != NullObjectID);
	Check(&object_name);

	//
   // Decide which index to add object to
	//
	switch (class_ID)
   {
	case RegisteredClass::ExplosionResourceTableClassID:
		AddGlobalObjectID(object_ID, object_name);
		break;

	default:
		break;
   }
}
