#include "precomp.h"

#ifdef PLS_DEBUG
#include "plog.h"



CCallLog::CCallLog(int size)
{
	m_bValid = false;

    DBG_SAVE_FILE_LINE
	m_pLogEntry = new CPacketLogEntry[size];
	if (m_pLogEntry == NULL)
	{
		WARNING_OUT(("CCallLog:Out of memory"));
		return;
	}

	m_currentIndex = 0;
	m_size = size;
	m_bValid = true;
}

CCallLog::~CCallLog()
{
	if (m_bValid)
	{
		delete [] m_pLogEntry;
	}
}



bool CCallLog::AddEntry(DWORD dwTimeStamp, DWORD dwSeqNum, LARGE_INTEGER LL_ArrivalTime, DWORD dwSize, bool fMark)
{
	CPacketLogEntry Entry;

	if (m_bValid == false) return false;

	Entry.dwTimeStamp = dwTimeStamp;
	Entry.dwSequenceNumber = dwSeqNum;
	Entry.LL_ArrivalTime = LL_ArrivalTime;
	Entry.dwSize = dwSize;
	Entry.dwLosses = 0;
	Entry.bLate = false;
	Entry.bMark = fMark;

	if (SizeCheck() == false)
		return false;

	m_pLogEntry[m_currentIndex++] = Entry;
	return true;

}

bool CCallLog::SizeCheck()
{
	if (m_bValid == false)
		return false;

	if (m_currentIndex < m_size)
		return true;
	return false;
}


bool CCallLog::Flush(HANDLE hFile)
{
	int index;
	CPacketLogEntry *pEntry;
	char szOutputLine[80];
	DWORD dwNumWritten;
   __int64 ArrivalTime64;
	__int64 PerfFreq64;
	LARGE_INTEGER LL_PerfFreq;
	
	if (m_bValid == false)
		return false;

	if (hFile == INVALID_HANDLE_VALUE)
		return false;

	PerformStats();

	// what's the performance frequency of this machine
	QueryPerformanceFrequency(&LL_PerfFreq);
	PerfFreq64 = LL_PerfFreq.QuadPart;

	for (index = 0; index < m_currentIndex; index++)
	{
		pEntry = &m_pLogEntry[index];

		ArrivalTime64 = pEntry->LL_ArrivalTime.QuadPart * 1000;
		ArrivalTime64 = ArrivalTime64 / PerfFreq64;

		sprintf(szOutputLine, "%d\t%d\t%d\t%d\t%d",
		        pEntry->dwTimeStamp,
		        pEntry->dwSequenceNumber,
		        (int)ArrivalTime64,
		        pEntry->dwSize,
		        pEntry->dwLosses);
		
		if (pEntry->bMark)
			strcat(szOutputLine, "\tMARK");
		else strcat(szOutputLine, "\t");

		if (pEntry->bLate)
			strcat(szOutputLine, "\tLATE");
		
		strcat(szOutputLine, "\n");

		WriteFile(hFile, szOutputLine, strlen(szOutputLine), &dwNumWritten, NULL);

	}
	return true;
}


CCallLog& CCallLog::operator=(const CCallLog& log)
{
	WARNING_OUT(("CCallLog:Assignment not supported"));
	return *this;
}

CCallLog::CCallLog(const CCallLog& log)
{
	WARNING_OUT(("CCallLog:Copy constructor not supported"));
	return;
}

bool CCallLog::PerformStats()
{
	int index, probeIndex;
	DWORD nextExpected;
	bool bFound;

	if (m_bValid == false)
		return false;

	if (m_currentIndex == 0)
		return false;

	// Set the current "loss" value for each entry to 0
	for (index = 0; index < m_currentIndex; index++)
	{
		m_pLogEntry[index].dwLosses = 0;
		m_pLogEntry[index].bLate = false;
	}

	// the first packet will always be a match with nextExpected
	nextExpected = m_pLogEntry[0].dwSequenceNumber;

	for (index = 0; index < m_currentIndex; index++)
	{
		if (nextExpected > 65535)
			nextExpected = 0;
		// if the packet has the next expected seq num, then everything is ok
		if (m_pLogEntry[index].dwSequenceNumber == nextExpected)
		{
			nextExpected++;
			continue;
		}

		// we've detected a missing packet... time to go hunting
		else if (m_pLogEntry[index].dwSequenceNumber > nextExpected)
		{
			bFound = false;
			probeIndex = index+1;
			while ((probeIndex < m_currentIndex) &&
			       (probeIndex < (index+PLOG_MAX_NOT_LATE)) )
			{
				if (m_pLogEntry[index].dwSequenceNumber == nextExpected)
					bFound = true;
				probeIndex++;
			}

			// the packet was merely out of sequence
			if (bFound == true)
			{
				nextExpected++;
				continue;
			}

			// the packet is probably lost
			// notice that we are simply incrementing dwLoss measure,
			// rolling the index back one
			m_pLogEntry[index].dwLosses++;
			nextExpected++;
			index--; // this is so we can continue to count additional
			         // loss packets for the same index
			continue; // back to the top of the for-loop
		}

		// a packet that was either previously thought of as lost or late
		else
		{
			m_pLogEntry[index].bLate = true;	
		}


	}


	return true;
}


// --------------------------------------------------------------

CPacketLog::CPacketLog(LPTSTR szDiskFile):
	m_hFile(INVALID_HANDLE_VALUE),
	m_nExtension(0)
{
	m_pCallLog = NULL;
	strcpy(m_szDiskFile, szDiskFile);
}


// initializes an output file.  Closes the current file if still open
bool CPacketLog::InitFile()
{
	char szFileName[120];

	if (m_hFile != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
	}

	sprintf(szFileName, "%s%d%s", m_szDiskFile, m_nExtension, PLOG_FILE_EXT);
	m_hFile = CreateFile(szFileName, 
									GENERIC_READ|GENERIC_WRITE, 
									0, NULL, OPEN_EXISTING,
									FILE_ATTRIBUTE_NORMAL, NULL);

	while ((m_hFile != INVALID_HANDLE_VALUE) && (m_nExtension < 100))
	{
		CloseHandle(m_hFile);
		sprintf(szFileName, "%s%d%s", m_szDiskFile, m_nExtension, PLOG_FILE_EXT);
		m_hFile = CreateFile(szFileName, 
									GENERIC_READ|GENERIC_WRITE, 
									0, NULL, OPEN_EXISTING,
									FILE_ATTRIBUTE_NORMAL, NULL);
		m_nExtension++;
	}
			

	m_hFile = CreateFile(szFileName,
	                     GENERIC_READ|GENERIC_WRITE, 
	                     FILE_SHARE_READ,
	                     NULL,
	                     CREATE_ALWAYS,
	                     FILE_ATTRIBUTE_NORMAL,
	                     NULL);
	                     
	                     
	if (m_hFile == INVALID_HANDLE_VALUE)
	{
		WARNING_OUT(("CPacketLog:Unable to initialize output file"));
		return false;
	}

	return true;

}


CPacketLog::CPacketLog(const CPacketLog& packet_log)
{
	// it wouldn't be an error if duphandle was used correctly
	ERROR_OUT(("CPacketLog:Invalid to create a new packetLog via a copy constructor"));
	return;
}



CPacketLog& CPacketLog::operator=(const CPacketLog& packet_log)
{
	// it wouldn't be an error if duphandle was used correctly
	ERROR_OUT(("CPacketLog:Invalid to create a new packetLog via the assignment operator"));
	return *this;	
}


CPacketLog::~CPacketLog()
{
	if (m_pCallLog != NULL)
	{
		Flush();
		delete (m_pCallLog);
	}

	if (m_hFile != INVALID_HANDLE_VALUE)
		CloseHandle(m_hFile);

}


bool CPacketLog::Flush()
{
	char *szCallHeader = "CALLSTART\n";
	DWORD dwNumWritten;

	if (m_pCallLog == NULL)
		return false;

	WriteFile(m_hFile, szCallHeader, strlen(szCallHeader), &dwNumWritten, NULL);
	m_pCallLog->Flush(m_hFile);		

	return true;
}



bool CPacketLog::MarkCallStart()
{
	// write the previous call to file
	if (m_pCallLog != NULL)
	{
		Flush();
		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
		delete m_pCallLog;
	}

	if (false == InitFile())
	{
		return false;
	}

    DBG_SAVE_FILE_LINE
	m_pCallLog = new CCallLog(PLOG_MAX_PACKETS_CALL);
	if (m_pCallLog == NULL)
		return false;

	return true;
}


bool CPacketLog::AddEntry(DWORD dwTimeStamp, DWORD dwSeqNum, LARGE_INTEGER LL_ArrivalTime, DWORD dwSize, bool fMark)
{
	if (m_pCallLog == NULL)
	{
		if (false == MarkCallStart())
			return false;
	}

	return (m_pCallLog->AddEntry(dwTimeStamp, dwSeqNum, LL_ArrivalTime, dwSize, fMark));

}


// if-def pls_debug
#endif