#include "munga.h"
#pragma hdrstop

#include "spooler.h"
#include "filestrm.h"

//#############################################################################
//##############################   SpoolFile   ################################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SpoolFile::SpoolFile(
	void *stream_start,
	size_t stream_size,
	size_t initial_offset
):
	MemoryStream(stream_start, stream_size, initial_offset)
{
	spoolState = Empty;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SpoolFile::SpoolFile(SpoolFile& spool):
	MemoryStream(
		spool.streamStart,
		spool.streamSize,
		spool.GetBytesUsed()
	)
{
	spoolState = spool.spoolState;
	Verify(spoolState == Playing);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SpoolFile::~SpoolFile()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SpoolFile::SaveAs(const char *file_name)
{
	FileStream output(file_name, True);
	size_t length = GetBytesUsed();
	output << length;
	Rewind();
	output.WriteBytes(GetPointer(), length);
	output.Close();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SpoolFile::Read(const char *file_name)
{
	FileStream input(file_name);
	if (!input.IsFileOpened())
	{
		spoolState = Empty;
		return;
	}

	//
	//-------------------------------
	// Figure out how big the file is
	//-------------------------------
	//
	size_t spool_size = 0;
	input >> spool_size;
	if (spool_size > GetBytesRemaining() || !spool_size)
	{
		spoolState = Empty;
	}

	//
	//-----------
	// Read it in
	//-----------
	//
	else
	{
		input.ReadBytes(streamStart, spool_size);
		streamSize = spool_size;
		Rewind();
		spoolState = Stored;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SpoolFile::SpoolPacket(NetworkPacket *packet)
{
	Check(this);
	Check_Pointer(packet);

	//
	//-------------------------------------------------------------------------
	// Check to see if we are out of spool space.  For now, if it happens, just
	// die
	//-------------------------------------------------------------------------
	//
	int length =
		packet->messageData.messageLength + sizeof(NetworkPacketHeader);
	int remaining = GetBytesRemaining();
	if (remaining < length)
	{
		DEBUG_STREAM << "Error: Spool file ran out of memory!\n" << std::flush;
		PostQuitMessage(AbortExitCodeID);
	}

	//
	//-----------------------------------------------------
	// Copy into the memory stream, and advance the pointer
	//-----------------------------------------------------
	//
	Mem_Copy(GetPointer(), packet, length, remaining);
	AdvancePointer(length);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NetworkPacket*
	SpoolFile::NextPacket()
{
	Check(this);

	//
	//--------------------------------------------------------------------------
	// Check to see if anything is left in the spool.  If only a partial message
	// remains, die from an error
	//-------------------------------------------------------------------------
	//
	if (!GetBytesRemaining())
	{
		return NULL;
	}
	NetworkPacket *packet = (NetworkPacket*)GetPointer();
	int length =
		packet->messageData.messageLength + sizeof(NetworkPacketHeader);
	if (GetBytesRemaining() < length)
	{
		DEBUG_STREAM << "Error: Partial packet in spool file!\n" << std::flush;
		PostQuitMessage(AbortExitCodeID);
		return NULL;
	}

	//
	//-----------------------------------------------------
	// Copy into the memory stream, and advance the pointer
	//-----------------------------------------------------
	//
	AdvancePointer(length);
	return (NetworkPacket*)GetPointer();
}

//#############################################################################
//#################    MissionReviewApplicationManager    #####################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MissionReviewApplicationManager::MissionReviewApplicationManager(
	HINSTANCE hInstance,
	HWND hWnd,
	Scalar frame_rate,
	int spool_count,
	size_t spool_size
):
	ApplicationManager(hInstance, hWnd, frame_rate)
{
	//
	//--------------------------------------------------------
	// Allocate the space for the spool files and buffer table
	//--------------------------------------------------------
	//
	spoolCount = spool_count;
	spoolSize = spool_size;
	spoolBuffers = new char* [spoolCount];
	Register_Pointer(spoolBuffers);
	spoolFiles = new SpoolFile* [spoolCount];
	Register_Pointer(spoolFiles);

	//
	//--------------------------------------------------------
	// Allocate each spool buffer, and initial the spool files to none
	//--------------------------------------------------------
	//
	for (int i=0; i<spoolCount; ++i)
	{
		spoolBuffers[i] = new char[spoolSize];
		Register_Pointer(spoolBuffers[i]);
		spoolFiles[i] = new SpoolFile(spoolBuffers[i], spoolSize);
		Register_Object(spoolFiles[i]);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MissionReviewApplicationManager::~MissionReviewApplicationManager()
{
	for (int i=0; i<spoolCount; ++i)
	{
		if (spoolFiles[i])
		{
			Unregister_Object(spoolFiles[i]);
			delete spoolFiles[i];
		}
		Unregister_Pointer(spoolBuffers[i]);
		delete spoolBuffers[i];
	}
	Unregister_Pointer(spoolFiles);
	delete spoolFiles;
	Unregister_Pointer(spoolBuffers);
	delete spoolBuffers;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SpoolFile*
	MissionReviewApplicationManager::GetEmptySpoolFile()
{
	for (int i=0; i<spoolCount; ++i)
	{
		if (spoolFiles[i]->spoolState == SpoolFile::Empty)
		{
			spoolFiles[i]->spoolState = SpoolFile::Spooling;
			return spoolFiles[i];
		}
	}

	Fail("No spool files available!");
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SpoolFile*
	MissionReviewApplicationManager::GetStoredSpoolFile(CString spoolFileName)
{
	for (int i=0; i<spoolCount; ++i)
	{
		if (spoolFiles[i]->spoolState == SpoolFile::Empty)
		{
			spoolFiles[i]->Read(spoolFileName);
			if (spoolFiles[i]->spoolState == SpoolFile::Stored)
			{
				spoolFiles[i]->spoolState = SpoolFile::Playing;
				return spoolFiles[i];
			}
			return NULL;
		}
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MissionReviewApplicationManager::StoreSpoolFile(SpoolFile *spool_file)
{
	for (int i=0; i<spoolCount; ++i)
	{
		if (spoolFiles[i] == spool_file)
		{
			Verify(spoolFiles[i]->spoolState == SpoolFile::Spooling);
			spoolFiles[i]->spoolState = SpoolFile::Stored;
			
			struct tm newtime;
			__int64 ltime;

			// get local time
			_time64(&ltime);

			// get UTC time
			_gmtime64_s(&newtime, &ltime);

			char filename[2056];
			sprintf(filename, "SPOOLS\\%.4i_%.2i_%.2i_%.2i%.2i%.2i.spl", newtime.tm_year + 1900, newtime.tm_mon + 1, newtime.tm_mday, newtime.tm_hour, newtime.tm_min, newtime.tm_sec);

			spoolFiles[i]->SaveAs(filename);

			// now copy the new file over to last.spl
			CopyFileA(filename, "last.spl", FALSE);
			
			spoolFiles[i]->Rewind();
			return;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MissionReviewApplicationManager::ReleaseSpoolFile(SpoolFile *spool_file)
{
	for (int i=0; i<spoolCount; ++i)
	{
		if (spoolFiles[i] == spool_file)
		{
			Verify(spoolFiles[i]->spoolState != SpoolFile::Spooling);
			spoolFiles[i]->spoolState = SpoolFile::Empty;
			spoolFiles[i]->Rewind();
			return;
		}
	}
}
