// File:	MemoryDiffKiller.hpp											 //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 10/04/00  JSE  die haxorz die   
//---------------------------------------------------------------------------//
// Copyright (C) 2000, Microsoft Corp										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"

#include "MemoryDiffKiller.hpp"

#include "MWApplication.hpp"
#include "MechLab.hpp"
#include "Mech.hpp"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//


MemoryDiffKiller::MemoryDiffKiller():
 Plug(DefaultData)
{

	m_sourceCount = 0;

	for (int i = 0; i < Maximum_Sources; ++i)
	{
		m_sourceSize[i] = 0;
	}


	for (i = 0; i < Maximum_Copies; ++i)
	{
		m_copyMemory[i] = NULL;
		m_copySize[i] = 0;
		m_copySourceIndex[i] = 0;
	}



}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//


MemoryDiffKiller::~MemoryDiffKiller()
{
	for (int i = 0; i < Maximum_Copies; ++i)
	{
		if (m_copyMemory[i] != NULL)
		{
			gos_Free(m_copyMemory[i]);
			m_copyMemory[i] = NULL;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//



void MemoryDiffKiller::Init()
{

#ifdef LAB_ONLY
	if (!MWApplication::MoveItAndShakeIt)
		return;
#endif

	Uninit();

	if (MWApplication::GetInstance()!=NULL)
	{
		MWApplication *app = MWApplication::GetInstance();
		//m_sourceData[m_sourceCount].Add(app);
		//m_sourceSize[m_sourceCount] = sizeof(*app);
		//++m_sourceCount;

		if (app->m_serverMissionParameters)
		{
			m_sourceData[m_sourceCount].Add(app->m_serverMissionParameters);
			m_sourceSize[m_sourceCount] = sizeof(*app->m_serverMissionParameters);
			++m_sourceCount;
		}

		if (app->m_localMissionParameters)
		{
			m_sourceData[m_sourceCount].Add(app->m_localMissionParameters);
			m_sourceSize[m_sourceCount] = sizeof(*app->m_localMissionParameters);
			++m_sourceCount;
		}
	}

	if (MechLab::Instance!=NULL)
	{
		if (MechLab::Instance->m_workingMech != NULL)
		{
			
			// we want the mech to have a higher weight...

			for (int i = 0; i < 30; ++i)
			{
				m_sourceData[m_sourceCount].Add(MechLab::Instance->m_workingMech);
				m_sourceSize[m_sourceCount] = sizeof(*MechLab::Instance->m_workingMech);
				++m_sourceCount;
			}


			ChainIteratorOf<Subsystem *> sub_iterator(&MechLab::Instance->m_workingMech->subsystemsInVehicle);
			Subsystem *subsystem;

			while((subsystem = sub_iterator.ReadAndNext()) != NULL)
			{
				m_sourceData[m_sourceCount].Add(subsystem);
				m_sourceSize[m_sourceCount] = sizeof(*subsystem);
				++m_sourceCount;

				if (m_sourceCount == Maximum_Sources)
					break;
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void MemoryDiffKiller::Uninit()
{
#ifdef LAB_ONLY
	if (!MWApplication::MoveItAndShakeIt)
		return;
#endif

	m_sourceCount = 0;

	for (int i = 0; i < Maximum_Sources; ++i)
	{
		if (m_sourceData[i].GetCurrent())
		{
			m_sourceData[i].Remove();
		}

		m_sourceSize[i] = 0;
	}
	
	for (i = 0; i < Maximum_Copies; ++i)
	{
		if (m_copyMemory[i] != NULL)
		{
			gos_Free(m_copyMemory[i]);
			m_copyMemory[i] = NULL;
			m_copySize[i] = 0;
			m_copySourceIndex[i] = 0;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

//define CHECK_ALLOC_SIZE

void MemoryDiffKiller::MoveItAndShakeIt()
{
#ifdef LAB_ONLY
	if (!MWApplication::MoveItAndShakeIt)
		return;
#endif

	for (int i = 0; i < Maximum_Copies; ++i)
	{

		// if our source was deleted...
		if ( m_sourceData[m_copySourceIndex[i]].GetCurrent() == NULL)
		{
			gos_Free(m_copyMemory[i]);
			m_copyMemory[i] = NULL;
			m_copySize[i] = 0;
			m_copySourceIndex[i] = 0;
		}
		else
		{
				
			// if the number is null we can allocate it
			if (m_copyMemory[i] == NULL)
			{
				
				// this could have gone away since the init, so go
				// till we find one or try 5 times
				// if we don't get it this time, we'll try 5 more times next time

				int counter = 0;
				while (counter < 5)
				{

					int random_number = Random::GetLessThan(m_sourceCount);

					if (m_sourceData[random_number].GetCurrent() != NULL)
					{
						m_copySourceIndex[i] = (BYTE)random_number;
						m_copySize[i] = m_sourceSize[random_number];
						m_copyMemory[i] =  gos_Malloc(m_copySize[i]);

						memcpy(m_copyMemory[i], m_sourceData[m_copySourceIndex[i]].GetCurrent(), m_copySize[i]);

						break;
					}
					++counter;
				}

			}
			else
			{

				// if we churn too much we actually narrow down the real value.

				//else if we may randomly decide to remove a copy...
				//int random_number = Random::GetLessThan(500);
				//if (random_number == 0)
				//{
				//	gos_Free(m_copyMemory[i]);
				//	m_copyMemory[i] = NULL;
				//	m_copySize[i] = 0;
				//	m_copySourceIndex[i] = 0;
				//}
				//else
				{
					// we go ahead and copy the source...
					memcpy(m_copyMemory[i], m_sourceData[m_copySourceIndex[i]].GetCurrent(), m_copySize[i]);
				}
			}			
		}
	}




#ifdef CHECK_ALLOC_SIZE


	int percentages[Maximum_Sources];

	for (i = 0; i < Maximum_Sources; ++i)
	{
		percentages[i] = 0;
	}

	int total_size = 0;
	for (i = 0; i < Maximum_Copies; ++i)
	{
		if (m_copyMemory[i] != NULL)
		{
			++percentages[m_copySourceIndex[i]];

			total_size += m_copySize[i];
		}
	}

	SPEWALWAYS(("jerryeds", "TOTAL SIZE USED : %d", total_size));


	for (i = 0; i < Maximum_Sources; ++i)
	{
		float percent = ((float)percentages[i] / Maximum_Copies) * 100.0f;
		SPEWALWAYS(("jerryeds","%d - %d - %f", i, percentages[i], percent));
	}


#endif


}
