#include "munga.h"
#pragma hdrstop

#include "tool.h"
#include "fileutil.h"
#include "audtools.h"
#include "registry.h"
#include "boxsolid.h"
#include "namelist.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	GetId(char *string)
{
	int id;

	char *number = strstr( string, "/id:");
	if (number != NULL)
	{
		number += 4;
		id = atoi(number);
		return id;
	}
	else
	{
		return -1;
	}
}

Logical
	PlatformTool::ConvertStringToModeMask(
		const char */*string*/,
		ModeMask */*returned_value_ptr*/
	)
{
	Fail("PlatformTool::ConvertStringToModeMask method not overridden!");
   return False;
}

Logical
	PlatformTool::ConvertStringToTechStatus(
		const char */*string*/,
		int */*returned_value_ptr*/
	)
{
	Fail("PlatformTool::ConvertStringToTechStatus method not overridden!");
   return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	ApplicationTool::BuildResources(
		int argc,
		char **argv,
		AudioCreateSymbols &create_symbols,
		Byte minor_version
	)
{
	char temp_name[200];
	const char *Res_Name;
	char *resfilename;
	char ops_filename[150];
	char dirgauge[150];
	char dircollision[150];
	char dirmodel[150];
	char dirmap[150];
	char diraudio[150];
	char dirvideo[150];
	char diranim[150];

	char short_name[100];
	char long_name[200];
	char id_name[100];

	const char *tempdir;

	ResourceDirectories
		resource_directories;

	NotationFile *ops_file = new NotationFile();
	Register_Object(ops_file);

	StreamableResourceFile file;

	if (argc < 2)
	{
		DEBUG_STREAM << "Usage: buildXX resource.bld [res2.bld ...]\n" << std::flush;
		return 1;
	}

	verboseMode = False;
	labOnly = False;

	//
	// Write audio symbols, add static audio resources
	//
	create_symbols.Execute();
	platformTool->CreateStaticAudioStreamResource(&file);

	//
	//------------------------------------
	//  Arguments are only build files,
	//  so step thru them
	//------------------------------------
	//
	for(int arg_number = 1; arg_number < argc; ++arg_number)
	{

		strcpy(temp_name,argv[arg_number]);
		NotationFile *notation_file;

		WorkingDirectory = MakeWorkingDirectory(temp_name);
		Register_Pointer(WorkingDirectory);

		char *filename = MakePathedFilename(WorkingDirectory, temp_name);
		Register_Pointer(filename);

		notation_file = new NotationFile(filename);
		Register_Object(notation_file);

		labOnly |= notation_file->IsMarkedLabOnly();

		if (!notation_file->PageCount())
		{
			DEBUG_STREAM << filename << " does not exist! " << std::endl << std::flush;
			Unregister_Object(notation_file);
			delete notation_file;
			Stop_Registering();
			return 0;
		}
		else if (!notation_file->PageExists("resource"))
		{
			DEBUG_STREAM << filename << " Not a valid resource list" << std::endl << std::flush;
			Unregister_Object(notation_file);
			delete notation_file;
			Stop_Registering();
			return 0;
		}

		Unregister_Pointer(filename);
		delete filename;

		//
		//-----------------------------------------------
		//  If this is the first file check
		//  for required info, if not skip this
		//  section so required info isn't overwritten
		//-----------------------------------------------
		//

		if(arg_number == 1)
		{


			//
			// Check verboseMode
			//

			const char *verb_string;

			if(notation_file->GetEntry("resource","verbose",&verb_string))
			{
				if(!stricmp(verb_string, "true"))
				{
					verboseMode = True;
				}
			}

			//---------------------------------
			//  Set up directories
			//--------------------------------

			if(!notation_file->GetEntry("resource","dirmap",&tempdir))
			{
				DEBUG_STREAM << "No map directory specified!!!" << std::endl << std::flush;
				PostQuitMessage(AbortExitCodeID);
			}

			strcpy(dirmap, tempdir);

			if(!notation_file->GetEntry("resource","dirmodel", &tempdir))
			{
				DEBUG_STREAM << "No model directory specified!!!" << std::endl << std::flush;
				PostQuitMessage(AbortExitCodeID);
			}

			strcpy(dirmodel, tempdir);

			if(!notation_file->GetEntry("resource","dircollision", &tempdir))
			{
				DEBUG_STREAM << "No collision directory specified!!!" << std::endl << std::flush;
				PostQuitMessage(AbortExitCodeID);
			}

			strcpy(dircollision, tempdir);


			const char
				*vp,
				*version_string;
			Byte version[2] = {0,0};

			notation_file->GetEntry("resource","diraudio", &tempdir);
			if (tempdir)
			{
				strcpy(diraudio, tempdir);
			}
			else
			{
				DEBUG_STREAM << "No directory specified for 'diraudio'!\n" << std::flush;
				PostQuitMessage(AbortExitCodeID);
			}

			notation_file->GetEntry("resource","dirvideo", &tempdir);
			if (tempdir)
			{
				strcpy(dirvideo, tempdir);
			}
			else
			{
				DEBUG_STREAM << "No directory specified for 'dirvideo'!\n" << std::flush;
				PostQuitMessage(AbortExitCodeID);
			}

			notation_file->GetEntry("resource","diranimation", &tempdir);
			if (tempdir)
			{
				strcpy(diranim, tempdir);
			}
			else
			{
				DEBUG_STREAM << "No directory specified for 'diranimation'!\n" << std::flush;
				PostQuitMessage(AbortExitCodeID);
			}

			notation_file->GetEntry("resource","dirgauge", &tempdir);
			if (tempdir)
			{
				strcpy(dirgauge, tempdir);
			}
			else
			{
				DEBUG_STREAM << "No directory specified for 'gauge'!\n" << std::flush;
				PostQuitMessage(AbortExitCodeID);
			}
			resource_directories.collisionDirectory = dircollision;
			resource_directories.modelDirectory = dirmodel;
			resource_directories.mapDirectory = dirmap;
			resource_directories.audioDirectory = diraudio;
			resource_directories.videoDirectory = dirvideo;
			resource_directories.animationDirectory = diranim;
			resource_directories.gaugeDirectory = dirgauge;

			//-------------------------
			//  Get version info
			//-------------------------


			if (!notation_file->GetEntry("release","version", &version_string))
			{
				DEBUG_STREAM << "No version number in build!!!!" << std::endl << std::flush;
				PostQuitMessage(AbortExitCodeID);
			}

			version[0] = (Byte)atoi(version_string);

			vp = strchr(version_string, '.');

			if (vp)
			{
				vp++;
				version[1] = (Byte)atoi(vp);
			}

			//-------------------------
			// Get Resource Name
			//-------------------------

			if(!notation_file->GetEntry("resource","name",&Res_Name))
			{
				DEBUG_STREAM << "No resource file specified!!!" << std::endl << std::flush;
				PostQuitMessage(AbortExitCodeID);
			}

			resfilename = MakePathedFilename(WorkingDirectory, Res_Name);
			Register_Pointer(resfilename);

			strcpy(ops_filename, Res_Name);
			char *p = ops_filename;

			p += strlen(ops_filename);
			p -= 4;
			*p = '\0';



			//================================
			// Set ops_file "res name"
			// write maps and write models
			// can use this for ops file
			// format.
			//================================

			ops_file->SetEntry("resource", "res name", ops_filename);
			ops_file->AppendEntry("resource", NULL, (char *)NULL);

			strcat(ops_filename, ".ops");

			file.versionArray[1] = version[0];
			file.versionArray[2] = version[1];
         file.versionArray[3] = minor_version;
		}

		//
		//---------------------------
		// Now step thru each entry
		// in every build file
		//---------------------------
		//

		NameList *entry_list =
			notation_file->MakeEntryList("resource");
		Register_Object(entry_list);
		NameList::Entry *entry = entry_list->GetFirstEntry();

		const char *entry_data;

		int pre_allocated = 0;

		while (entry != NULL)
		{

			entry_data = entry->GetName();
			if (entry_data == NULL)
			{
				entry = entry->GetNextEntry();
				continue;
			}

			entry_data = entry->GetChar();
			if (entry_data == NULL)
			{
				entry = entry->GetNextEntry();
				continue;
			}

			strcpy(id_name, entry_data);
			int id = GetId(id_name);
			if ( id != -1)
			{
				if(arg_number > 1)
				{

					Fail("Manual ResourceID's not allowed in second build files!");

				}

				if (id > pre_allocated)
				{
					pre_allocated = id;
				}
			}

			entry = entry->GetNextEntry();
		}

		//
		// pre_allocated number....
		//
		file.PreAllocateIDs(pre_allocated);



		entry = entry_list->GetFirstEntry();
		while (entry != NULL)
		{

			entry_data = entry->GetName();

			if (entry_data == NULL)
			{
				entry = entry->GetNextEntry();
				continue;
			}

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

				const char *q = entry->GetChar();
				strcpy(long_name, q);


				int id = GetId(long_name);
				if (id != -1)
				{

					//
					// Strip  id number off
					//

					char *p = strchr(long_name, '\t');
					if (p)
					{
						*p = '\0';
					}
					else
					{
						char *p = strchr(long_name, ' ');
						*p = '\0';
					}
				}


				//
				// open mapname
				//

				char *filename = MakePathedFilename(resource_directories.mapDirectory, long_name);
				Register_Pointer(filename);

				DEBUG_STREAM << "Opening Map " << filename << std::endl << std::flush;

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

				labOnly |= new_notation_file->IsMarkedLabOnly();

				StripDirectory(long_name);
				strcpy(short_name,  long_name);


				if (new_notation_file->PageCount())
				{
					if (
						!WriteMaps(
							short_name,
							new_notation_file,
							ops_file,
							&file,
							&resource_directories,
							id
						)
					)
					{
						DEBUG_STREAM << filename << " Did not work!!!! " << std::endl << std::flush;

					}
				}
				else
				{
					 DEBUG_STREAM << filename << " Does not exist! " << std::endl << std::flush;
				}


				Unregister_Pointer(filename);
				delete filename;
				Unregister_Object(new_notation_file);
				delete new_notation_file;
			}
			else if (!stricmp(entry_data, "model"))
			{

				strcpy(long_name, entry->GetChar());

				int id = GetId(long_name);
				if (id != -1)
				{

					//
					// Strip  id number off
					//

					char *p = strchr(long_name, '\t');
					if (p)
					{
						*p = '\0';
					}
					else
					{
						char *p = strchr(long_name, ' ');
						*p = '\0';
					}

				}


				//
				// open modelname
				//

				char *filename = MakePathedFilename(resource_directories.modelDirectory, long_name);
				Register_Pointer(filename);
				DEBUG_STREAM << "Opening Model " << filename << std::endl << std::flush;
				NotationFile *new_notation_file = new NotationFile(filename);
				Register_Object(new_notation_file);

				labOnly |= new_notation_file->IsMarkedLabOnly();

				const char *p = StripExtension(StripDirectory(long_name));

				strcpy(short_name, p);


				if (new_notation_file->PageCount())
				{
					if (
						!WriteModels(
							new_notation_file,
							ops_file,
							&file,
							short_name,
							&resource_directories,
							id
						)
					)
					{

						DEBUG_STREAM << "Error - Model did not work : " << filename << std::endl << std::flush;

					}
				}
				else
				{
					DEBUG_STREAM << filename << " does not exist " << std::endl << std::flush;

				}


				Unregister_Pointer(filename);
				delete filename;

				Unregister_Object(new_notation_file);
				delete new_notation_file;

			}
			else if (!stricmp(entry_data, "zone"))
			{
				char *q = entry->GetChar();
				strcpy(long_name, q);

				//
				// open zone
				//

				char *filename = MakePathedFilename(resource_directories.mapDirectory, long_name);
				Register_Pointer(filename);

				DEBUG_STREAM << "Opening Zones " << filename << std::endl << std::flush;

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

				labOnly |= new_notation_file->IsMarkedLabOnly();

				if (new_notation_file->PageCount())
				{

				  InterestLatticeManager::CreateZoneResources
					(
						&file,
						new_notation_file
					);
				}
				else
				{
					 DEBUG_STREAM << filename << " Does not exist! " << std::endl << std::flush;
				}


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

			}
			else if (!stricmp(entry_data, "static"))
			{
				//
				// Add registry static objects
				//
				Check(entry);
				const char *static_object_file_name = entry->GetChar();
				Check_Pointer(static_object_file_name);

				char *full_path_file_name =
					MakePathedFilename(
						resource_directories.modelDirectory,
						static_object_file_name
					);
				Register_Pointer(full_path_file_name);

				NotationFile
					static_object_notation_file(full_path_file_name);

				Registry::CreateStaticObjectStreamResource(
					&static_object_notation_file,
					&file
				);

				Unregister_Pointer(full_path_file_name);
				delete full_path_file_name;
			}

			entry = entry->GetNextEntry();
		}

		Unregister_Object(entry_list);
		delete entry_list;

		//
		//-------------------------------------------------------
		// Make this resource Lab only if labOnly was specified
		//-------------------------------------------------------
		//

		if (labOnly == True)
		{
			file.labOnly = 1;
		}


		Unregister_Pointer(WorkingDirectory);
		Unregister_Object(notation_file);

		delete WorkingDirectory;
		delete notation_file;

	}

	WriteOpsFile(ops_file, &file, &resource_directories);

	file.SaveAs(resfilename);
	ops_file->WriteFile(ops_filename);


	Unregister_Pointer(resfilename);
	delete resfilename;

	//------------------------
	// open the vehicle.tbl
	// and write to ops and
	//------------------------


	Unregister_Object(ops_file);
	delete ops_file;

	return 0;
}

//#############################################################################

void
	ApplicationTool::ListResource(
		ResourceDescription *resource
	)
{
	Check(resource);
	switch (resource->resourceType)
	{
	case ResourceDescription::ModelListResourceType:
		DEBUG_STREAM << resource->resourceName << ": Model List of "
		 << *(int*)resource->resourceAddress << " elements";
		break;

	case ResourceDescription::MapListResourceType:
		DEBUG_STREAM << resource->resourceName << ": Map List of "
		 << *(int*)resource->resourceAddress << " elements";
		break;

	case ResourceDescription::AudioStreamListResourceType:
		DEBUG_STREAM << resource->resourceName << ": Audio stream List of "
		 << *(int*)resource->resourceAddress << " elements";
		break;

	case ResourceDescription::VideoListResourceType:
		DEBUG_STREAM << resource->resourceName << ": Video List of "
		 << *(int*)resource->resourceAddress << " elements";
		break;

	case ResourceDescription::SubsystemListResourceType:
		DEBUG_STREAM << resource->resourceName << ": Subsystem List of "
		 << *(int*)resource->resourceAddress << " elements";
		break;

	case ResourceDescription::ControlMappingsListResourceType:
		DEBUG_STREAM << resource->resourceName << ": ControlsMappings List of "
		 << *(int*)resource->resourceAddress << " elements";
		break;

	case ResourceDescription::DamageZoneListResourceType:
		DEBUG_STREAM << resource->resourceName << ": DamageZone List of "
		 << *(int*)resource->resourceAddress << " elements";
		break;

	case ResourceDescription::SkeletonListResourceType:
		DEBUG_STREAM << resource->resourceName << ": Skeleton List of "
		 << *(int*)resource->resourceAddress << " elements";
		break;

	case ResourceDescription::BoxedSolidStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": Boxed Solid Stream of "
		 << resource->resourceSize/sizeof(BoxedSolidResource)
		 << " solids";
		break;

	case ResourceDescription::VideoModelResourceType:
		DEBUG_STREAM << resource->resourceName << ": VideoModel" << std::flush;
		break;

	case ResourceDescription::StaticAudioStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": StaticAudioStream" << std::flush;
		break;

	case ResourceDescription::InternalAudioStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": InternalAudioStream" << std::flush;
		break;

	case ResourceDescription::ExternalAudioStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": ExternalAudioStream" << std::flush;
		break;

	case ResourceDescription::MakeMessageStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": Map stream of "
		 << *(int*)resource->resourceAddress << " instances";
		break;

	case ResourceDescription::GameModelResourceType:
		DEBUG_STREAM << resource->resourceName << ": GameModel" << std::flush;
		break;

	case ResourceDescription::AnimationResourceType:
		DEBUG_STREAM << resource->resourceName << ": Animation" << std::flush;
		break;

	case ResourceDescription::SubsystemModelStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": Stream of "
		 << *(int*)resource->resourceAddress << " Subsystems";
		break;

	case ResourceDescription::GaugeImageStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": GaugeImage" << std::flush;
		break;

	case ResourceDescription::ControlMappingStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": Stream of "
		 << *(int*)resource->resourceAddress << " ControlsMappings";
		break;

	case ResourceDescription::DamageZoneStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": Stream of "
		 << *(int*)resource->resourceAddress << " DamageZones";
		break;

	case ResourceDescription::SkeletonStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": Stream of "
		 << *(int*)resource->resourceAddress << " Segments";
		break;

	case ResourceDescription::EnvironmentZoneResourceType:
		DEBUG_STREAM << resource->resourceName << ": EnvironmentZone" << std::flush;
		break;

	case ResourceDescription::InterestZoneResourceType:
		DEBUG_STREAM << resource->resourceName << ": InterestZone" << std::flush;
		break;

	case ResourceDescription::VehicleTableResourceType:
		DEBUG_STREAM << resource->resourceName << ": VehicleTable" << std::flush;
		break;

	case ResourceDescription::ExistanceBoxStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": Stream of "
		 << resource->resourceSize/sizeof(ExtentBox)
		 << " Existance boxes";
		break;

	case ResourceDescription::CameraStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": Stream of "
		 << *(int*)resource->resourceAddress << " Cameras";
		break;

	case ResourceDescription::DamageLookupTableStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": DamageZoneLookupTable" << std::flush;
		break;

	case ResourceDescription::ExplosionTableStreamResourceType:
		DEBUG_STREAM << resource->resourceName << ": ExplosionTable" << std::flush;
		break;

	default:
		DEBUG_STREAM << "Unknown type " << resource->resourceType << std::flush;
		break;
	}
}

//#############################################################################

int
	ApplicationTool::ListResources(
		int argc,
		char **argv
	)
{
	if (argc < 2)
	{
		DEBUG_STREAM << "Usage: listXX resource.res [res2.res ...]\n" << std::flush;
		return 1;
	}

	for (int i=1; i < argc; ++i)
	{
		char res_name[200];
		strcpy(res_name, argv[i]);

		StreamableResourceFile
			file;

		file.Open(res_name);

		DEBUG_STREAM << res_name << ": v" << (int)file.versionArray[0] << '.'
		 << (int)file.versionArray[1] << '.' << (int)file.versionArray[2] << '.'
		 << (int)file.versionArray[3] << std::endl;

		if (file.labOnly == 1)
		{
			DEBUG_STREAM << "LAB ONLY RESOURCE!" << std::endl << std::flush;
		}

		DEBUG_STREAM << std::endl << std::flush;
		DEBUG_STREAM << "ID#   Size    Data\n" << std::flush;
		DEBUG_STREAM << "----  ------  -----------------------------------------------------\n" << std::flush;

		for (int j=0; j<=file.GetMaxResourceID(); ++j)
		{
			DEBUG_STREAM << std::setw(4) << j << "  " << std::flush;
			ResourceDescription *resource = file.FindResourceDescription(j);
			if (resource)
			{
				DEBUG_STREAM << std::setw(6) << resource->resourceSize << "  " << std::flush;
				resource->Lock();
				ListResource(resource);
            resource->Unlock();
			}
			else
			{
				DEBUG_STREAM << "          Not Used" << std::flush;
			}
			DEBUG_STREAM << std::endl << std::flush;
		}
	}
	return 0;
}

