#include "munga.h"
#pragma hdrstop

#include "boxsolid.h"
#include "fileutil.h"
#include "doorfram.h"
#include "door.h"
#include "app.h"
#include "notation.h"
#include "namelist.h"

//#############################################################################
// Shared Data Support
//
Derivation* DoorFrame::GetClassDerivations()
{	static Derivation classDerivations(UnscalableTerrain::GetClassDerivations(), "DoorFrame");
	return &classDerivations;
}


DoorFrame::SharedData
	DoorFrame::DefaultData(
		DoorFrame::GetClassDerivations(),
		DoorFrame::GetMessageHandlers(),
		DoorFrame::GetAttributeIndex(),
		DoorFrame::StateCount,
		(Entity::MakeHandler)DoorFrame::Make
	);
//#############################################################################
// Test Support
//
Logical
	DoorFrame::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}

//#############################################################################
// Construction and Destruction
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DoorFrame::DoorFrame(
	DoorFrame::MakeMessage *creation_message,
	DoorFrame::SharedData &virtual_data
):
	UnscalableTerrain(creation_message, virtual_data)
{
	Check_Pointer(this);
	Check(creation_message);
	Check(&virtual_data);

	SetValidFlag();
	SetPerformance(&DoorFrame::DoNothing);

	//
	//  Get the subsystem_resource from the resource file
	//
	Check(application);
	ResourceDescription *subsystem_resource =
		application->GetResourceFile()->SearchList(
			resourceID,
			ResourceDescription::SubsystemModelStreamResourceType
		);
	Check(subsystem_resource);
	subsystem_resource->Lock();
	Check_Pointer(subsystem_resource->resourceAddress);
	MemoryStream subsystem_stream(
		subsystem_resource->resourceAddress,
		subsystem_resource->resourceSize);

	subsystemCount = *(int*)subsystem_stream.GetPointer();
	subsystem_stream.AdvancePointer(sizeof(int));
	Verify(subsystemCount);

	//
	//	Set up Subsystems
	//
	subsystemArray = new (Subsystem(*[subsystemCount]));
	Register_Pointer(subsystemArray);

	//
	//	Attach the doors as defined in the mod file
	//
	for (int ii=BasicSubsystemCount; ii<subsystemCount; ++ii)
	{
		Door::SubsystemResource *subsystem_resource =
			(Door::SubsystemResource *)subsystem_stream.GetPointer();
		subsystemArray[ii] = new Door(this, ii, subsystem_resource);
		Register_Object(subsystemArray[ii]);
		subsystem_stream.AdvancePointer(subsystem_resource->subsystemModelSize);
	}

	subsystem_resource->Unlock();
	Check(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DoorFrame*
	DoorFrame::Make(DoorFrame::MakeMessage *creation_message)
{
	return new DoorFrame(creation_message);
}

Logical
	DoorFrame::CreateMakeMessage(
		MakeMessage *creation_message,
		NotationFile *model_file,
		const ResourceDirectories *directories
	)
{
	Check(creation_message);
	Check(model_file);

   if (
		!UnscalableTerrain::CreateMakeMessage(
			creation_message,
			model_file,
			directories
		)
	)
	{
		return false;
	}

	creation_message->classToCreate = RegisteredClass::DoorFrameClassID;
	creation_message->instanceFlags =
		MasterInstance|DynamicFlag|MapFlag|TrappedFlag;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DoorFrame::~DoorFrame()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription::ResourceID
	DoorFrame::CreateSubsystemStream(
		ResourceFile *resource_file,
		const char *model_name,
		NotationFile *model_file,
		const ResourceDirectories *directories
	)
{
	Check(resource_file);
	Check_Pointer(model_name);
	Check(model_file);
	Check_Pointer(directories);

	//
	//-------------------
	// Find the .sub file
	//-------------------
	//
	const char* entry_data;
	if (
		!model_file->GetEntry(
			"gamedata",
			"Subsystems",
			&entry_data
		)
	)
	{
		std::cerr << model_name << " is missing .sub file specification!\n";
		return -1;
	}

	char *sub_filename = MakePathedFilename(directories->modelDirectory, entry_data);
	Register_Pointer(sub_filename);

	NotationFile *sub_file = new NotationFile(sub_filename);
	Register_Object(sub_file);
	NameList *sub_namelist = sub_file->MakePageList();
	Register_Object(sub_namelist);
	int subsystem_count = sub_file->PageCount();
	if (!subsystem_count)
	{
		std::cerr << sub_filename << " empty of missing!\n";
Dump_And_Die:
		Unregister_Pointer(sub_filename);
		delete sub_filename;
		Unregister_Object(sub_file);
		delete sub_file;
		Unregister_Object(sub_namelist);
		delete sub_namelist;
		return -1;
	}
	int buf_size =
		subsystem_count * sizeof(Door::SubsystemResource) + sizeof(int);
	char *buffer = new char[buf_size];
	Register_Pointer(buffer);
	MemoryStream subsystem_stream(buffer, buf_size);

	*(int*)subsystem_stream.GetPointer() = subsystem_count;
	subsystem_stream.AdvancePointer(sizeof(int));

	NameList::Entry *sub_entry = sub_namelist->GetFirstEntry();
	while(sub_entry)
	{
		char subsystem_name[32];
		Str_Copy(subsystem_name, sub_entry->GetName(), sizeof(subsystem_name));
		const char *sub_type;
		if(!sub_file->GetEntry(
				subsystem_name,
				"Type",
				&sub_type
			)
		)
		{
		  std::cerr << model_name << ':' << subsystem_name << " missing Type!\n";
Dump_And_Die_2:
		  Unregister_Pointer(buffer);
		  delete[] buffer;
		  goto Dump_And_Die;
		}

		Subsystem::SubsystemResource *sub_res =
			(Subsystem::SubsystemResource*)subsystem_stream.GetPointer();
		if (!strcmp(sub_type, "DoorClassID"))
		{
			if (
				Door::CreateStreamedSubsystem(
					model_file,
               model_name,
					subsystem_name,
					(Door::SubsystemResource*)sub_res,
					sub_file,
					directories,
					resource_file
				) == -1
			)
			{
				goto Dump_And_Die_2;
			}
		}
		else
		{
			std::cerr << model_name << ':' << subsystem_name
			 << " has an unknown component type of " << sub_type << std::endl;
			goto Dump_And_Die_2;
		}
		subsystem_stream.AdvancePointer(sub_res->subsystemModelSize);
		sub_entry = sub_entry->GetNextEntry();
	}
	ResourceDescription *res_desc = resource_file->
		ResourceFile::AddResourceMemoryStream(
			model_name,
			ResourceDescription::SubsystemModelStreamResourceType,
			1,
			ResourceDescription::Preload,
			&subsystem_stream
		);
	ResourceDescription::ResourceID res_id;

	if(!res_desc)
	{
		res_id = ResourceDescription::NullResourceID;
	}
	else
	{
		 res_id = res_desc->resourceID;
	}

	Unregister_Pointer(sub_filename);
	delete sub_filename;
	Unregister_Object(sub_file);
	delete sub_file;
	Unregister_Object(sub_namelist);
	delete sub_namelist;
	Unregister_Pointer(buffer);
	delete[] buffer;
	return res_id;

}
