#include "AdeptHeaders.hpp"

#include "Map.hpp"
#include "RendererManager.hpp"
#include "Tool.hpp"
#include <ElementRenderer\GridElement.hpp>
#include "NameTable.hpp"
#include "Zone.hpp"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map__CreateMessage::ConstructCreateMessage(Script *script)
{
	Check_Object(script);
	#if defined(_ARMOR)
		int stack_level = Tool::Instance->GetStackLevel();
	#endif

	//
	//--------------
	// Set up stream
	//--------------
	//
	MemoryStream *message_stream = script->messageStream;
	Check_Object(message_stream);
	message_stream->AllocateBytes(sizeof(Map__CreateMessage));
	Entity__CreateMessage::ConstructCreateMessage(script);
	Map__CreateMessage *message =
		Cast_Pointer(
			Map__CreateMessage*,
			message_stream->GetPointer()
		);
	message->messageLength = sizeof(*message);

	//
	//-------------------------------------------------------------------------
	// No matter what the instance file said, all maps are independent entities
	//-------------------------------------------------------------------------
	//
	message->replicatorFlags &= ~Replicator::ReplicatorModeMask;
	message->replicatorFlags |= Replicator::IndependentMode;

	//
	//---------------------------------------------------------------
	// Point at the notation file and make other files relative to it
	//---------------------------------------------------------------
	//
	Page *page = script->instancePage;
	Check_Object(page);

	//
	//---------------------------------------------------------------------
	// Open up the contents file, and see if its empty.  If so, we are done
	//---------------------------------------------------------------------
	//
	NotationFile props_file;
	page->GetEntry("Props", &props_file);
	NotationFile::PageIterator *pages = props_file.MakePageIterator();
	Check_Object(pages);
	if (!pages->GetSize())
	{
		message->m_propStreamResourceID = ResourceID::Null;
		Verify(stack_level == Tool::Instance->GetStackLevel());
		delete pages;
		return;
	}

	//
	//-------------------------------------------------------------
	// Look for the prop stream resource to see if it is up to date
	//-------------------------------------------------------------
	//
	STOP(("Not updated"));
	const char* props_filename = props_file.GetFileName();
	Resource prop_resource(props_filename);
	if (
		prop_resource.DoesResourceExist() &&
		prop_resource.IsResourceUpToDate()
	)
	{
		WORD span;
		prop_resource >> span;
		Verify(span > 0);
		*script->baseID += span;
		message->replicatorID += span;
		message->m_propStreamResourceID = prop_resource.GetResourceID();
	}

	//
	//---------------------------------------
	// For each page, an entity will be built
	//---------------------------------------
	//
	else
	{
		Check_Object(Tool::Instance);
		Tool::Instance->PushFilePath(&props_file);
		DynamicMemoryStream prop_stream(sizeof(WORD));
		prop_stream << static_cast<WORD>(0);
		Page *instance;
		ReplicatorID starting_id = *script->baseID;
		while ((instance = pages->ReadAndNext()) != NULL)
		{
			Check_Object(instance);

			//
			//--------------------------------
			// Create the entity for this page
			//--------------------------------
			//
			const char *instance_name = instance->GetName();
			Check_Pointer(instance_name);
			Entity::CreateMessage *message =
				Tool::Instance->ConstructCreateMessage(
					&prop_stream,
					instance,
					script->baseID
				);
			Check_Object(message);
			Check_Object(NameTable::GetInstance());
			message->nameID = NameTable::GetInstance()->FindID(instance_name);
			prop_stream.AdvancePointer(message->messageLength);
		}

		//
		//-----------------------------------------
		// Only save out the stream if there is one
		//-----------------------------------------
		//
		if (prop_stream.GetBytesUsed() > sizeof(WORD))
		{
			prop_stream.Rewind();
			Verify(*script->baseID != starting_id);
			WORD span =
				static_cast<WORD>(script->baseID->localID - starting_id.localID);
			prop_stream << span;
			message->replicatorID += span;
			prop_resource.Save(&prop_stream, props_file.GetFileDependencies());
			Check_Object(&prop_resource);
			message->m_propStreamResourceID = prop_resource.GetResourceID();
		}
		else
			message->m_propStreamResourceID = ResourceID::Null;
		Tool::Instance->PopFilePath();
	}

	//
	//------------------------
	// Clean up the tool stack
	//------------------------
	//
	Check_Object(pages);
	delete pages;
	Verify(stack_level == Tool::Instance->GetStackLevel());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Map__GameModel::ConstructGameModel(Script *script)
{
	Check_Object(script);
	Check_Object(Tool::Instance);
	#if defined(_ARMOR)
		int stack_index = Tool::Instance->GetStackLevel();
	#endif
	Check_Object(Resource::ParentFileDependencies);

	//
	//--------------
	// Set up stream
	//--------------
	//
	MemoryStream *model_stream = script->modelStream;
	Check_Object(model_stream);
	model_stream->AllocateBytes(sizeof(Map__GameModel));
	Entity__GameModel::ConstructGameModel(script);
	Map__GameModel *model =
		Cast_Pointer(Map__GameModel*, model_stream->GetPointer());
	Check_Object(model);
	NotationFile *model_file = script->modelFile;
	Check_Object(model_file);
	Tool::Instance->PushFilePath(model_file);

	//
	//-------------------------------------
	// We need to get our grid element file
	//-------------------------------------
	//
	Page *page = model_file->FindPage("GameData");
	Check_Object(page);
	model->m_waterLevel = 0.0f;
	page->GetEntry("WaterLevel", &model->m_waterLevel);
	const char* grid_filename;
	page->GetEntry("Grid", &grid_filename, true);
	FileStream grid_file(grid_filename);
	Tool::Instance->PushFilePath(&grid_file);
	grid_filename = grid_file.GetFileName();

	const char* hudmap_filename;
	model->m_HudMap[0] = 0;
	if (page->GetEntry ("hudmap",&hudmap_filename,false))
	{
		Str_Copy (model->m_HudMap,hudmap_filename,128);
	}
	//
	//---------------------------------------------------------------
	// See if the resource already exists, and if so, just use its ID
	//---------------------------------------------------------------
	//
	Resource grid_resource(grid_filename);
	MString zones_name = grid_filename;
	zones_name += "{zones}";
	Resource zones_resource(zones_name);
	if (!grid_resource.DoesResourceExist() || !grid_resource.IsResourceUpToDate())
	{
		FileDependencies grid_dependencies;
		grid_dependencies.AddDependency(&grid_file);

		//
		//--------------------------------------------------------------------
		// Now, create the actual grid and see how big it is.  We will need to
		// resourcify that number of erf's
		//--------------------------------------------------------------------
		//
		ElementRenderer::GridElement *grid =
			Cast_Object(
				ElementRenderer::GridElement*,
				ElementRenderer::Element::Create(
					&grid_file,
					ElementRenderer::ReadERFVersion(&grid_file)
				)
			);
		grid_file.Rewind();
		BYTE x,z;
		grid->GetSize(&z, &x);
		WORD zone_count = static_cast<WORD>(x*z);
		if (
			x>26 || z>26 || !zone_count
				|| !grid->IsDerivedFrom(ElementRenderer::GridElement::DefaultData)
		)
		{
			STOP((
				"%s{[GameData]Grid=%s}: Bad grid element!",
				model_file->GetFileName(),
				grid_filename
			));
		}

		DynamicArrayOf<Zone::ResourceData> zones(zone_count);
		Check_Object(grid);
		delete grid;

		//
		//---------------------------------------------------------------
		// For each zone in the grid, look for the corresponding erf file
		//---------------------------------------------------------------
		//
		unsigned zone=0;
		for (unsigned r=0; r<z; ++r)
		{
			for (unsigned c=0; c<x; ++c)
			{
				//
				//-------------------------
				// We need to tile geometry
				//-------------------------
				//
				char buffer[17];
				#if defined(_ARMOR)
					int count =
				#endif
				sprintf(buffer, "%c%c.erf", r+'A', c+'A');
				Verify(count < sizeof(buffer));
				FileStream erf_file(buffer);
				const char* zone_filename = erf_file.GetFileName();

				//
				//---------------------------------------------------------------
				// See if the resource already exists, and if so, just use its ID
				//---------------------------------------------------------------
				//
				Resource zone_resource(zone_filename);
				if (!zone_resource.DoesResourceExist() || !zone_resource.IsResourceUpToDate())
				{
					FileDependencies zone_dependencies;
					zone_dependencies.AddDependency(&erf_file);
					zone_resource.Save(&erf_file, &zone_dependencies);
				}
				zone_resource.AddFileDependenciesTo(&grid_dependencies);
				zones[zone].m_geometryResourceID = zone_resource.GetResourceID();

				//
				//----------------------------
				// We need to get our tile bsp
				//----------------------------
				//
				#if defined(_ARMOR)
					count =
				#endif
				sprintf(buffer, "%c%c.bsp", r+'A', c+'A');
				Verify(count < sizeof(buffer));
				FileStream bsp_file(buffer);
				zone_filename = bsp_file.GetFileName();

				//
				//---------------------------------------------------------------
				// See if the resource already exists, and if so, just use its ID
				//---------------------------------------------------------------
				//
				Resource bsp_resource(zone_filename);
				if (!bsp_resource.DoesResourceExist() || !bsp_resource.IsResourceUpToDate())
				{
					FileDependencies zone_dependencies;
					zone_dependencies.AddDependency(&bsp_file);
					bsp_resource.Save(&bsp_file, &zone_dependencies);
				}
				bsp_resource.AddFileDependenciesTo(&grid_dependencies);
				zones[zone].m_bspResourceID = bsp_resource.GetResourceID();

				//
				//---------------------------------
				// We need to get our tile material
				//---------------------------------
				//
				#if defined(_ARMOR)
					count =
				#endif
				sprintf(buffer, "%c%c.material", r+'A', c+'A');
				Verify(count < sizeof(buffer));
				if (!gos_DoesFileExist(buffer))
					zones[zone].m_materialResourceID = ResourceID::Null;
				else
				{
					FileStream material_file(buffer);
					zone_filename = material_file.GetFileName();

					//
					//---------------------------------------------------------------
					// See if the resource already exists, and if so, just use its ID
					//---------------------------------------------------------------
					//
					Resource material_resource(zone_filename);
					if (!material_resource.DoesResourceExist() || !material_resource.IsResourceUpToDate())
					{
						FileDependencies zone_dependencies;
						zone_dependencies.AddDependency(&material_file);
						material_resource.Save(&material_file, &zone_dependencies);
					}
					material_resource.AddFileDependenciesTo(&grid_dependencies);
					zones[zone].m_materialResourceID = material_resource.GetResourceID();
				}
				++zone;
			}
		}

		//
		//-------------------------------------------
		// Now save out the resource IDs of the model
		//-------------------------------------------
		//
		MemoryStream res_id_stream(zones.GetData(), zones.GetSize());
		zones_resource.Save(&res_id_stream, &grid_dependencies);
		grid_resource.Save(&grid_file, &grid_dependencies);
		if (!zones_resource.IsRegistered())
			Resource::ParentFileDependencies->AddDependencies(&grid_dependencies);
	}
	else if (!grid_resource.IsRegistered())
		grid_resource.AddFileDependenciesTo(Resource::ParentFileDependencies);
	model->m_gridElementResourceID = grid_resource.GetResourceID();
	model->m_zoneArrayResourceID = zones_resource.GetResourceID();
	Tool::Instance->PopFilePath();

	//
	//-----------------------------
	// Set up the texture spec file
	//-----------------------------
	//
	const char* texture_filename;
	if (page->GetEntry("TextureData", &texture_filename))
	{
		FileStream file_stream(texture_filename);
		const char* file_name = file_stream.GetFileName();
		Resource texture_res(file_name);
		if (!texture_res.DoesResourceExist() || !texture_res.IsResourceUpToDate())
		{
			FileDependencies tex_dependencies;
			tex_dependencies.AddDependency(&file_stream);
			texture_res.Save(&file_stream, &tex_dependencies);
			if (!texture_res.IsRegistered())
				Resource::ParentFileDependencies->AddDependencies(&tex_dependencies);
		}
		else if (!texture_res.IsRegistered())
			texture_res.AddFileDependenciesTo(Resource::ParentFileDependencies);
		model->m_textureDataResourceID = texture_res.GetResourceID();

		//
		//-----------------------------
		// Load up the TCF file
		//-----------------------------
		//
		const char* tcf_filename;
		model->m_tcfResourceID = ResourceID::Null;
		page->GetEntry("TCFFilename", &tcf_filename, true);

		MString long_filename("Content\\Textures\\CompostTexture\\");
		long_filename += tcf_filename;

		FileStream tcf_file_stream(long_filename);
		const char* tcf_file_name = tcf_file_stream.GetFileName();
		Resource tcf_res(tcf_file_name);
		if (!tcf_res.DoesResourceExist() || !tcf_res.IsResourceUpToDate())
		{
			FileDependencies tcf_dependencies;
			tcf_dependencies.AddDependency(&tcf_file_stream);
			tcf_res.Save(&tcf_file_stream, &tcf_dependencies);
			if (!tcf_res.IsRegistered())
				Resource::ParentFileDependencies->AddDependencies(&tcf_dependencies);
		}
		else if (!tcf_res.IsRegistered())
			tcf_res.AddFileDependenciesTo(Resource::ParentFileDependencies);
		model->m_tcfResourceID = tcf_res.GetResourceID();
	}
	else
	{
		model->m_textureDataResourceID = ResourceID::Null;
		model->m_tcfResourceID = ResourceID::Null;
	}

	//
	//-----------------------------
	// Set up the cultural data
	//-----------------------------
	//
	const char* cultualdata_filename;
	model->m_culturalDataResourceID = ResourceID::Null;
	if (page->GetEntry("CulturalData", &cultualdata_filename))
	{
		FileStream file_stream(cultualdata_filename);
		const char* file_name = file_stream.GetFileName();
		Resource culturaldata_res(file_name);
		if (!culturaldata_res.DoesResourceExist() || !culturaldata_res.IsResourceUpToDate())
		{
			FileDependencies culturaldata_dependencies;
			culturaldata_dependencies.AddDependency(&file_stream);
			culturaldata_res.Save(&file_stream, &culturaldata_dependencies);
			if (!culturaldata_res.IsRegistered())
				Resource::ParentFileDependencies->AddDependencies(&culturaldata_dependencies);
		}
		else if (!culturaldata_res.IsRegistered())
			culturaldata_res.AddFileDependenciesTo(Resource::ParentFileDependencies);
		model->m_culturalDataResourceID = culturaldata_res.GetResourceID();
	}
	else
		model->m_culturalDataResourceID = ResourceID::Null;


	Tool::Instance->PopFilePath();
	Verify(stack_index == Tool::Instance->GetStackLevel());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Map__GameModel::ReadAndVerify(
		Map__GameModel *model,
		ModelAttributeEntry *attribute_entry,
		const char *data,
		char **error,
		int error_buffer
	)
{
	Check_Object(attribute_entry);

	bool result = false;
	bool valid_data = (*data != '\0');

	//
	//----------------------------------------
	//Read in the values from the data entered
	//----------------------------------------
	//
	result =
		Entity__GameModel::ReadAndVerify(
			model,
			attribute_entry,
			data,
			error,
			error_buffer
		);
	//
	//---------------------------
	//Verify all the model values
	//---------------------------
	//
	switch(attribute_entry->attributeID)
	{
	case DamageCraterResourceAttributeID:
		{
			if(!valid_data)
			{
				ResourceID value = ResourceID::Null;
				attribute_entry->SetValue(model, (void *)&value);
				result = true;
			}
			break;
		}
	}
	return result;
}
