//===========================================================================//
// File:	bridge.cpp										
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/12/2000  AHF  Inital base class
//===========================================================================//

#include "MW4Headers.hpp"

#include "Lancemate.hpp"
#include "AI_UserConstants.hpp"
#include "MWMission.hpp"
#include "MWTool.hpp"

using namespace MW4AI;
																																	
//#############################################################################
//############################    LancemateManager    #########################
//#############################################################################

LancemateManager::ClassData* LancemateManager::DefaultData = NULL;
LancemateManager *LancemateManager::Instance = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancemateManager::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			LancemateManagerClassID,
			"MechWarrior4::LancemateManager",
			Receiver::DefaultData,
			0,
			NULL
		);
	Check_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancemateManager::TerminateClass()
{
	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
LancemateManager::LancemateManager(MemoryStream *stream)
	: Receiver(DefaultData),
  	m_lancemates(NULL)
{
	Check_Pointer(stream);
	int number,i;

	*stream >> number;
	for (i = 0;i<number;i++)
	{
		LancematePlug *lance;

		lance = new LancematePlug (stream);
		Check_Object(lance);
		m_lancemates.Add(lance);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
LancemateManager::~LancemateManager()
{
	Check_Object(this);
	m_lancemates.DeletePlugs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
LancemateManager* LancemateManager::MakeNewLancemateManager(const char *lancematemanager_name)
{
	Check_Pointer(lancematemanager_name);

	Resource lancematemanager_resource(lancematemanager_name);
	Verify(lancematemanager_resource.DoesResourceExist());
	lancematemanager_resource.LoadData();

	LancemateManager *new_lancematemanager;
	new_lancematemanager = new LancemateManager(&lancematemanager_resource);
	Check_Object(new_lancematemanager);

	return new_lancematemanager;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
LancemateManager* LancemateManager::MakeNewLancemateManager(Adept::ResourceID lancemate_id)
{
	Resource lancematemanager_resource(lancemate_id);
	Verify(lancematemanager_resource.DoesResourceExist());

	LancemateManager *new_lancematemanager;
	new_lancematemanager = new LancemateManager(&lancematemanager_resource);
	Check_Object(new_lancematemanager);

	return new_lancematemanager;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancemateManager::ConstructLancemateStream(MemoryStream *stream,NotationFile *notation_file)
{
	Check_Pointer(stream);
	Check_Object (notation_file);

	NotationFile::PageIterator *pages = notation_file->MakePageIterator();
	Check_Object(pages);

	*stream << pages->GetSize ();
	Page *page_entry;
	while ((page_entry = pages->ReadAndNext()) != NULL)
	{
		Check_Object(page_entry);
		LancematePlug::ConstructStream (stream,page_entry);
	}
	delete pages;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancemateManager::Save(MemoryStream *stream)
{
	ChainIteratorOf<LancematePlug *> iterator(&m_lancemates);
	LancematePlug *lancemate_plug;
	*stream << m_lancemates.GetSize();
	while((lancemate_plug = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(lancemate_plug);
		lancemate_plug->Save (stream);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancemateManager::WriteToText(const char *file_name)
{
	Stuff::NotationFile file (file_name);

	ChainIteratorOf<LancematePlug *> iterator(&m_lancemates);
	LancematePlug *lancemate_plug;
	while((lancemate_plug = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(lancemate_plug);
		lancemate_plug->WriteToText (file);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancemateManager::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//#############################################################################
//##########################     LancematePlug   ##############################
//#############################################################################


LancematePlug::LancematePlug(Stuff::MemoryStream *stream) : 	Plug(Plug::DefaultData)
{
	MW4AI::UserConstants::IncrementRefCount();
	*stream >> m_lancemateName;
	*stream >> m_TalkerSuffix;
	*stream >> m_lanceDataListID;
	*stream >> m_MustSurvive;
	int state;
	*stream >> state;
	m_CurrentState = (LANCEMATE_STATE) state;
	*stream >> m_Joined;
	*stream >> m_GunnerySkill;
	*stream >> m_PilotSkill;
	*stream >> m_EliteSkill;
	*stream >> m_MinHeatSkill;
	*stream >> m_MaxHeatSkill;
	*stream >> m_SensorSkill;
	*stream >> m_BlindFightingSkill;
	*stream >> m_LongRangeGunnerySkill;
	*stream >> m_ShortRangeGunnerySkill;
	*stream >> m_TowardsPilotSkill;
	*stream >> m_TowardsGunnerySkill;
	*stream >> m_TowardsEliteSkill;
	*stream >> m_StartGunnerySkill;
	*stream >> m_StartPilotSkill;
	*stream >> m_StartEliteSkill;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
LancematePlug::~LancematePlug()
{
	MW4AI::UserConstants::DecrementRefCount();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancematePlug::WriteToText(Stuff::NotationFile& file)
{
	Check_Object(this);
	Check_Object(&file);

	Stuff::Page *page = file.AddPage (m_lancemateName);
	page->AppendEntry ("Name",m_lancemateName);
	Resource model_resource(m_lanceDataListID);
	Verify(model_resource.DoesResourceExist());
	page->AppendEntry("TalkerSuffix",m_TalkerSuffix);
	page->AppendEntry ("Model", model_resource.GetName());
	page->AppendEntry("MustSurvive",m_MustSurvive);
	page->AppendEntry("CurrentState",m_CurrentState);
	page->AppendEntry("Joined",m_Joined);
	page->AppendEntry("Gunnery",m_GunnerySkill);
	page->AppendEntry("Pilot",m_PilotSkill);
	page->AppendEntry("Elite",m_EliteSkill);
	page->AppendEntry("MinHeat",m_MinHeatSkill);
	page->AppendEntry("MaxHeat",m_MaxHeatSkill);
	page->AppendEntry("Sensor",m_SensorSkill);
	page->AppendEntry("BlindFight",m_BlindFightingSkill);
	page->AppendEntry("LongRange",m_LongRangeGunnerySkill);
	page->AppendEntry("ShortRange",m_ShortRangeGunnerySkill);
	page->AppendEntry("TowardsPilot",m_TowardsPilotSkill);
	page->AppendEntry("TowardsGunnery",m_TowardsGunnerySkill);
	page->AppendEntry("TowardsElite",m_TowardsEliteSkill);
	page->AppendEntry("StartGunnery",m_StartGunnerySkill);
	page->AppendEntry("StartPilot",m_StartPilotSkill);
	page->AppendEntry("StartElite",m_StartEliteSkill);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancematePlug::Save(MemoryStream *stream)
{
	*stream << m_lancemateName;
	*stream << m_TalkerSuffix;
	*stream << m_lanceDataListID;
	*stream << m_MustSurvive;
	*stream << ((int) m_CurrentState);
	*stream << m_Joined;
	*stream << m_GunnerySkill;
	*stream << m_PilotSkill;
	*stream << m_EliteSkill;
	*stream << m_MinHeatSkill;
	*stream << m_MaxHeatSkill;
	*stream << m_SensorSkill;
	*stream << m_BlindFightingSkill;
	*stream << m_LongRangeGunnerySkill;
	*stream << m_ShortRangeGunnerySkill;
	*stream << m_TowardsPilotSkill;
	*stream << m_TowardsGunnerySkill;
	*stream << m_TowardsEliteSkill;
	*stream << m_StartGunnerySkill;
	*stream << m_StartPilotSkill;
	*stream << m_StartEliteSkill;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
template <class T>
T GetPageValue(Stuff::Page *page, const char* name, T default_value)
{
	T temp = default_value;
	page->GetEntry(name,&temp,false);
	return (temp);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancematePlug::ConstructStream(MemoryStream *stream,Stuff::Page *page)
{
	Check_Object (stream);
	Check_Object (page);

//	*stream << page->GetName ();
	MString name;
	const char *name_string;
	page->GetEntry("Name", &name_string, true);
	name = name_string;
	*stream << name;

	MString talker_suffix;
	const char *suffix;
	page->GetEntry("TalkerSuffix", &suffix, true);
	talker_suffix = suffix;
	*stream << talker_suffix;

	//Get the model and make sure that it is made...
	const char *resource_name;
	page->GetEntry("Model", &resource_name);
	Resource model_resource;
	Tool::Instance->ConstructDataList(&model_resource, resource_name);
	Check_Object(&model_resource);
	Verify(model_resource.DoesResourceExist());
	*stream << model_resource.GetResourceID();

	*stream << GetPageValue<bool>(page,"MustSurvive",false);
	*stream << GetPageValue<int>(page,"CurrentState",0);
	*stream << GetPageValue<bool>(page,"Joined",false);
	*stream << GetPageValue<int>(page,"Gunnery",-1);
	*stream << GetPageValue<int>(page,"Pilot",-1);
	*stream << GetPageValue<int>(page,"Elite",-1);
	*stream << GetPageValue<int>(page,"MinHeat",-1);
	*stream << GetPageValue<int>(page,"MaxHeat",-1);
	*stream << GetPageValue<int>(page,"Sensor",-1);
	*stream << GetPageValue<int>(page,"BlindFight",-1);
	*stream << GetPageValue<int>(page,"LongRange",-1);
	*stream << GetPageValue<int>(page,"ShortRange",-1);
	*stream << GetPageValue<int>(page,"TowardsPilot",-1);
	*stream << GetPageValue<int>(page,"TowardsGunnery",-1);
	*stream << GetPageValue<int>(page,"TowardsElite",-1);
	*stream << GetPageValue<int>(page,"StartGunnery",-1);
	*stream << GetPageValue<int>(page,"StartPilot",-1);
	*stream << GetPageValue<int>(page,"StartElite",-1);
}

int LancematePlug::GetLanceIDBasedOnSuffix()
{
	Check_Object(this);

	//This is hardcoded for our game
	if(!_stricmp((const char *)m_TalkerSuffix, "_GON"))
		return 1;
	if(!_stricmp((const char *)m_TalkerSuffix, "_CAS"))
		return 4;
	if(!_stricmp((const char *)m_TalkerSuffix, "_JEN"))
		return 2;
	if(!_stricmp((const char *)m_TalkerSuffix, "_TER"))
		return 0;
	if(!_stricmp((const char *)m_TalkerSuffix, "_DAM"))
		return 3;

	return -1;
}
	
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//		Skills

int DefaultedSkill(int skill_value, MW4AI::UserConstants::ID id)
{
	Verify(MW4AI::UserConstants::Instance());

	if (skill_value == -1)
	{
		return ((int)MW4AI::UserConstants::Instance ()->Get(id));
	}

	return skill_value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int LancematePlug::GunnerySkill (void) const
{
	return m_GunnerySkill;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int LancematePlug::PilotSkill (void) const
{
	return m_PilotSkill;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int LancematePlug::SensorSkill (void) const
{
	return m_SensorSkill;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int LancematePlug::BlindFightingSkill (void) const
{
	return m_BlindFightingSkill;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int LancematePlug::LongRangeGunnerySkill (void) const
{
	return (DefaultedSkill(m_LongRangeGunnerySkill,MW4AI::UserConstants::default_longrange));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int LancematePlug::ShortRangeGunnerySkill (void) const
{
	return (DefaultedSkill(m_ShortRangeGunnerySkill,MW4AI::UserConstants::default_shortrange));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int LancematePlug::EliteSkill (void) const
{
	return (DefaultedSkill(m_EliteSkill,MW4AI::UserConstants::default_elite));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int LancematePlug::MinHeatSkill (void) const
{
	return (DefaultedSkill(m_MinHeatSkill,MW4AI::UserConstants::default_minheat));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int LancematePlug::MaxHeatSkill (void) const
{
	return (DefaultedSkill(m_MaxHeatSkill,MW4AI::UserConstants::default_maxheat));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Scalar GetConstantOrMissionValue(int mission_value, UserConstants::ID constant)
{
	if (mission_value == -1)
	{
		return (UserConstants::Instance()->Get(UserConstants::eject_chance_for_lancemate) * 0.01f);
	}

	return (((Stuff::Scalar)mission_value) * 0.01f);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool LancematePlug::ShouldEjectSafely()
{
#if 0
	if (m_MustSurvive == true)
	{
		m_CurrentState = OK;
		return (true);
	}
#endif

	MechWarrior4::MWMission *mission = Cast_Object(MechWarrior4::MWMission*,MechWarrior4::MWMission::GetInstance());
	Check_Object (mission);

	Stuff::Scalar normalized_chance_to_eject = GetConstantOrMissionValue(mission->m_ChanceLancematesEject,
																		 UserConstants::eject_chance_for_lancemate);

	if (Stuff::Random::GetFraction() < normalized_chance_to_eject)
	{
		Stuff::Scalar normalized_chance_to_be_ok = GetConstantOrMissionValue(mission->m_ChanceLancematesOKWhenEjecting,
																			 UserConstants::eject_chance_ok_when_ejected);

		Stuff::Scalar normalized_chance_to_be_injured = GetConstantOrMissionValue(mission->m_ChanceLancematesInjuredWhenEjecting,
																				  UserConstants::eject_chance_injured_when_ejected);

		Stuff::Scalar random = Stuff::Random::GetFraction();

		if (random < normalized_chance_to_be_ok)
		{
			m_CurrentState = OK;
		}
		else
		{
			if (random < normalized_chance_to_be_injured + normalized_chance_to_be_ok)
			{
				m_CurrentState = INJURED;
			}
			else
			{
				if (m_MustSurvive)
					m_CurrentState = INJURED;
				else
					m_CurrentState = MIA;
			}
		}

		return (true);
	}

	if (m_MustSurvive)
		m_CurrentState = INJURED;
	else
		m_CurrentState = KIA;
	return (false);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancemateManager::RevealLancemate (char *name)
{
	LancematePlug *plug;

	plug = FindLancemate (name,false);
	Verify (plug);
	plug->Joined (true);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void LancemateManager::HideLancemate (char *name)
{
	LancematePlug *plug;

	plug = FindLancemate (name,false);
	Verify (plug);
	plug->Joined (false);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
LancematePlug*
	LancemateManager::FindLancemate(MString lance_name,bool case_sensitive)
{
	//
	// We can't use sorted chain or the data doesn't appear in the same order as the
	// campaign source file. But the normal chain does not have a find value.
	//
	Verify(lance_name);

	ChainIteratorOf<LancematePlug *> find_iterator(&m_lancemates);
	LancematePlug *cur_lance;
	while ((cur_lance = find_iterator.ReadAndNext()) != NULL)
	{
		if (case_sensitive)
		{
			if (cur_lance->m_lancemateName == lance_name)
				break;
		}
		else
		{
			if (!strnicmp ((char *) cur_lance->m_lancemateName,lance_name,sizeof (lance_name)))
				break;
		}
	}
	return cur_lance;
}
