#include "munga.h"
#pragma hdrstop

#include "tool.h"
#include "fileutil.h"
#include "eyecandy.h"
#include "dropzone.h"
#include "terrain.h"
#include "cultural.h"
#include "doorfram.h"
#include "camship.h"
#include "boxsolid.h"
#include "team.h"
#include "namelist.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	ApplicationTool::CreateMakeMessage(
		const char* class_name,
		Entity::MakeMessage *creation_message,
		NotationFile *model_file,
		const ResourceDirectories *directories
	)
{
	if (!stricmp(class_name,"TerrainClassID"))
	{
		Terrain::CreateMakeMessage(
			(Terrain::MakeMessage*)creation_message,
			model_file,
			directories
		);
		return True;
	}
	else if (!stricmp(class_name,"UnscalableTerrainClassID"))
	{
		UnscalableTerrain::CreateMakeMessage(
			(UnscalableTerrain::MakeMessage*)creation_message,
			model_file,
			directories
		);
		return True;
	}
	else if (!stricmp(class_name,"CulturalIconClassID"))
	{
		CulturalIcon::CreateMakeMessage(
			(CulturalIcon::MakeMessage*)creation_message,
			model_file,
			directories
		);
		return True;
	}
	else if (!stricmp(class_name,"LandmarkClassID"))
	{
		Landmark::CreateMakeMessage(
			(Landmark::MakeMessage*)creation_message,
			model_file,
			directories
		);
		return True;
	}
	else if (!stricmp(class_name,"DoorFrameClassID"))
	{
		DoorFrame::CreateMakeMessage(
			(DoorFrame::MakeMessage*)creation_message,
			model_file,
			directories
		);
		return True;
	}
	else if (!stricmp(class_name,"EyeCandyClassID"))
	{
		EyeCandy::CreateMakeMessage(
			(EyeCandy::MakeMessage*)creation_message,
			model_file,
			directories
		);
		return True;
	}
	else if (!stricmp(class_name, "TeamClassID"))
	{
		Team::CreateMakeMessage(
			(Team::MakeMessage*)creation_message,
			model_file,
			directories
		);
		return True;
	}

	//
	//-----------------------------------------------
	// Not a class MUNGA knows about, so return False
	//-----------------------------------------------
	//
	return False;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	ApplicationTool::WriteMaps(
		char *map_name,
		NotationFile *notation_file,
		NotationFile *ops_file,
		StreamableResourceFile *file,
		const ResourceDirectories *resource_directories,
		int resource_id
	)
{
	const char
		*entry_data, *type, *ops_name;
	char *p;
	char
		short_map_name[15],
		ops_file_page[100],
		ops_file_data[100],
		temp[100];


	Str_Copy(short_map_name, map_name, 15);
	p = short_map_name;
	p += strlen(short_map_name) - 4;
	*p = '\0';




	//----------------------
	// Write ops_file stuff
	//----------------------

	ops_file->GetEntry("resource", "res name", &ops_name);

	strcpy(ops_file_data, ops_name);
	strcat(ops_file_data, "::Standard::Location::");
	strcat(ops_file_data, short_map_name );
	strcpy(ops_file_page, ops_name);
	strcat(ops_file_page, "::Standard::LocationList");

	ops_file->AppendEntry(ops_file_page, "location", ops_file_data);
	ops_file->SetEntry(ops_file_data, "tag", short_map_name );
	ops_file->AppendEntry(ops_file_data, NULL, (char *)NULL);


	//
	//-----------------------------------------------
	// Get exsitance box stream from notation file
	// named after map then save it to the resource
	// Not required yet!
	//-----------------------------------------------
	//




	strcpy(temp, short_map_name);
	strcat(temp, ".xst");

	char *filename = MakePathedFilename(
						resource_directories->collisionDirectory,
						temp);
	Register_Pointer(filename);

	NotationFile *exist_notation_file = new NotationFile(filename);
	Register_Object(exist_notation_file);

	Unregister_Pointer(filename);
	delete filename;

	labOnly |= exist_notation_file->IsMarkedLabOnly();

	if (exist_notation_file->PageCount())
	{



		int size;
		ExtentBox *existance_box = MakeExistanceBoxStream(exist_notation_file, &size);
		Register_Pointer(existance_box);

		file->AddResource(
			short_map_name,
			ResourceDescription::ExistanceBoxStreamResourceType,
			1,
			ResourceDescription::Preload,
			existance_box,
			size * sizeof(ExtentBox)
		);

		Unregister_Pointer(existance_box);
		delete[] existance_box;
	}

	Unregister_Object(exist_notation_file);
	delete exist_notation_file;



	//---------------------
	// Make camera stream
	//---------------------

	CameraInstanceManager::CreateCameraInstancesStream(
		file,
		map_name,
		resource_directories
	);

	//
	//--------------------------------------------------------
	// Get the name and types of all the pages in the map file
	//--------------------------------------------------------
	//

	NameList *namelist = notation_file->MakePageList();
	Register_Object(namelist);
	NameList::Entry *entry = namelist->GetFirstEntry();


	Logical model_failed = False;

	char model_name[100];
	while (entry)
	{
		entry_data = entry->GetName();


		if (!stricmp(entry_data, "LAB_ONLY"))
		{
			  labOnly = True;
			  entry = entry->GetNextEntry();
			  continue;
		}

		else if(!notation_file->GetEntry(entry_data, "type", &type))
		{
			std::cout << "No Type in " << entry_data << std::endl;

			Unregister_Object(namelist);
			delete namelist;

			return False;


		}

		//
		//----------------------------
		// Read in a model description
		//----------------------------
		//
		char scenario_name[100];

		if (!stricmp(type, "model"))
		{
			strcpy(model_name, entry_data);

			if(
				!notation_file->GetEntry(
					model_name,
					"modelfile",
					&entry_data
				)
			)
			{
				std::cout << "No modelfile in " << model_name << std::endl;
				Unregister_Object(namelist);
				delete namelist;

				return False;
			}

			ResourceDescription *res = file->FindResourceDescription(
				model_name,
				ResourceDescription::ModelListResourceType
			);
			if (!res)
			{
				char *filename =
					MakePathedFilename(
						resource_directories->modelDirectory,
						entry_data
				);
				Register_Pointer(filename);

				NotationFile *new_notation_file = new NotationFile(filename);
				Register_Object(new_notation_file);

				labOnly |= new_notation_file->IsMarkedLabOnly();

				if (new_notation_file->PageCount())
				{
					if (
						!WriteModels(
							new_notation_file,
							ops_file,
							file,
							model_name,
							resource_directories
						)
					)
					{
						std::cout << "Model Failed : " << filename << std::endl;
						model_failed = True;
					}
				}
				else
				{
					 std::cout << "Model File : " << filename << " does not exist!\n";
					 model_failed = True;
				}

				Unregister_Pointer(filename);
				delete filename;
				Unregister_Object(new_notation_file);
				delete new_notation_file;
			}
		}

		else if (!stricmp(type, "dropzone"))
		{
#if 0
			strcpy(model_name, entry_data);
			ResourceDescription *res =
				file->FindResourceDescription(
					model_name,
					ResourceDescription::ModelListResourceType
				);
			if (!res)
			{
				//
				//      make drop zone name be  blah.name
				//
				char dropzone_name[32];
				Str_Copy(dropzone_name, short_map_name, sizeof(dropzone_name));
				Str_Cat(dropzone_name, ".", sizeof(dropzone_name));
				Str_Cat(dropzone_name, model_name,sizeof(dropzone_name));



				strcpy(ops_file_data, ops_name);
				strcat(ops_file_data, "::");
				strcat(ops_file_data, short_map_name);
				strcat(ops_file_data, "::DropZone::" );
				strcat(ops_file_data, dropzone_name);

				strcpy(ops_file_page, ops_name);
				strcat(ops_file_page, "::");
				strcat(ops_file_page,  short_map_name );
				strcat(ops_file_page, "::DropZoneList");

				ops_file->AppendEntry(ops_file_page, "DropZone", ops_file_data);
				ops_file->SetEntry(ops_file_data, "tag", dropzone_name );
				ops_file->SetEntry(ops_file_data, "classID", "DpZn");
				ops_file->AppendEntry(ops_file_data, NULL, (char *)NULL);


				ResourceDescription::ResourceID res_number =
					DropZone::CreateModelResource(
						file,
						model_name,
						notation_file,
						dropzone_name,
						resource_directories
					);

				if (res_number != ResourceDescription::NullResourceID)
				{
					file->AddResourceList(
						dropzone_name,
						ResourceDescription::ModelListResourceType,
						1,
						ResourceDescription::Preload,
						&res_number,
						1
					);
				}
				else
				{
					model_failed = True;
				}
			}
#endif
		}

		else if (!stricmp(type, "include"))
		{
			strcpy(scenario_name, entry_data);

			NameList *namelist2 =
				notation_file->MakeEntryList(entry_data, "file");
			Register_Object(namelist2);
			NameList::Entry *entry2 = namelist2->GetFirstEntry();

			while(entry2 != NULL)
			{

				const char *entry2_data = entry2->GetChar();

				char *filename =
					MakePathedFilename(
						resource_directories->mapDirectory,
						entry2_data
				);
				Register_Pointer(filename);

				NotationFile *new_notation_file = new NotationFile(filename);
				Register_Object(new_notation_file);

				labOnly |= new_notation_file->IsMarkedLabOnly();


				//
				//  Write out drop zones....
				//  Write out maps...
				//
				char short_name[100];
				strcpy( short_name, StripDirectory(entry2_data) );

				if (new_notation_file->PageCount())
				{
					if(
						!WriteMaps(
							short_name,
							new_notation_file,
							ops_file,
							file,
							resource_directories
						)
					)
					{
						std::cout << "Include Failed : " << filename << std::endl;
						model_failed = True;
					}
				}
				else
				{
					std::cout << "Include File : " << filename << " does not exist\n";
					model_failed = True;
				}


				Unregister_Pointer(filename);
				delete filename;
				Unregister_Object(new_notation_file);
				delete new_notation_file;
				entry2 = entry2->GetNextEntry();
			}

			Unregister_Object(namelist2);
			delete namelist2;

		}

		else if (!stricmp(type, "map"))
		{
			if (model_failed == False)
			{
				//
				// Check to see that map is not already in resource file
				//

				ResourceDescription *test = file->FindResourceDescription(
					 short_map_name, ResourceDescription::MakeMessageStreamResourceType);

				BoxedSolidList *box_solid_list = new BoxedSolidList;
				Register_Object(box_solid_list);

				if ( test == NULL)
				{
					NameList *namelist2 =
						notation_file->MakeEntryList(entry_data, "instance");
					Register_Object(namelist2);
					NameList::Entry *entry2 = namelist2->GetFirstEntry();

					//
					//----------------
					// Count instances
					//----------------
					//
					int instance_count;
					for (instance_count=0; entry2; ++instance_count)
					{
						entry2 = entry2->GetNextEntry();
					}
					entry2 = namelist2->GetFirstEntry();

					//
					//-------------------------------------------------------
					// make a buffer to hold all these messages, and have the
					// beginning store the number of messages
					//-------------------------------------------------------
					//
					int streamSize = instance_count * 4 * sizeof(Entity::MakeMessage);
					Entity::MakeMessage *stream = (Entity::MakeMessage*)new char[streamSize];
					memset(stream, 0, streamSize);

					MemoryStream m(stream, streamSize);
					*(int*)m.GetPointer() = instance_count;
					m.AdvancePointer(sizeof(int));

					EulerAngles angular_position;
					Degree value;

					while (entry2)
					{
						char find_me[100];
						strcpy(find_me, entry2->GetChar());
						char *p = find_me;
						char *token = strtok(p,White_Space);
						ResourceDescription *res = NULL;

						if (!stricmp( entry2->GetName(), "instancedropzone"))
						{

							char dropzone_name[32];
						#if 0
							Str_Copy(dropzone_name, short_map_name, sizeof(dropzone_name));
							Str_Cat(dropzone_name, ".", sizeof(dropzone_name));
							Str_Cat(dropzone_name,token,sizeof(dropzone_name));
						#else
							Str_Copy(dropzone_name,token,sizeof(dropzone_name));
						#endif


							DropZone::CreateMakeMessage(
								(DropZone::MakeMessage*)m.GetPointer(),
								token,
								dropzone_name,
								notation_file
							);
						}
						else
						{
							notation_file->GetEntry(token,"type",&type);

							if(!stricmp(type, "include"))
							{
								res =
								file->FindResourceDescription(
									token,
									ResourceDescription::MakeMessageStreamResourceType
								);

							}
							else
							{
								res =
									file->FindResourceDescription(
										token,
										ResourceDescription::ModelListResourceType
									);
							}

							if (!res)
							{
								std::cout << "Instance Not Found : " << token << std::endl;

								Unregister_Object(namelist2);
								Unregister_Pointer(stream);
								Unregister_Object(namelist);
								delete namelist2;
								delete[] stream;
								delete namelist;

								return False;
							}

						}

						//
						//--------------
						// Next instance
						//--------------
						//

						//
						// If it's not a drop zone
						//
						if (!stricmp( entry2->GetName(), "instancedropzone"))
						{
							// do nothing  this is already done.
						}
						else
						{

							notation_file->GetEntry(token,"type",&type);


							if(!stricmp(type, "include"))
							{

								char name[100];
								Str_Copy(name, token, 100);


								ResourceDescription *map_res = file->FindResourceDescription(
									name, ResourceDescription::MakeMessageStreamResourceType);

								if (!map_res)
								{
									std::cout << "CreateMakeMapMessage Failed To Find Map : " << name << std::endl;
									Unregister_Object(namelist2);
									Unregister_Pointer(stream);
									Unregister_Object(namelist);
									delete namelist2;
									delete[] stream;
									delete namelist;

									return False;

								}

								if (
									!Entity::CreateMakeMapMessage(
										(Entity::MakeMapMessage*)m.GetPointer(),
										map_res->resourceID,
										notation_file,
										resource_directories
									)
								)
								{
									std::cout << "CreateMakeMapMessage Failed : " << type << std::endl;
									Unregister_Object(namelist2);
									Unregister_Pointer(stream);
									Unregister_Object(namelist);
									delete namelist2;
									delete[] stream;
									delete namelist;

									return False;

								}
							}
							else
							{
								if (!notation_file->GetEntry(token,"modelfile",&type))
								{
									std::cout << token << " has no modelfile specification\n";
									Unregister_Object(namelist2);
									Unregister_Pointer(stream);
									Unregister_Object(namelist);
									delete namelist2;
									delete[] stream;
									delete namelist;

									return False;
								}

								char *filename3 =
									MakePathedFilename(
										resource_directories->modelDirectory,
										type
									);
								Register_Pointer(filename3);
								NotationFile *new_notation_file =
									new NotationFile(filename3);
								Register_Object(new_notation_file);

								labOnly |= new_notation_file->IsMarkedLabOnly();

								Unregister_Pointer(filename3);
								delete filename3;

								if (!new_notation_file->PageCount())
								{
									std::cout << filename3 << " does not exist!";
									Unregister_Object(namelist2);
									Unregister_Pointer(stream);
									Unregister_Object(namelist);
									delete namelist2;
									delete[] stream;
									delete namelist;

									return False;

								}

								if(!new_notation_file->GetEntry("gamedata","class",&type))
								{
									std::cout << "gamedata not found\n";
									Unregister_Object(namelist2);
									Unregister_Pointer(stream);
									Unregister_Object(namelist);
									delete namelist2;
									delete[] stream;
									delete namelist;

									return False;

								}

								if (
									!CreateMakeMessage(
										type,
										(Entity::MakeMessage*)m.GetPointer(),
										new_notation_file,
										resource_directories
									)
								)
								{
									std::cout << "Invalid gamedata type : " << type << std::endl;
									Unregister_Object(namelist2);
									Unregister_Pointer(stream);
									Unregister_Object(namelist);
									delete namelist2;
									delete[] stream;
									delete namelist;

									return False;

								}

								Unregister_Object(new_notation_file);
								delete new_notation_file;
							}
						}


						Entity::MakeMessage *message =
							(Entity::MakeMessage*)m.GetPointer();
						if (
							!(message->messageFlags
							 & Entity::MakeMessage::MapStreamMarkerFlag)
						)
						{
							message->resourceID =
							 (res) ?
								res->resourceID
								 : ResourceDescription::NullResourceID;
							message->ownerID = MapHostID;
							message->interestZoneID = 0;
						}
						m.AdvancePointer(message->messageLength);
						entry2 = entry2->GetNextEntry();
					}


					if (resource_id == -1)
					{
						file->AddResource(
							short_map_name,
							ResourceDescription::MakeMessageStreamResourceType,
							1,
							ResourceDescription::Preload,
							stream,
							(char*)m.GetPointer() - (char*)stream
						);
					}
					else
					{

						//
						// Check for resource_id , if there Fail.
						//

						ResourceDescription *res_des =
							file->FindResourceDescription(resource_id);

						if (!res_des)
						{

							file->AddResource(
								short_map_name,
								ResourceDescription::MakeMessageStreamResourceType,
								1,
								ResourceDescription::Preload,
								stream,
								(char*)m.GetPointer() - (char*)stream,
								resource_id
							);
						}
						else
						{
							DEBUG_STREAM << short_map_name << " : " << resource_id << std::endl << std::flush;
							Fail("Duplicate Resource Number");
						}

					}


					Unregister_Pointer(stream);
					delete[] stream;
					Unregister_Object(namelist2);
					delete namelist2;
				}
				Unregister_Object(box_solid_list);
				delete box_solid_list;
			}
			else
			{
				Unregister_Object(namelist);
				delete namelist;
				return False;
			}

		}
		else
		{
			std::cout << "Error: unknown type " << type << "!\n";


			Unregister_Object(namelist);
			delete namelist;
			return False;
		}

		entry = entry->GetNextEntry();
	}

	strcpy(ops_file_page, ops_name);
	strcat(ops_file_page, "::");
	strcat(ops_file_page,  short_map_name );
	strcat(ops_file_page, "::DropZoneList");
	ops_file->AppendEntry(ops_file_page, NULL, (char *)NULL);

	Unregister_Object(namelist);
	delete namelist;
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  This does NOT write the maps to the ops file, Write maps does that.
// This just finishes the ops file up
//
void
	ApplicationTool::WriteOpsFile(
		NotationFile *ops_file,
		StreamableResourceFile *file,
		const ResourceDirectories *resource_directories
	)
{
	//
	//--------------------------------------
	// open vehicle table and write out info
	//--------------------------------------
	//
	const char *ops_name;
	ops_file->GetEntry("resource", "res name", &ops_name);

	char *filename3 =
		MakePathedFilename(resource_directories->modelDirectory, "vehicle.tbl");
	Register_Pointer(filename3);

	NotationFile *new_notation_file = new NotationFile(filename3);
	Register_Object(new_notation_file);

	//
	// Kill if file isn't there.
	//

	if (!new_notation_file->PageCount())
	{
		std::cout << "vehicle.tbl does not exist in the resource directory!\n" << std::flush;
		Unregister_Pointer(filename3);
		delete filename3;
		PostQuitMessage(AbortExitCodeID);
	}
	Unregister_Pointer(filename3);
	delete filename3;

	NameList *list = new_notation_file->MakeEntryList("color");
	Register_Object(list);
	NameList::Entry *entry = list->GetFirstEntry();

	char ops_file_data[100];
	char ops_file_page[100];
	while(entry)
	{
		if(entry->GetChar() != NULL)
		{
			strcpy(ops_file_data, ops_name);
			strcat(ops_file_data, "::Standard::Color::");
			strcat(ops_file_data, entry->GetName() );
			strcpy(ops_file_page, ops_name);
			strcat(ops_file_page, "::Standard::ColorList");

			ops_file->AppendEntry(ops_file_page, "color", ops_file_data);
			ops_file->SetEntry(ops_file_data, "tag", entry->GetName() );
			ops_file->AppendEntry(ops_file_data, NULL, (char *)NULL);
		}

		entry = entry->GetNextEntry();
	}

	ops_file->AppendEntry(ops_file_page, NULL, (char *)NULL);
	Unregister_Pointer(list);
	delete list;

	list = new_notation_file->MakeEntryList("badge");
	Register_Object(list);
	entry = list->GetFirstEntry();

	while(entry)
	{
		if(entry->GetChar() != NULL)
		{
			strcpy(ops_file_data, ops_name);
			strcat(ops_file_data, "::Standard::Badge::");
			strcat(ops_file_data, entry->GetName() );
			strcpy(ops_file_page, ops_name);
			strcat(ops_file_page, "::Standard::BadgeList");

			ops_file->AppendEntry(ops_file_page, "badge", ops_file_data);
			ops_file->SetEntry(ops_file_data, "tag", entry->GetName() );
			ops_file->AppendEntry(ops_file_data, NULL, (char *)NULL);
		}

		entry = entry->GetNextEntry();
	}

	ops_file->AppendEntry(ops_file_page, NULL, (char *)NULL);
	Unregister_Pointer(list);
	delete list;


	//
	//  Put spaces at the end of vehicles and maps
	//  that were defined elsewere.
	//

	char temp[30];



	strcpy(temp, ops_name);
	strcat(temp, "::Standard::LocationList");
	ops_file->AppendEntry(temp, NULL, (char *)NULL);
	strcpy(temp, ops_name);
	strcat(temp, "::Standard::VehicleList");
	ops_file->AppendEntry(temp, NULL, (char *)NULL);


	//
	//  Add notation file to resource.
	//

	long size;
	size = new_notation_file->SizeOfText();
	Verify( size >= 0 );
	#define EDO_DEBUG False
	#if EDO_DEBUG
		Dump( size );
	#endif
	char *notation_string = new char[size];
	Register_Pointer(notation_string);
	#if DEBUG_LEVEL>0
		long actual_size;
		actual_size =
	#endif
	new_notation_file->WriteText(notation_string, size);

	#if EDO_DEBUG
		{
			ofstream edo_debug;
			edo_debug.open("DEBUGRES.EDO",std::ios::out);
			if (!edo_debug) { Fail("can't open DEBUGRES.EDO"); }
			const char
				*whip = notation_string,
				*stop = whip + size;
			std::cout << "writing DEBUGRES.EDO" << std::endl;
			while (whip < stop)
			{
				edo_debug << "|" << whip << "|" << std::endl;
				whip = strchr(whip, '\0') + 1;
			}
			edo_debug << "<EOF>" << std::endl;
			edo_debug.close();
		}
	#endif

	Verify( actual_size == size );
	file->AddResource(
		"vehicletable",
		ResourceDescription::VehicleTableResourceType,
		1,
		ResourceDescription::Preload,
		notation_string,
		sizeof(char) * size
	);

	Unregister_Pointer(notation_string);
	Unregister_Object(new_notation_file);
	delete[] notation_string;
	delete new_notation_file;
}
