//===========================================================================//
// File:	MissionReview.cpp											
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 07/11/2000  JSE  Inital coding
//---------------------------------------------------------------------------//
// Copyright (C) 2000, Microsoft Corp.										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//


#include "MW4Headers.hpp"
#include "MissionReview.hpp"
#include "MWMission.hpp"
#include "MWApplication.hpp"
#include "MWPlayer.hpp"
#include "Bucket.hpp"
#include <Adept\DamageObject.hpp>
#include <Adept\Map.hpp>

#if !defined(NO_MR)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

MRPlayerData::MRPlayerData()
{
	Reset();
}

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

void MRPlayerData::Reset()
{
	m_playerStatus = NoChange;
	m_playerName = NULL;
	m_position = Point3D::Identity;
	m_yawRotation = 0.0f;
	m_takenDamage = false;
	m_firedWeapon = false;
	m_alive = false;


	m_scoreBucket1 = 0;
	m_scoreBucket2 = 0;
	m_scoreBucket3 = 0;

	
	for (int i = 0; i < Damage_Zones; ++i)
	{
		m_damageLevel[i] = 0;
	}


}

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

MissionReview::MissionReview():
	Plug(DefaultData)
{

	m_missionLog = NULL;
	m_nameLog = NULL;

	for (int i = 0; i < Maximum_Players; ++i)
	{
		m_playBackLog[i] = NULL;
	}

	ClearLog();

}

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

MissionReview::~MissionReview()
{
	
	ClearLog();
	
}

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

void MissionReview::ClearLog(void)
{
	timeSinceLastSnapShot = 0.0;
	m_playersInGame = 0;
	m_bufferedSnapShots = 0;
	m_totalSnapShots = 0;
	m_snapShotFrequency = 0.0f;
	for (int i = 0; i < Maximum_Players; ++i)
	{
		m_playerData[i].Reset();

		if (m_playBackLog[i] != NULL)
		{
			delete[] m_playBackLog[i];
			m_playBackLog[i] = NULL;
		}
	}

	if (m_missionLog != NULL)
	{
		delete m_missionLog;
		m_missionLog = NULL;
	}

	if (m_nameLog != NULL)
	{
		delete m_nameLog;
		m_nameLog = NULL;
	}

	minExtents = Point3D::Identity;
	maxExtents = Point3D::Identity;
	missionName = "\0";

	haveHeaderInfo = false;

	m_mode = NoMode;
	m_recording = false;
}

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

void MissionReview::ReadyRecord()
{
	ClearLog();
	m_mode = RecordMode;

	m_missionLog = new DynamicMemoryStream;
	m_missionLog->AllocateBytes(1000000); // about one meg
	m_nameLog = new DynamicMemoryStream;
	m_nameLog->AllocateBytes(64000); // alot of friggin names...
}



void MissionReview::StartRecordingLog(Scalar frequency)
{

	m_recording = true;
	
	m_snapShotFrequency = frequency;	

	Mission *miss = Mission::GetInstance();
	Check_Object(miss);
	missionName = miss->GetModelName ();

	Map::GetInstance()->GetMapExtents(&minExtents.z, &maxExtents.z, &minExtents.x, &maxExtents.z);

}

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

void MissionReview::StopRecordingLog(void)
{
	Verify(m_mode == RecordMode);

	m_nameLog->WriteByteAlign();
	m_missionLog->WriteByteAlign();

	m_nameLog->Rewind();
	m_missionLog->Rewind();


	
}

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

void MissionReview::AddPlayer(const char *player_name, int id)
{
	Verify(m_mode == RecordMode);

	int token = AddToken;
	m_nameLog->WriteBytes(&token, 1);
	m_nameLog->WriteBytes(&m_bufferedSnapShots, 4);
	m_nameLog->WriteBytes(&id, 1);

	int size = strlen(player_name);
	size++;
	m_nameLog->WriteBytes(&size, 1);
	m_nameLog->WriteBytes(player_name, size);
	
	m_playerData[id].Reset();
	m_playerData[id].m_playerName = player_name;
	m_playerData[id].m_playerStatus = MRPlayerData::InToken;

	
}

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

void MissionReview::RemovePlayer(int id)
{
	Verify(m_mode == RecordMode);

	int token = RemoveToken;
	m_nameLog->WriteBytes(&token, 1);
	m_nameLog->WriteBytes(&m_bufferedSnapShots, 4);
	m_nameLog->WriteBytes(&id, 1);
	
	m_playerData[id].Reset();



}

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

void MissionReview::TakeSnapShot()
{
	Verify(m_mode == RecordMode);
	
	if (!m_recording)
		return;

	if ((gos_GetElapsedTime() - timeSinceLastSnapShot) < m_snapShotFrequency)
	{
		return;
	}

	timeSinceLastSnapShot = gos_GetElapsedTime();
	++m_bufferedSnapShots;
	m_totalSnapShots = m_bufferedSnapShots;
	
	MWMission *mission = Cast_Object(MWMission*, Mission::GetInstance());
	for (int i = 0; i < Maximum_Players; ++i)
	{
		if (m_playerData[i].m_playerStatus == MRPlayerData::InToken)
		{
			MWApplication *app = MWApplication::GetInstance();
			Verify(app->servedConnectionData[i].clientConnected);
			Verify(app->servedConnectionData[i].clientPlayer != NULL);
			Verify(app->servedConnectionData[i].clientPlayer->vehicle != NULL);

			m_playerData[i].m_alive = (app->servedConnectionData[i].clientPlayer->vehicle->executionState->GetState() != Entity::ExecutionStateEngine::NeverExecuteState)?1:0;


			m_playerData[i].m_position = app->servedConnectionData[i].clientPlayer->vehicle->GetLocalToWorld();
			YawPitchRange ypr;
			
			
			if (app->servedConnectionData[i].clientPlayer->vehicle->GetClassID() == MechClassID)
			{
				Mech *mech = Cast_Object(Mech*,app->servedConnectionData[i].clientPlayer->vehicle);

				Stuff::UnitVector3D vector;
				mech->animationArray[mech->torsoAboveJointIndex]->GetLocalToWorld().GetLocalForwardInWorld(&vector);

				ypr = vector;

				//ypr = app->servedConnectionData[i].clientPlayer->vehicle->GetLocalToWorld();
				m_playerData[i].m_yawRotation = ypr.yaw;
			}
			else
			{	

				Stuff::UnitVector3D vector;
				app->servedConnectionData[i].clientPlayer->vehicle->GetLocalToWorld().GetLocalForwardInWorld(&vector);

				ypr = vector;

				m_playerData[i].m_yawRotation = ypr.yaw;
			}
			
						
			CBucket *bucket = mission->m_BucketManager->PlayerBucket( app->servedConnectionData[i].clientPlayer->vehicle->GetReplicatorID(), CBucketManager::CUSTOM);
			if (bucket)
				m_playerData[i].m_scoreBucket1 = bucket->Value();
			else
				m_playerData[i].m_scoreBucket1 = 0;

			bucket = mission->m_BucketManager->PlayerBucket( app->servedConnectionData[i].clientPlayer->vehicle->GetReplicatorID(), CBucketManager::KILLS);
			if (bucket)
				m_playerData[i].m_scoreBucket2 = bucket->Value();
			else
				m_playerData[i].m_scoreBucket2 = 0;

			bucket = mission->m_BucketManager->PlayerBucket( app->servedConnectionData[i].clientPlayer->vehicle->GetReplicatorID(), CBucketManager::DEATHS);
			if (bucket)
				m_playerData[i].m_scoreBucket3 = bucket->Value();
			else
				m_playerData[i].m_scoreBucket3 = 0;


			// weapon and damage bools are pre fed...

			// read damage state...

			int current_damage_object = 0;


			if (app->servedConnectionData[i].clientPlayer->vehicle->IsDestroyed())
			{
				m_playerData[i].m_damageLevel[current_damage_object] = 1.0f;
			}
			else
			{
				m_playerData[i].m_damageLevel[current_damage_object] = 0.0f;
			}

			++current_damage_object;


			DamageObject *damage_object;
			Vehicle *vehicle = app->servedConnectionData[i].clientPlayer->GetVehicle();
			SortedChainIteratorOf<DamageObject *, int> damage_iterator(&vehicle->damageObjects);

			while((damage_object = damage_iterator.ReadAndNext()) != NULL)
			{
				Verify(current_damage_object < MRPlayerData::Damage_Zones);
				Check_Object(damage_object);
				m_playerData[i].m_damageLevel[current_damage_object] = damage_object->GetHighResDamageLevel();
				++current_damage_object;
			}

			// write the player into the log...

			Scalar min_z, max_z, min_x, max_x;
			Map::GetInstance()->GetMapExtents(&min_z, &max_z, &min_x, &max_x);

			m_missionLog->WriteScaledFloatToBits(m_playerData[i].m_position.x, min_x, max_x, XBitDepth);
			m_missionLog->WriteScaledFloatToBits(m_playerData[i].m_position.z, min_z, max_z, YBitDepth);
			m_missionLog->WriteScaledFloatToBits(m_playerData[i].m_yawRotation, -Stuff::Pi, Stuff::Pi, RotationBitDepth);
			m_missionLog->WriteBits(&m_playerData[i].m_scoreBucket1, 32);
			m_missionLog->WriteBits(&m_playerData[i].m_scoreBucket2, 32);
			m_missionLog->WriteBits(&m_playerData[i].m_scoreBucket3, 32);

			m_missionLog->WriteBit(m_playerData[i].m_takenDamage);
			m_missionLog->WriteBit(m_playerData[i].m_firedWeapon);
			m_missionLog->WriteBit(m_playerData[i].m_alive);
			m_playerData[i].m_takenDamage = false;
			m_playerData[i].m_firedWeapon = false;

			for (int dz = 0; dz < MRPlayerData::Damage_Zones; ++dz)
			{
				m_missionLog->WriteScaledFloatToBits(m_playerData[i].m_damageLevel[dz], 0.0f, 1.0f, DamageBitDepth);
			}

			m_missionLog->WriteByteAlign();
		}
	}
	
}

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

void MissionReview::SaveLog(const char *file_name)
{
	Verify(m_mode == RecordMode);
	
	Stuff::FileStream file_stream(file_name, Stuff::FileStream::WriteOnly);

	DWORD token = 0xAAAAAAAA;
	file_stream.WriteBytes(&token, 4);
	file_stream << missionName;
	file_stream << minExtents << maxExtents;

	file_stream.WriteBytes(&m_totalSnapShots, 4);
	file_stream.WriteBytes(&m_snapShotFrequency, 4);
	int size = m_nameLog->GetBufferBytesUsed();
	file_stream.WriteBytes(&size, 4);
	if (size > 0)
	{
		file_stream.WriteBytes(m_nameLog->GetPointer(), m_nameLog->GetBufferBytesUsed());
	}
	size = m_missionLog->GetBufferBytesUsed();
	file_stream.WriteBytes(&size, 4);
	if (size > 0)
	{
		file_stream.WriteBytes(m_missionLog->GetPointer(), m_missionLog->GetBufferBytesUsed());
	}
}


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

void MissionReview::GetHeader(DynamicMemoryStream *stream)
{
	
	DWORD token = 0xAAAAAAAA;
	stream->WriteBytes(&token, 4);
	*stream << missionName;
	*stream << minExtents << maxExtents;
	stream->WriteBytes(&m_totalSnapShots, 4);
	stream->WriteBytes(&m_snapShotFrequency, 4);
	int size = m_nameLog->GetBufferBytesUsed();
	stream->WriteBytes(&size, 4);
	if (size > 0)
	{
		stream->WriteBytes(m_nameLog->GetPointer(), m_nameLog->GetBufferBytesUsed());
	}
}


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

void MissionReview::GetDataLog(DynamicMemoryStream *stream)
{
	m_missionLog->Rewind();
	int size = m_missionLog->GetBufferBytesUsed();
	stream->WriteBytes(&size, 4);
	if (size > 0)
	{
		stream->WriteBytes(m_missionLog->GetPointer(), m_missionLog->GetBufferBytesUsed());

	}
}

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

bool MissionReview::HaveLog(void)
{
	return haveHeaderInfo;
}

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

void MissionReview::StartLogBuffer(void)
{
	ClearLog();
	m_missionLog = new DynamicMemoryStream;
	m_nameLog = new DynamicMemoryStream;

	m_mode = PlayBackMode;

	lastReadSnapShot = 0;
	lastReadPlayer = 0;
	lastByteRead = 0;
	totalLogSize = 0;
}


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

void MissionReview::LoadLog(const char *file_name)
{

	Verify(m_mode == PlayBackMode);
	
	Stuff::FileStream file_stream(file_name);


	if (ReadNames(&file_stream))
	{
		totalLogSize = 0;
		file_stream.ReadBytes(&totalLogSize, 4);

		//SPEW(("jerryeds", "SERVER TOTAL SIZE : %d", totalLogSize));
		//SPEW(("jerryeds", "TOTAL SNAPSHOTS %d", m_totalSnapShots));

		m_missionLog->WriteBytes(file_stream.GetPointer(), totalLogSize);
		m_missionLog->Rewind();

		// fill in the keyframe data...
		for (int j = 0; j < m_totalSnapShots; ++j)
		{
			for (int i = 0; i < Maximum_Players; ++i)
			{
				if (m_playBackLog[i][j].m_playerStatus == MRPlayerData::InToken)
				{
					Scalar min_x =  0.0f;
					Scalar max_x =  1.0f;
					Scalar min_z =  0.0f;
					Scalar max_z =  1.0f;

				

					m_missionLog->ReadBitsToScaledFloat(m_playBackLog[i][j].m_position.x, min_x, max_x, XBitDepth);
					m_missionLog->ReadBitsToScaledFloat(m_playBackLog[i][j].m_position.z, min_z, max_z, YBitDepth);
					m_missionLog->ReadBitsToScaledFloat(m_playBackLog[i][j].m_yawRotation, -Stuff::Pi, Stuff::Pi, RotationBitDepth);
					m_missionLog->ReadBits(&m_playBackLog[i][j].m_scoreBucket1, 32);
					m_missionLog->ReadBits(&m_playBackLog[i][j].m_scoreBucket2, 32);
					m_missionLog->ReadBits(&m_playBackLog[i][j].m_scoreBucket3, 32);

					m_missionLog->ReadBit(m_playBackLog[i][j].m_takenDamage);
					m_missionLog->ReadBit(m_playBackLog[i][j].m_firedWeapon);
					m_missionLog->ReadBit(m_playBackLog[i][j].m_alive);

					

//					SPEW (("afarrier","%d pos=%5.2f, %5.2f",j,m_playBackLog[i][j].m_position.x,m_playBackLog[i][j].m_position.z));
//					SPEW (("afarrier","%d yaw=%5.2f",j,m_playBackLog[i][j].m_yawRotation));

					for (int dz = 0; dz < MRPlayerData::Damage_Zones; ++dz)
					{
						m_missionLog->ReadBitsToScaledFloat(m_playBackLog[i][j].m_damageLevel[dz], 0.0f, 1.0f, DamageBitDepth);
					}

					m_missionLog->ReadByteAlign();

					



					//SPEW(("jerryeds", "%d:%d -%d:%d - %d -%d:%d - %d- %f %f : %f", i, j, index, bit, data,  index2, bit2, data2, m_playBackLog[i][j].m_position.x, m_playBackLog[i][j].m_position.y, m_playBackLog[i][j].m_yawRotation));

				}
			}
		}
	}
}


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

bool MissionReview::ReadNames(MemoryStream *stream)
{

	Verify(m_mode == PlayBackMode);

	if (stream->GetSize() > 20)
	{

		DWORD token;
		stream->ReadBytes(&token, 4);

		if (token == 0xAAAAAAAA)
		{
			haveHeaderInfo = true;

			*stream >> missionName;
			*stream >> minExtents >> maxExtents;

			stream->ReadBytes(&m_totalSnapShots, 4);
			m_bufferedSnapShots = m_totalSnapShots;
			stream->ReadBytes(&m_snapShotFrequency, 4);
			int size = 0;
			stream->ReadBytes(&size, 4);
			m_nameLog->AllocateBytes(size);

			if (size > 0)
			{
				stream->ReadBytes(m_nameLog->GetPointer(), size);
			}
		
		
			for (int i = 0; i < Maximum_Players; ++i)
			{
				m_playBackLog[i] = new MRPlayerData[m_totalSnapShots];
			}

			
			// Fill in the name log data...
			while(m_nameLog->GetBytesRemaining())
			{
				int token = 0;
				m_nameLog->ReadBytes(&token, 1);
				
				if (token == AddToken)
				{
					int snap_shot = 0;
					int id = 0;

					m_nameLog->ReadBytes(&snap_shot, 4);
					m_nameLog->ReadBytes(&id, 1);

					m_playBackLog[id][snap_shot].m_playerStatus = MRPlayerData::InToken;

					int size = 0;
					m_nameLog->ReadBytes(&size, 1);
					m_playBackLog[id][snap_shot].m_playerName = (char *)m_nameLog->GetPointer();
					m_nameLog->AdvancePointer(size);

				}
				else if (token == RemoveToken)
				{
					int snap_shot = 0;
					int id = 0;

					m_nameLog->ReadBytes(&snap_shot, 4);
					m_nameLog->ReadBytes(&id, 1);

					m_playBackLog[id][snap_shot].m_playerName = NULL;
					m_playBackLog[id][snap_shot].m_playerStatus = MRPlayerData::OutToken;

				}
				else
				{
					STOP(("MISSION REVIEW LOG CORRUPT"));
				}
			}

			//fill in the namelog gaps...
			for (i = 0; i < Maximum_Players; ++i)
			{
				for (int j = 1; j < m_totalSnapShots; ++j)
				{
					if (m_playBackLog[i][j].m_playerStatus == MRPlayerData::NoChange)
					{
						m_playBackLog[i][j].m_playerStatus = m_playBackLog[i][j-1].m_playerStatus;
						m_playBackLog[i][j].m_playerName = m_playBackLog[i][j-1].m_playerName;
					}
				}
			}

			return true;
		}
	}

	return false;
}



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

void MissionReview::AddPartToLogBuffer(MemoryStream *log_part_stream)
{
	Verify(m_mode == PlayBackMode);

	
	

	if (m_nameLog->GetBufferBytesUsed() == 0)
	{
		//SPEW(("jerryeds", "SNAPSHOT-START"));

		Verify(m_missionLog->GetBufferBytesUsed() == 0);

		//m_nameLog->WriteBytes(log_part_stream->GetPointer(), log_part_stream->GetBytesRemaining());
		//m_nameLog->Rewind();
		ReadNames(log_part_stream);

		
	}
	else
	{
		//SPEW(("jerryeds", "ADDPART : %d", log_part_stream->GetBufferBytesUsed()));

		Verify(m_nameLog->GetBufferBytesUsed() != 0);

		// 12 + 12 + 5 + 32 + 32 + 1 + 1 + (12 * 4 )

		
		// write the data

		m_missionLog->SetPointer(m_missionLog->GetBufferBytesUsed());
		m_missionLog->WriteBytes(log_part_stream->GetPointer(), log_part_stream->GetBufferBytesUsed());
		
		// restore our last position
		m_missionLog->SetPointer(lastByteRead);

		//SPEW(("jerryeds", "SET LASTBYTE : %d", lastByteRead));
		

		if (lastByteRead == 0)
		{
			totalLogSize = 0;
			
		
			if (m_missionLog->GetBytesRemaining() > 4)
			{
				m_missionLog->ReadBytes(&totalLogSize, 4);
				//SPEW(("jerryeds", "CLIENT TOTAL SIZE : %d", totalLogSize));
			}
			lastByteRead = 4;
		}

		//SPEW(("jerryeds", "REPORTED SIZE : %d, current size %d, packet size %d", totalLogSize, m_missionLog->GetBufferBytesUsed(), log_part_stream->GetBufferBytesUsed()));

		//if (m_missionLog->GetBufferBytesUsed() >= totalLogSize)
		{


			int bits_per_sample = XBitDepth + YBitDepth + RotationBitDepth + 32 + 32 + 32 + 1 + 1 + 1 + (DamageBitDepth * MRPlayerData::Damage_Zones);
			int bytes_per_sample;

			if (bits_per_sample & 7)
			{
				bytes_per_sample = bits_per_sample >> 3;
				++bytes_per_sample;
			}
			else
			{
				bytes_per_sample = bits_per_sample >> 3;
			}	

			for (int j = lastReadSnapShot; j < m_totalSnapShots; ++j)
			{
				for (int i = lastReadPlayer; i < Maximum_Players; ++i)
				{
					if (m_missionLog->GetBytesRemaining() >= bytes_per_sample)
					{

						if (m_playBackLog[i][j].m_playerStatus == MRPlayerData::InToken)
						{
							Scalar min_x =  0.0f;
							Scalar max_x =  1.0f;
							Scalar min_z =  0.0f;
							Scalar max_z =  1.0f;



							m_missionLog->ReadBitsToScaledFloat(m_playBackLog[i][j].m_position.x, min_x, max_x, XBitDepth);
							m_missionLog->ReadBitsToScaledFloat(m_playBackLog[i][j].m_position.z, min_z, max_z, YBitDepth);
							m_missionLog->ReadBitsToScaledFloat(m_playBackLog[i][j].m_yawRotation, -Stuff::Pi, Stuff::Pi, RotationBitDepth);
							m_missionLog->ReadBits(&m_playBackLog[i][j].m_scoreBucket1, 32);
							m_missionLog->ReadBits(&m_playBackLog[i][j].m_scoreBucket2, 32);
							m_missionLog->ReadBits(&m_playBackLog[i][j].m_scoreBucket3, 32);

							m_missionLog->ReadBit(m_playBackLog[i][j].m_takenDamage);
							m_missionLog->ReadBit(m_playBackLog[i][j].m_firedWeapon);
							m_missionLog->ReadBit(m_playBackLog[i][j].m_alive);

							for (int dz = 0; dz < MRPlayerData::Damage_Zones; ++dz)
							{
								m_missionLog->ReadBitsToScaledFloat(m_playBackLog[i][j].m_damageLevel[dz], 0.0f, 1.0f, DamageBitDepth);
							}


							


							m_missionLog->ReadByteAlign();

						
							//SPEW(("jerryeds", "%d:%d - %d:%d %d - %d:%d %d - %f %f : %f", i, j, index, bit, data ,index2, bit2, data2 ,m_playBackLog[i][j].m_position.x, m_playBackLog[i][j].m_position.y, m_playBackLog[i][j].m_yawRotation));



						}
					}
					else
					{
						// save where we are...
						lastReadSnapShot = j;
						lastReadPlayer = i;
						lastByteRead = m_missionLog->GetIndex();


						//SPEW(("jerryeds","LAST READ %d:%d - BYTE - %d", i, j, lastByteRead)); 


						// in case it evenly ends...
						if (totalLogSize == m_missionLog->GetBufferBytesUsed())
						{
							Verify(m_missionLog->GetBytesRemaining() == 0);
							m_bufferedSnapShots = m_totalSnapShots;

							//SPEW(("jerryeds","END LOG")); 

						}


						return;
					}
				}

				lastReadPlayer = 0;


				m_bufferedSnapShots = j;
				//SPEW(("jerryeds", "SNAPSHOT %d", m_bufferedSnapShots));
			}

		}
	}
	
	


}


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

char *MissionReview::GetMissionName()
{
	return missionName;
}

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

void MissionReview::GetMapExtents(Point3D &min_extents, Point3D &max_extents)
{
	min_extents = minExtents;
	max_extents = maxExtents;
}


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


void MissionReview::GetLogTimes(Scalar &buffered_time, Scalar &total_time, Scalar &sample_rate)
{
	Verify(m_mode == PlayBackMode);

	buffered_time = m_bufferedSnapShots * m_snapShotFrequency;
	total_time = m_totalSnapShots * m_snapShotFrequency;
	sample_rate = m_snapShotFrequency;
}

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

void MissionReview::GetPlayerData(MRPlayerData *new_data, Scalar time, int player)
{
	Verify(m_mode == PlayBackMode);

	new_data->Reset();
	
	int from_frame = 0;
	int to_frame = 0;
	Scalar lerp = 0.0f;
	
	
	if (time < 0.0f || time >= (Scalar)(m_bufferedSnapShots*m_snapShotFrequency))
	{
		return;
	}
	
	if (m_bufferedSnapShots == 0)
	{
		return;
	}

	from_frame = (int)(time / m_snapShotFrequency);
	to_frame = from_frame + 1;
	
	Clamp(from_frame, 0, m_bufferedSnapShots-1);
	Clamp(to_frame, 0, m_bufferedSnapShots-1);
	

	if (m_playBackLog[player][to_frame].m_playerStatus != MRPlayerData::InToken || to_frame == from_frame)
	{

		//SPEWALWAYS(("jerryeds", "PLAYER : %d : FRAME : %d", player, to_frame));

		new_data->m_playerStatus = m_playBackLog[player][to_frame].m_playerStatus;
		new_data->m_playerName = m_playBackLog[player][to_frame].m_playerName;
		new_data->m_scoreBucket1 = m_playBackLog[player][to_frame].m_scoreBucket1;
		new_data->m_scoreBucket2 = m_playBackLog[player][to_frame].m_scoreBucket2;
		new_data->m_scoreBucket3 = m_playBackLog[player][to_frame].m_scoreBucket3;

		new_data->m_takenDamage = m_playBackLog[player][to_frame].m_takenDamage;
		new_data->m_firedWeapon = m_playBackLog[player][to_frame].m_firedWeapon;
		new_data->m_alive = m_playBackLog[player][to_frame].m_alive;
		
		for (int i = 0; i < MRPlayerData::Damage_Zones; ++i)
		{
			new_data->m_damageLevel[i] = m_playBackLog[player][to_frame].m_damageLevel[i];
		}
		new_data->m_yawRotation = m_playBackLog[player][to_frame].m_yawRotation;
		new_data->m_position = m_playBackLog[player][to_frame].m_position;
		return;
	}
	
	new_data->m_playerStatus = m_playBackLog[player][from_frame].m_playerStatus;
	new_data->m_playerName = m_playBackLog[player][from_frame].m_playerName;
	new_data->m_scoreBucket1 = m_playBackLog[player][from_frame].m_scoreBucket1;
	new_data->m_scoreBucket2 = m_playBackLog[player][from_frame].m_scoreBucket2;
	new_data->m_scoreBucket3 = m_playBackLog[player][from_frame].m_scoreBucket3;
	
	new_data->m_takenDamage = m_playBackLog[player][from_frame].m_takenDamage;
	new_data->m_firedWeapon = m_playBackLog[player][from_frame].m_firedWeapon;
	new_data->m_alive = m_playBackLog[player][from_frame].m_alive;

	for (int i = 0; i < MRPlayerData::Damage_Zones; ++i)
	{
		new_data->m_damageLevel[i] = m_playBackLog[player][from_frame].m_damageLevel[i];
	}
	
	lerp = ( time - (from_frame*m_snapShotFrequency) ) / m_snapShotFrequency;

	Radian rotation;
	rotation.Lerp(m_playBackLog[player][from_frame].m_yawRotation, m_playBackLog[player][to_frame].m_yawRotation, lerp);
	new_data->m_yawRotation  = rotation.angle;
	new_data->m_position.Lerp(m_playBackLog[player][from_frame].m_position,  m_playBackLog[player][to_frame].m_position, lerp);
	
}
#endif // !defined(NO_MR)
