#include "AdeptHeaders.hpp"

#include "NameTable.hpp"
#include "Entity.hpp"
//#include <mw4\Cultural.hpp>

#include "Application.hpp"

//#############################################################################
//###########################    MissionNameTable    ##########################
//#############################################################################



NameTable::ClassData
	*NameTable::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			NameTableClassID,
			"Adept::NameTable",
			Plug::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameTable::NameTable() :
	Plug(DefaultData),
	nameTableArray(NULL)
{
	for(int i=DefaultArray; i<TableSize; i++)
		nameTableArray[i].SetLength(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameTable::~NameTable()
{
	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::LoadTable(MemoryStream *stream)
{
	Check_Object(this);
	Check_Pointer(stream);
	
	int num_arrays;
	*stream >> num_arrays;

	for(int i=0; i<num_arrays;i++)
	{
		int num_entries;
		*stream >> num_entries;
		nameTableArray[i].SetLength(num_entries);
		for(int j=0; j<num_entries; j++)
		{
			MString object_name;
			*stream >> object_name;
			ObjectID id;
			*stream >> id;
			nameTableArray[i][j].SetNameTableEntry(object_name, NULL);
			nameTableArray[i][j].SetObjectID(id);
		}
	}
}			

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::LoadTable(NotationFile *table_file)
{
	Check_Object(this);
	Check_Object(table_file);
	
	int array_index = 0;
	NotationFile::PageIterator *arrays = table_file->MakePageIterator();
	Check_Object(arrays);
	Page *array_instance;
	while ((array_instance = arrays->ReadAndNext()) != NULL)
	{
		Check_Object(array_instance);
		const char *array_name = array_instance->GetName();
		Check_Pointer(array_name);
		array_index = atoi(array_name);
		Verify(array_index < TableSize);
		if((array_index != PlayerArray) && (array_index != PlayerAIArray))
		{
			Page::NoteIterator *entries = array_instance->MakeNoteIterator();
			Check_Object(entries);
			nameTableArray[array_index].SetLength(entries->GetSize());
			Note *table_entry;
			int entry_array_index = 0;
			int object_index = 0;
			while ((table_entry = entries->ReadAndNext()) != NULL)
			{
				Check_Object(table_entry);

				const char *object_name = table_entry->GetName();
				Check_Pointer(object_name);
		//		table_entry->GetEntry(&object_index);
				int object_id = NameTableEntry::BuildObjectID(array_index, object_index);
				nameTableArray[array_index][entry_array_index].SetNameTableEntry(object_name, NULL);
				nameTableArray[array_index][entry_array_index].SetObjectID(object_id);
				entry_array_index ++;
				object_index++;
			}
			delete entries;
		}
		array_index = atoi(array_name);
	}
	Check_Object(arrays);
	delete arrays;

	//
	//----------------------------------------------------
	//We now need to hardcode the init of the player array
	//----------------------------------------------------
	//
	nameTableArray[PlayerArray].SetLength(Maximum_Players);
	int p;
	for(p=0; p<Maximum_Players; p++)
	{
		char player_name[256];
		sprintf(player_name, "Player%d", p);
		int object_id = NameTableEntry::BuildObjectID(PlayerArray, p);
		nameTableArray[PlayerArray][p].SetNameTableEntry(player_name, NULL);
		nameTableArray[PlayerArray][p].SetObjectID(object_id);
	}
	nameTableArray[PlayerAIArray].SetLength(Maximum_Players);
	for(p=0; p<Maximum_Players; p++)
	{
		char player_name[256];
		sprintf(player_name, "PlayerAI%d", p);
		int object_id = NameTableEntry::BuildObjectID(PlayerAIArray, p);
		nameTableArray[PlayerAIArray][p].SetNameTableEntry(player_name, NULL);
		nameTableArray[PlayerAIArray][p].SetObjectID(object_id);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::Clear()
{
	Check_Pointer(this);
	for(int i=DefaultArray; i<TableSize; i++)
		nameTableArray[i].SetLength(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::SaveTable(MemoryStream *stream)
{
	Check_Pointer(this);
	Check_Pointer(stream);
	
	int size = TableSize;
	*stream << size;

	for(int i=0; i<TableSize;i++)
	{
		NameTableEntryArray entry_array = nameTableArray[i];
		*stream << entry_array.GetLength();
		for(int j=0; j<entry_array.GetLength(); j++)
		{
			
			*stream << entry_array[j].objectName;
			*stream << entry_array[j].GetObjectID();
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::SaveTable(NotationFile *table_file)
{
	Check_Pointer(this);
	Check_Object(table_file);
	
	for(int i=0; i<TableSize;i++)
	{
		NameTableEntryArray entry_array = nameTableArray[i];
		char page_name[256] = "";
		sprintf((char *)page_name, "%d", i);
		Page *page = table_file->SetPage(page_name);
		Check_Object(page);
		for(int j=0; j<entry_array.GetLength(); j++)
		{
		//	page->SetEntry(
		//		entry_array[j].objectName,
		//		NameTableEntry::GetObjectIndex(entry_array[j].GetObjectID())
		//	);
			page->SetEntry(
				entry_array[j].objectName,
				69
			);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::AddEntry(Entity *entity, int array_index)
{
	Check_Object(this);
	Check_Object(entity);
	
	Verify(FindID(entity->instanceName)==NullObjectID);	
	
	array_index = entity->GetTableArray();

	entity->objectID = NullObjectID;

	if(array_index < TableSize)
	{
		int index = nameTableArray[array_index].GetLength();
		nameTableArray[array_index].SetLength(index + 1);
		nameTableArray[array_index][index].SetNameTableEntry(entity->instanceName, entity);
		entity->objectID = NameTableEntry::BuildObjectID(array_index, index);
		nameTableArray[array_index][index].SetObjectID(entity->objectID);

		SetData(entity);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ObjectID
	NameTable::AddEntry(const char *entity_name, int array_index)
{
	Check_Object(this);
	Check_Pointer(entity_name);
	

	int object_id = NullObjectID;

	if(array_index < TableSize)
	{
		int index = nameTableArray[array_index].GetLength();
		nameTableArray[array_index].SetLength(index + 1);
		nameTableArray[array_index][index].SetNameTableEntry(entity_name, NULL);
		object_id = NameTableEntry::BuildObjectID(array_index, index);
		nameTableArray[array_index][index].SetObjectID(object_id);
	}

	return object_id;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	NameTable::GetLength(int array_index)
{
	Check_Object(this);
	
	if(array_index < TableSize)
	{
		return (nameTableArray[array_index].GetLength());
	}
	return -1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ObjectID
	NameTable::FindID(const char *object_name)
{
	Check_Object(this);

	if(object_name == NULL)
		return NullObjectID;
	if(!stricmp(object_name, ""))
		return NullObjectID;

	for(int i=DefaultArray; i<TableSize; i++)
	{
		int array_length = nameTableArray[i].GetLength();
		for(int j=0; j<array_length; j++)
		{
			if(nameTableArray[i][j].objectName && !_stricmp(nameTableArray[i][j].objectName, object_name))
				return nameTableArray[i][j].GetObjectID();
		}
	}

	return NullObjectID;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity
	*NameTable::FindData(ObjectID id)
{
	Check_Object(this);
	NameTableEntry *entry = FindEntry(id);
	if(!entry)
		return (Entity *)-1;

	Check_Object(entry);

	return entry->dataPointer->GetCurrent();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::SetData(Entity *entity)
{
	Check_Object(this);
	Check_Object(entity);

	if(entity->objectID == NullObjectID)
		return; 

	int array_index = NameTableEntry::GetArrayIndex(entity->objectID);
	int object_index = NameTableEntry::GetObjectIndex(entity->objectID);


	Verify(array_index < TableSize);
	Verify(object_index < nameTableArray[array_index].GetLength());
	nameTableArray[array_index][object_index].dataPointer->Remove();
	nameTableArray[array_index][object_index].dataPointer->Add(entity);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::SetData(ObjectID id,Entity *entity)
{
	Check_Object(this);
//	Check_Object(entity);

	if(id == NullObjectID)
		return; 

	int array_index = NameTableEntry::GetArrayIndex(id);
	int object_index = NameTableEntry::GetObjectIndex(id);

	if(array_index < TableSize)
	{
		Verify(object_index < nameTableArray[array_index].GetLength());
		nameTableArray[array_index][object_index].dataPointer->Remove();
		if(entity)
			nameTableArray[array_index][object_index].dataPointer->Add(entity);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char
	*NameTable::FindName(ObjectID id)
{
	Check_Object(this);
	NameTableEntry *entry = FindEntry(id);
	if(!entry)
		return NULL;
	Check_Object(entry);

	return entry->objectName;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameTableEntry
	*NameTable::FindEntry(ObjectID id)
{
	Check_Object(this);

	int array_index = NameTableEntry::GetArrayIndex(id);
	int object_index = NameTableEntry::GetObjectIndex(id);
	if(id == NullObjectID)
		return NULL;
	Verify(array_index >= 0);
	if(array_index < TableSize)
	{
		if(object_index >= nameTableArray[array_index].GetLength())
			return NULL;
		return &nameTableArray[array_index][object_index];
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameTableEntry
	*NameTable::FindEntry(const char *object_name)
{
	Check_Object(this);
	Check_Pointer(object_name);

	Check_Object(this);

	if(object_name == NULL)
		return NULL;

	for(int i=DefaultArray; i<TableSize; i++)
	{
		int array_length = nameTableArray[i].GetLength();
		for(int j=0; j<array_length; j++)
		{
			if(!_stricmp(nameTableArray[i][j].objectName, object_name))
				return &nameTableArray[i][j];
		}
	}

	return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
NameTable::IsValid()
{
	Check_Object(this);
bool errors=false;
	for(int i=DefaultArray; i<TableSize; i++)
	{
		int array_length = nameTableArray[i].GetLength();
		if(i==BuildingArray ||
			i==CulturalArray ||
			i==VehicleArray ||
			i==DropZoneArray ||
			i==PathArray ||
			i==NavArray ||
			i==AIArray ||
			i==TurretArray
			)
		{
			for(int j=0; j<array_length; j++)
			{
				if(!(i==1&& j==0))
				{
				if(!nameTableArray[i][j].dataPointer->GetCurrent()) 
				{
					if(nameTableArray[i][j].objectName)
						PAUSE(("NameTable Error: \"%s\"( Page=%i , Entry=%i ),in .Table file Does not have an entry in the contents file",(char *)nameTableArray[i][j].objectName,i,j));
					else
						PAUSE(("NameTable Error: \"\" ( Page=%i , Entry=%i ) in .Table file Does not have an entry in the contents file",i,j));
					//
					//-------------------------------------------------
					//We now need to remove the entry and fill the hole
					//-------------------------------------------------
					//


					for(int k=j; k<(array_length-1); k++)
					{
						Verify((k+1) < array_length);
						Entity *entity;
						MString object_name;
						if(nameTableArray[i][k+1].dataPointer->GetCurrent())
						{
						entity = nameTableArray[i][k+1].dataPointer->GetCurrent();
						object_name = nameTableArray[i][k+1].objectName;
						nameTableArray[i][k].SetNameTableEntry(object_name, entity);
						if(entity)		
							entity->objectID = NameTableEntry::BuildObjectID(i, k);
						}
					}
					nameTableArray[i].SetLength(array_length-1);
					array_length-=1;
					j--;
					errors=true;
				}
				else
				{
				ObjectID id=nameTableArray[i][j].dataPointer->GetCurrent()->objectID;
					if(NameTableEntry::GetArrayIndex(id)!=i || 
							NameTableEntry::GetObjectIndex(id)!=j) 
								{
							if(nameTableArray[i][j].objectName)
								PAUSE(("NameTable Error: \"%s\" (%i,%i) Has been set incorrectly",(char *)nameTableArray[i][j].objectName,i,j));
							else
								PAUSE(("NameTable Error: \"\" (%i,%i) Has been set incorrectly",i,j));
								errors=true;
			
				}
				}
				}
			}


		}
	}
	
	return !errors;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameTableEntry
	*NameTable::FindEntry(int array_index,int entry_index)
{
	Check_Object(this);

	return &nameTableArray[array_index][entry_index];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::RemoveEntry(NameTableEntry *entry)
{
	Check_Object(this);
	Check_Object(entry);

	STOP(("This needs to be implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::RemoveEntry(ObjectID id)
{
	Check_Object(this);

	int array_index = NameTableEntry::GetArrayIndex(id);
	int object_index = NameTableEntry::GetObjectIndex(id);

	if(id == NullObjectID)
		return;

	Verify(array_index >= 0);
	Verify(array_index < TableSize);
	Verify(object_index < nameTableArray[array_index].GetLength());

	int array_length = nameTableArray[array_index].GetLength();
	//
	//---------------------------------
	//Check if we are last in our array
	//---------------------------------
	//
	if(object_index == (nameTableArray[array_index].GetLength() - 1))
	{
		nameTableArray[array_index].SetLength(object_index);
		return;
	}
	//
	//------------------------------------------------
	//Check to see if we are the only one in the array
	//------------------------------------------------
	//
	if(array_length == 1)
	{
		nameTableArray[array_index].SetLength(0);
		return;
	}

	//
	//-------------------------------------------------
	//We now need to remove the entry and fill the hole
	//-------------------------------------------------
	//
	for(int i=object_index; i<(array_length-1); i++)
	{
		Verify((i+1) < array_length);
		Entity *entity;
		MString object_name;
		entity = nameTableArray[array_index][i+1].dataPointer->GetCurrent();
		object_name = nameTableArray[array_index][i+1].objectName;
		nameTableArray[array_index][i].SetNameTableEntry(object_name, entity);
		if(entity)		
			entity->objectID = NameTableEntry::BuildObjectID(array_index, i);
	}
	nameTableArray[array_index].SetLength(array_length-1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameTable::SetName(ObjectID id, const char *new_name)
{
	Check_Object(this);

	NameTableEntry *entry = FindEntry(id);
	if(entry)
	{
		Check_Object(entry);
		entry->objectName = new_name;
	}
}
