#include "MW4Headers.hpp"
#pragma warning(disable:4100)	// unreferenced formal parameter

#include "CampaignMechLab.hpp"
#include <gosScript\gosScriptHeaders.hpp>

#include "Mech.hpp"
#include "MWDamageObject.hpp"
#include "Subsystem.hpp"
#include "Weapon.hpp"
#include "MWMover.hpp"
#include "Armor.hpp"
#include "SubsystemClassData.hpp"
#include "Salvage.hpp"
#include "MWGame.hpp"
#include "MWApplication.hpp"
#include "MWTable.hpp"

#include <Adept\Entity.hpp>
#include <Adept\VideoRenderer.hpp>
#include <Adept\Site.hpp>

#include <ElementRenderer\CameraElement.hpp>
#include <ElementRenderer\StateChange.hpp>
#include <ElementRenderer\GroupElement.hpp>

#include <MLR\MLRTexturePool.hpp>

#include "..\buildnum\buildnum.h"

// jcem begin
#if 1 // jcem for MechView, CampaignMechLab.cpp too
#define CONN4ML             (Connection::Hermit)
#else
#define CONN4ML             (Connection::Local)
#endif

// jcem end
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CampaignMechLab::CampaignMechLab() :
	MechLab()
{
//	m_chassisCount = GetChassisCount();
	m_isCampaign = 1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CampaignMechLab::ReInitialize()
{
	m_mechIDs = NULL;
	m_chassisIDs = NULL;
	DestroyWorkingEntity();

	if(m_mechResourceFile.GetCurrent())
		delete m_mechResourceFile.GetCurrent();

	m_mechResourceFile.Remove();

	m_mode = ChassisTabMode;

	m_mechLabCount = GetMechCount();
	m_chassisCount = GetChassisCount();

	//Need to somehow make this a paramter so we can start with the current
	//mech if there was one in the pilot place.
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CampaignMechLab::InitializeSubsystemsAvailable()
{
	Check_Object(MWGame::GetInstance());
	Check_Object(MWGame::GetInstance()->m_salvageManager);

	SortedChainIteratorOf<SalvagePlug*, int> iterator(&MWGame::GetInstance()->m_salvageManager->m_salvageList);
	SalvagePlug *salvage_plug;

	while((salvage_plug = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(salvage_plug);
		
		Resource model_resource(salvage_plug->m_salvageID);
		Verify(model_resource.DoesResourceExist());

		Subsystem::GameModel *sub_model = Cast_Pointer(
			Subsystem::GameModel *, model_resource.GetPointer());

		//We are a Generic Sub and should be unlimmited
		if((sub_model->itemID >= FirstGenericSubsystemID) && (sub_model->itemID <= LastGenericSubsystemID))
		{
			SubsystemResource *sub_resource;

			sub_resource = AddAvailableSubsystem(
					salvage_plug->m_dataListID, 
					salvage_plug->m_salvageID, 
					sub_model->itemID
				);
			Check_Object(sub_resource);
			sub_resource->SetUnlimited();
		}
		else
		{
			//We need to check in with the salvage manager to get what is available!
			for(int i=0; i<salvage_plug->m_count; i++)
			{
				AddAvailableSubsystem(
					salvage_plug->m_dataListID, 
					salvage_plug->m_salvageID, 
					sub_model->itemID
				);
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int 
	CampaignMechLab::GetChassis(void* data[], int count)
{
	Check_Pointer(this);

	return 1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int 
	CampaignMechLab::GetChassisCount()
{
	Check_Pointer(this);
	Check_Object(MWGame::GetInstance());
	Check_Object(MWGame::GetInstance()->m_salvageManager);

	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int CampaignMechLab::GetMechCount()
{
	Check_Pointer(this);
	Check_Object(MWGame::GetInstance());

	int count = 0;
	SortedChainIteratorOf<MechTablePlug *, MString> iterator(&MWGame::GetInstance()->m_mechTable);
	MechTablePlug *mech_plug;

	while((mech_plug = iterator.ReadAndNext()) != NULL)
	{
		if(mech_plug->GetStatus() != MechTablePlug::DestroyedStatus)
		{
			count ++;
		}
	}
	return count;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int CampaignMechLab::GetMechs(void* data[], int count)
{
	Check_Pointer(this);
	Check_Object(MWGame::GetInstance());

	char **name_array = STRARRAYPARAM(1);
	int *selected_mech = INTARRAYPARAM(2);

	*selected_mech = 0; // jcem

	if(m_mechLabCount > 0)
	{
		if(m_mechIDs)
		{
			gos_Free(m_mechIDs);
			m_mechIDs = NULL;
		}
		m_mechIDs = (ResourceID *)gos_Malloc(m_mechLabCount * sizeof(ResourceID));
	}

	int i = 0;

	SortedChainIteratorOf<MechTablePlug *, MString> iterator(&MWGame::GetInstance()->m_mechTable);
	MechTablePlug *mech_plug;

	while((mech_plug = iterator.ReadAndNext()) != NULL)
	{
		if(mech_plug->GetStatus() != MechTablePlug::DestroyedStatus)
		{
			FREEANDNULL(name_array[i]);
			name_array[i] = (char *)gos_Malloc(128);
			Str_Copy(
				name_array[i], 
				(const char *)mech_plug->GetMechName(), 
				mech_plug->GetMechName().GetLength() + 1
			); 
			m_mechIDs[i] = mech_plug->GetMechResourceID();
			i++;
		}
	}
	return 1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	CampaignMechLab::SelectMech(void *data[], int count, const char *path)
{
	Check_Pointer(this);

	const char *mech_name = STRPARM(2);

	MWGame::GetInstance()->ResetSalvageManager();
	m_subsystemsAvailable.DeletePlugs();
	InitializeSubsystemsAvailable();
	int return_value = MechLab::SelectMech(data, count, "");
	Check_Object(GetWorkingMech());
	MechTablePlug *mech_plug = MWGame::GetInstance()->m_mechTable.Find(mech_name);
	MWGame::GetInstance()->SetCurrentMechPlug(mech_plug);
	m_workingMech->instanceName = mech_plug->GetOriginalName();

	return return_value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	CampaignMechLab::SaveMech()
{
	//This needs to be done totally diferntly than that other one
	if(GetWorkingMech())
	{
		Check_Object(MWGame::GetInstance());

		DynamicMemoryStream stream;
		m_workingMech->SaveMakeMessage(&stream, MWGame::GetInstance()->GetGameResourceFile());

		stream.Rewind();
	//	Stuff::FileDependencies dependancies;
	//	dependancies.AddDependency("");
		MechTablePlug *mech_plug = MWGame::GetInstance()->GetCurrentMechPlug();
		Check_Object(mech_plug);
		Resource mech_resource(mech_plug->GetMechResourceID());
		mech_resource.Save(&stream, NULL);	
	}
	MWGame::GetInstance()->SaveMWGame(MWGame::GetInstance()->GetGameName(), false);
	MWGame::GetInstance()->ResetSalvageManager();
	m_subsystemsAvailable.DeletePlugs();
	InitializeSubsystemsAvailable();
	
	//MWGame::GetInstance()->GetGameResourceFile()->Save();
	return 1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	CampaignMechLab::Exit()
{
//	SaveMech();
	Check_Object(MWGame::GetInstance());
	MWGame::GetInstance()->ResetSalvageManager();

	MWGame::GetInstance()->SaveMWGame(MWGame::GetInstance()->GetGameName(), false);

	return MechLab::Exit();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	CampaignMechLab::CreateNewMech(void *data[], int count, const char *path)
{
	Check_Pointer(this);

	STOP(("SHOULD NEVER GET HERE!"));

	return -1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	CampaignMechLab::RemoveEquipmentFromList(Mech *mech)
{
	Check_Pointer(this);
	Check_Object(mech);

	int return_value = 1;

	ChainIteratorOf<Subsystem *> sub_iterator(&mech->subsystemsInVehicle);
	Subsystem *subsystem;
	while((subsystem = sub_iterator.ReadAndNext()) != NULL)
	{
		Check_Object(subsystem);
		const Subsystem::GameModel *sub_model = subsystem->GetGameModel();
		Check_Object(sub_model);
		if(sub_model->itemID > -1)
		{
			if(DoesHaveAvailableSubsystem(sub_model->itemID))
			{
				//We have it in stock...now subtract it from stock
				RemoveAvailableSubsystem(sub_model->itemID);
			}
			else
			{
				subsystem->DisconnectSubsystem();
				subsystem->Destroy();

				return_value = -1;
			}
		}
	}

	return return_value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CampaignMechLab::AddEquipmentToList(Mech *mech)
{
	Check_Pointer(this);
	Check_Object(mech);

	//Here we need to run through the mech equipment and re add it to our
	//list of available stuff.  
	ChainIteratorOf<Subsystem *> sub_iterator(&mech->subsystemsInVehicle);
	Subsystem *subsystem;
	while((subsystem = sub_iterator.ReadAndNext()) != NULL)
	{
		Check_Object(subsystem);
		const Subsystem::GameModel *sub_model = subsystem->GetGameModel();
		Check_Object(sub_model);

		if(sub_model->itemID > -1)
		{
			AddAvailableSubsystem(subsystem->GetDataListResourceID(), 
				subsystem->GetGameModelResourceID(), 
				sub_model->itemID
			);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CampaignMechLab::SetUpWorkingMech(Entity *entity)
{
	Check_Pointer(this);
	Check_Object(entity);

	MechLab::SetUpWorkingMech(entity);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CampaignMechLab::AddSubsystemToMech(int *sub_type, int *zone, int *error)
{
	MechLab::AddSubsystemToMech(sub_type, zone, error);

	//Make sure that we added the subsystem!
	if(GetWorkingMech() && (*error == 1))
	{
		Check_Object(m_workingMech);
		MWInternalDamageObject *internal_zone;

		internal_zone = m_workingMech->internalDamageObjects.Find(*zone);
		Check_Object(internal_zone);
		
		Subsystem *subsystem = internal_zone->GetSubsystem(*sub_type);
		if((subsystem) && (*sub_type != Sub_HeatSink))
		{
			MWGame::GetInstance()->m_salvageManager->RemoveSalvage(subsystem);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	CampaignMechLab::AutoAddSubsystem(void *data[], int count)
{
	int *sub_type = INTARRAYPARAM(1);
	int *error = INTARRAYPARAM(2);

	*error = 1;
	if(GetWorkingMech())
	{
		Check_Object(m_workingMech);
		SubsystemResource *sub_resource = m_subsystemsAvailable.Find(*sub_type);
		if(sub_resource)
		{
			Check_Object(sub_resource);
			RegisteredClass::ClassID class_id = Entity::GetClassIDFromDataListID(sub_resource->GetDataListID());
			Subsystem::ClassData *class_data =
				Cast_Pointer(Subsystem::ClassData*, RegisteredClass::FindClassData(class_id));
			Check_Object(class_data);
			DynamicMemoryStream stream;
			(*class_data->streamCreate)(sub_resource->GetDataListID(), &stream);
			ReplicatorID base_id = Connection::Local->GetNextReplicatorID();
			
			Subsystem__CreateMessage *create_message = 
				Cast_Pointer(Subsystem__CreateMessage *, stream.GetPointer());


			//Ok now we have the subsystem stream...before we create we need to know where
			//we want to put the thing.  

			Resource sub_model_resource(sub_resource->GetGameModelID());
			Verify(sub_model_resource.DoesResourceExist());

			Subsystem::GameModel *sub_model = Cast_Pointer(
				Subsystem::GameModel *, sub_model_resource.GetPointer());

			int slots_needed = sub_model->totalSlotsTaken;
			int slot_type = sub_model->slotType;

			//First check for a slot with the correct type, starting with the arms
			int location = FindInternalLocation(slot_type, slots_needed);
			//Next check for an omni slot starting with the arms
			if(location == -1)
				location = FindInternalLocation(MWInternalDamageObject::OmniSlotType, slots_needed);

			if(location == -1)
			{
				*error = 0;
				return 0;
			}
			//if no zone then exit!
			create_message->locationID = (char)location;

			Subsystem *subsystem = 
				Cast_Object(Subsystem *, Entity::CreateEntity(&stream, &base_id));
			Check_Object(subsystem);

			if(subsystem->IsDerivedFrom(Weapon::DefaultData))
			{
				Weapon *weapon = Cast_Object(Weapon *, subsystem);
				MWInternalDamageObject *zone_object = m_workingMech->internalDamageObjects.Find(location);
				Check_Object(zone_object);
				Site *site = zone_object->GetNextWeaponSite();
				if(!site)
				{
					*error = 0;
					subsystem->Destroy();
					return 0;
				}
				weapon->siteName = site->GetName();
			}

			m_workingMech->AddChild(subsystem);
			m_workingMech->SyncMatrices(true);
			if(subsystem->ConnectSubsystem())
			{
				m_workingMech->AddSubsystem(subsystem);
				RemoveAvailableSubsystem(*sub_type);
				MWGame::GetInstance()->m_salvageManager->RemoveSalvage(subsystem);
			}
			else
			{
				subsystem->Destroy();
				*error = 0;
				return 0;
			}
		}
		else
		{
			*error = 0;
			return 0;
		}
	}

	MWApplication *app = MWApplication::GetInstance();
	app->memoryDiffKiller.Init();
	return 1;
}
	

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CampaignMechLab::RemoveWeaponFromMech(int *sub_id, int *zone)
{
	if(GetWorkingMech())
	{
		Check_Object(m_workingMech);
		MWInternalDamageObject *internal_zone;

		internal_zone = m_workingMech->internalDamageObjects.Find(*zone);
		Check_Object(internal_zone);
		
	//	Subsystem *subsystem = internal_zone->subsystemChain.GetNth(*sub_id);
		Subsystem *subsystem;
		int weapon_count = 0;
		ChainIteratorOf<Subsystem *> iterator(&internal_zone->subsystemChain);
		while((subsystem = iterator.ReadAndNext()) != NULL)
		{
			Check_Object(subsystem);
			if(subsystem->IsDerivedFrom(Weapon::DefaultData))
			{
				if(*sub_id == weapon_count)
				{
					//we need to get the subsystem name from the table using the model resource name
					Check_Object(MWApplication::GetInstance());
					MString sub_name = subsystem->GetModelName();
					MWTableEntry *sub_entry = 
						MWApplication::GetInstance()->m_weaponsTable->FindEntryFromDataString(sub_name);
					Check_Object(sub_entry);
					MWGame::GetInstance()->m_salvageManager->AddSalvage(subsystem, sub_entry->GetEntryName());
				}
				else
				{
					weapon_count ++;
				}
			}
		}
	}
	MechLab::RemoveWeaponFromMech(sub_id, zone);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CampaignMechLab::RemoveSubsystemFromMech(int *sub_type, int *zone)
{
	if(GetWorkingMech())
	{
		Check_Object(m_workingMech);
		MWInternalDamageObject *internal_zone;

		internal_zone = m_workingMech->internalDamageObjects.Find(*zone);
		Check_Object(internal_zone);
		
		Subsystem *subsystem = internal_zone->GetSubsystem(*sub_type);
		//Subsystem *subsystem = internal_zone->subsystemChain.GetNth(*sub_type);
		if((subsystem)&& (*sub_type != Sub_HeatSink))
		{
			//we need to get the subsystem name from the table using the model resource name
			Check_Object(MWApplication::GetInstance());
			MString sub_name = subsystem->GetModelName();
			MWTableEntry *sub_entry = 
				MWApplication::GetInstance()->m_subsystemTable->FindEntryFromDataString(sub_name);
			Check_Object(sub_entry);
			MWGame::GetInstance()->m_salvageManager->AddSalvage(subsystem, sub_entry->GetEntryName());
		}
	}
	MechLab::RemoveSubsystemFromMech(sub_type, zone);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	CampaignMechLab::RemoveAllSubsystems()
{
	if(GetWorkingMech())
	{
		Check_Object(m_workingMech);
		ChainIteratorOf<Subsystem *> iterator(&m_workingMech->subsystemsInVehicle);	
		Subsystem *subsystem;

		while((subsystem = iterator.ReadAndNext()) != NULL)
		{
			Check_Object(subsystem);

			if(subsystem->IsDerivedFrom(Weapon::DefaultData))
			{
				//Disconnect, add to our reserves, and delete
				Check_Object(MWApplication::GetInstance());
				MString sub_name = subsystem->GetModelName();
				MWTableEntry *sub_entry = 
					MWApplication::GetInstance()->m_weaponsTable->FindEntryFromDataString(sub_name);
				if(!sub_entry)
				{
					//Check the subsystem one
					sub_entry = 
						MWApplication::GetInstance()->m_subsystemTable->FindEntryFromDataString(sub_name);
				}
				Check_Object(sub_entry);
				MWGame::GetInstance()->m_salvageManager->AddSalvage(subsystem, sub_entry->GetEntryName());
				subsystem->DisconnectSubsystem();
				m_workingMech->RemoveSubsystem(subsystem);
				AddAvailableSubsystem(subsystem);
				subsystem->Destroy();
			}
		}

		//SaveMech();
		return 1;
	}
	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	CampaignMechLab::Rename(void *data[], int count, const char *)
{
	const char *chassis_name = STRPARM(1);
	const char *mech_name = STRPARM(2);

	MString new_name = MString(chassis_name)+" "+mech_name;
	//new_name.ToLower();

	MechTablePlug *mech_plug = MWGame::GetInstance()->GetCurrentMechPlug();
	Check_Object(mech_plug);
	MString old_name = mech_plug->GetMechName();
	//old_name.ToLower();

	if (new_name == old_name)
		return 1;

	if (MWGame::GetInstance()->m_mechTable.Find(new_name))
		return E_MechAlreadyExists;


	MWGame::GetInstance()->m_mechTable.Remove(mech_plug);
	mech_plug->SetMechName(MString(chassis_name)+" "+mech_name);

	MWGame::GetInstance()->m_mechTable.AddValue(mech_plug, new_name);

	return 1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int CampaignMechLab::Restore()
{
	Check_Object(MWGame::GetInstance());
	MWGame::GetInstance()->ResetSalvageManager();
	m_subsystemsAvailable.DeletePlugs();
	InitializeSubsystemsAvailable();

	MechTablePlug *mech_plug = MWGame::GetInstance()->GetCurrentMechPlug();

	DestroyWorkingEntity();

	Resource mech_resource(mech_plug->GetMechResourceID());
	Verify(mech_resource.DoesResourceExist());

	ReplicatorID base_id = Connection::Local->GetNextReplicatorID();
	Entity *entity;
	entity = Entity::CreateEntity(&mech_resource, &base_id);

	Check_Object(entity);
	SetUpWorkingMech(entity);
	
	m_workingMech->instanceName = mech_plug->GetOriginalName();

	return 1;
}
