//===========================================================================//
// File:	NetAutoPacketSpliter.cpp											
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/06/1999  JSE  Inital coding
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Microsoft Corp.										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//


#include "MW4Headers.hpp"

#include "MWEntityManager.hpp"
#include "NetUpdateManager.hpp"
#include "Dictionary.hpp"
#include "NetAutoPacketSpliter.hpp"

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

NetUpdateSortEntry::NetUpdateSortEntry(Entity *entity, int size, int type, UpdateRate rate):
	Plug(DefaultData),
	m_rate(rate)
{
	m_entity = entity;
	m_size = size;
	m_type = type;
}

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

NetUpdatePageHolder::NetUpdatePageHolder(int page_overhead):
	Plug(DefaultData),
	m_UpdateEntries(NULL)
{
	entryCount = 0;
	totalMaxSize = page_overhead;
}


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

NetUpdatePageHolder::~NetUpdatePageHolder()
{
	m_UpdateEntries.DeletePlugs();
}

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

void NetUpdatePageHolder::AddToList(NetUpdateSortEntry *entry, int paragraph_overhead)
{
	++entryCount;
	totalMaxSize += entry->m_size + paragraph_overhead;

	m_UpdateEntries.Add(entry);

	
}

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

bool NetUpdatePageHolder::IsThereRoom(NetUpdateSortEntry *entry, int paragraph_overhead)
{
	//if (entryCount+1 >= 64)
	//	return false;

	if (totalMaxSize + entry->m_size + paragraph_overhead < (Network::DefaultPacketSize*8))
		return true;

	return false;
}

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


#ifdef LAB_ONLY
char* Text_Table[DictionaryPage::UpdateCycleCount] = 
{
	"HalfhzType",
	"OneHzType",
	"TwoHzType",
	"ThreeHzType",
	"FourHzType",
	"SixHzType",
	"EightHzType",
	"NineHzType",
	"TwelveHzType",
	"SixteenHzType" 
};


char* Update_Text_Table[UpdateManager::UpdateCount] = 
{
	"MechPositionUpdateID",
	"MechAnimationUpdateID",
	"MechMovemntUpdateID",
	"MechExternalDamageUpdateID",
	"MechFirstPersonPositionUpdateID",
	"MechFirstPersonControlUpdateID",
	"WeaponCommandID",
	"MissionObjectiveUpdateID",
	"MechInternalDamageUpdateID",
	"MechInternalHeatUpdateID",
	"SubsystemUpdateID",
	"FlushUpdateID",
	"SearchLightUpdateID",
	"InternalAMSAmmoUpdateID",
	"ExternalAMSAmmoUpdateID",
	"NearBuildingExternalDamageUpdateID",
	"SecondaryBuildingExternalDamageUpdateID",
	"FarBuildingExternalDamageUpdateID",
	"ExternalJumpJetUpdateID",
	"InternalJumpJetUpdateID",
	"VehicleDamageUpdateID",
	"AirMovementUpdateID",
	"GroundMovementUpdateID",
	"TurretMovementUpdateID",
	"TorsoMovementUpdateID",
	"NavPointUpdateID",
	"FlagUpdateID",
	"TimeUpdateID",
	"SecurityQueryID",
	"SecurityResponseID",
	"PingUpdateID"
};
#endif


void NetUpdatePageHolder::AddSelfToDictionary(Dictionary *dictionary, int page_number)
{

	DictionaryPage *page = dictionary->MakeDictionaryPage(page_number);

	//SPEW(("jerryeds", "\n\nADDING PAGE : %d, UPDATE COUNT : %d - SIZE %d", page_number, entryCount, totalMaxSize));

	Stuff::ChainIteratorOf<NetUpdateSortEntry*> iterator(&m_UpdateEntries);
	NetUpdateSortEntry *entry;
	while ((entry = iterator.ReadAndNext()) != NULL)
	{
		//SPEW(("jerryeds", "%s : %s : size %d", Update_Text_Table[entry->m_type], Text_Table[entry->m_rate], entry->m_size));
		page->MakeDictionaryParagraph(entry->m_entity, entry->m_type, entry->m_rate);
	}
}


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


NetUpdateSorter::NetUpdateSorter(int debug_level, int connection_rate):
	Plug(DefaultData),
	m_halfhzList(NULL),
	m_oneHzList(NULL),
	m_twoHzList(NULL),
	m_threeHzList(NULL),
	m_fourHzList(NULL),
	m_sixHzList(NULL),
	m_eightHzList(NULL),
	m_nineHzList(NULL),
	m_twelveHzList(NULL),
	m_sixteenHzList(NULL)
{
	m_bandwidth = connection_rate;
	m_debugLevel = debug_level;

	updateList[0] = &m_halfhzList;
	updateList[1] = &m_oneHzList;
	updateList[2] = &m_twoHzList;
	updateList[3] = &m_threeHzList;
	updateList[4] = &m_fourHzList;
	updateList[5] = &m_sixHzList;
	updateList[6] = &m_eightHzList;
	updateList[7] = &m_nineHzList;
	updateList[8] = &m_twelveHzList;
	updateList[9] = &m_sixteenHzList;

	// dictionary version 8bit
	// page number 8bit
	// full ping time stamp 64bit

	pageOverhead = 16+64;
	paragraphOverhead = 1;
	switch(debug_level)
	{
	case 1:
		pageOverhead += 8;
		break;
	case 2:
		pageOverhead += 16;
		paragraphOverhead += 8;
		break;
	}

}


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

NetUpdateSorter::~NetUpdateSorter()
{
	updateList[0]->DeletePlugs(); 
	updateList[1]->DeletePlugs(); 
	updateList[2]->DeletePlugs(); 
	updateList[3]->DeletePlugs(); 
	updateList[4]->DeletePlugs(); 
	updateList[5]->DeletePlugs(); 
	updateList[6]->DeletePlugs(); 
	updateList[7]->DeletePlugs(); 
	updateList[8]->DeletePlugs(); 
	updateList[9]->DeletePlugs(); 
}

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

void NetUpdateSorter::AddUpdateToList(Entity *entity, int type, UpdateRate rate)
{

	UpdateEntry *update = &UpdateManager::UpdateEntries[type];
	int size = (update->m_maxSize)(entity, m_debugLevel, m_bandwidth);

	NetUpdateSortEntry *entry = new NetUpdateSortEntry(entity, size, type, rate);
	
	Verify(rate.m_ActiveRate >= 0);
	Verify(rate.m_ActiveRate < 10);
	Verify(rate.m_InactiveRate >= 0);
	Verify(rate.m_InactiveRate < 10);

	Check_Object(updateList[rate.m_ActiveRate]);
	updateList[rate.m_ActiveRate]->Add(entry);

}

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

void NetUpdateSorter::MakeDictionaryPages(Dictionary *dictionary)
{


	// sort by like packets first...


	// remember: anyway i cut it the cull bit will allways be sent
	//           the cull bit costs only go down if I only use like
	//			 update rates


	

	ChainOf<NetUpdatePageHolder*> twos_pages(NULL);
	ChainOf<NetUpdatePageHolder*> threes_pages(NULL);

	ChainOf<NetUpdateSortEntry*> *sub_list[4];

	int twos_count = 0;
	sub_list[0] = &m_sixteenHzList;
	sub_list[1] = &m_eightHzList;
	sub_list[2] = &m_fourHzList;
	sub_list[3] = &m_twoHzList;

	for (int i = 0; i < 4; ++i)
	{
		Stuff::ChainIteratorOf<NetUpdateSortEntry*> iterator(sub_list[i]);
		NetUpdateSortEntry *entry;
		while ((entry = iterator.GetCurrent()) != NULL)
		{
			++twos_count;
			FindPageWithRoom(&twos_pages, entry);
			iterator.Remove();
		}

	}

	sub_list[0] = &m_twelveHzList;
	sub_list[1] = &m_nineHzList;
	sub_list[2] = &m_sixHzList;
	sub_list[3] = &m_threeHzList;
	
	int threes_count = 0;
	for (i = 0; i < 4; ++i)
	{
		Stuff::ChainIteratorOf<NetUpdateSortEntry*> iterator(sub_list[i]);
		NetUpdateSortEntry *entry;
		while ((entry = iterator.GetCurrent()) != NULL)
		{
			++threes_count;
			FindPageWithRoom(&threes_pages, entry);
			iterator.Remove();
		}

	}

	sub_list[0] = &m_oneHzList;
	sub_list[1] = &m_halfhzList;
	
	int page_count = 0;

	if (twos_count)
	{
		for (i = 0; i < 2; ++i)
		{
			Stuff::ChainIteratorOf<NetUpdateSortEntry*> iterator(sub_list[i]);
			NetUpdateSortEntry *entry;
			while ((entry = iterator.GetCurrent()) != NULL)
			{
				++twos_count;
				FindPageWithRoom(&twos_pages, entry);
				iterator.Remove();
			}
		}

		Stuff::ChainIteratorOf<NetUpdatePageHolder*> iterator(&twos_pages);
		NetUpdatePageHolder *page;
		while ((page = iterator.ReadAndNext()) != NULL)
		{
			page->AddSelfToDictionary(dictionary, page_count);
			++page_count;
		}
	}

	if (threes_count)
	{
		for (i = 0; i < 2; ++i)
		{
			Stuff::ChainIteratorOf<NetUpdateSortEntry*> iterator(sub_list[i]);
			NetUpdateSortEntry *entry;
			while ((entry = iterator.GetCurrent()) != NULL)
			{
				++threes_count;
				FindPageWithRoom(&threes_pages, entry);
				iterator.Remove();
			}
		}

		Stuff::ChainIteratorOf<NetUpdatePageHolder*> iterator(&threes_pages);
		NetUpdatePageHolder *page;
		while ((page = iterator.ReadAndNext()) != NULL)
		{
			page->AddSelfToDictionary(dictionary, page_count);
			++page_count;
		}
	}



#ifdef _ARMOR
	Verify(!updateList[0]->GetSize()); 
	Verify(!updateList[1]->GetSize()); 
	Verify(!updateList[2]->GetSize()); 
	Verify(!updateList[3]->GetSize()); 
	Verify(!updateList[4]->GetSize()); 
	Verify(!updateList[5]->GetSize()); 
	Verify(!updateList[6]->GetSize()); 
	Verify(!updateList[7]->GetSize()); 
	Verify(!updateList[8]->GetSize()); 
	Verify(!updateList[9]->GetSize()); 
#endif



	twos_pages.DeletePlugs();
	threes_pages.DeletePlugs();


}


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


void NetUpdateSorter::FindPageWithRoom(ChainOf<NetUpdatePageHolder*> *pages, NetUpdateSortEntry *entry)
{

	Stuff::ChainIteratorOf<NetUpdatePageHolder*> iterator(pages);
	NetUpdatePageHolder *page;
	while ((page = iterator.ReadAndNext()) != NULL)
	{
		if (page->IsThereRoom(entry, paragraphOverhead))
		{
			page->AddToList(entry, paragraphOverhead);
			return;
		}
	}

	NetUpdatePageHolder *new_page = new NetUpdatePageHolder(pageOverhead);
	pages->Add(new_page);

	new_page->AddToList(entry, paragraphOverhead);

}

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

