///////////////////////////
//	File: CSGD_MemoryManager.cpp
//	Author:	Eric Green
//	Purpose: Memory Manager function definitions

#include <memory.h>
#include <malloc.h>
#include <fstream>
using namespace std;


// Always make this the last file included. Normally you only include this file in .cpp's
#include "CSGD_MemoryManager.h"

#ifdef _DEBUG
//This protects our actual function defnintion from being 
// mangled by the #define new directive in the header.
#undef new

#endif

#define LOGFILE "SGD_MemoryLog.txt"

// initialize our static variable member.

CSGD_MemoryManager CSGD_MemoryManager::m_Instance;

CSGD_MemoryManager *CSGD_MemoryManager::GetInstance()
{
	return &m_Instance;
}

CSGD_MemoryManager::CSGD_MemoryManager()
{
	m_nCurUsedMem = m_nMaxUsedMem = 0;

	m_pMemoryHead = 0;

	ofstream fout(LOGFILE, ios_base::out | ios_base::trunc);
	fout.close();
}

CSGD_MemoryManager::~CSGD_MemoryManager()
{
	Flush();
}

#ifdef _DEBUG

void *operator new(size_t nBytes, int nLine, char *szFile)
{
	// create the node for the linked list first.
	CSGD_MemoryManager::tMemoryNode *pNode = 
		(CSGD_MemoryManager::tMemoryNode *)malloc(sizeof(CSGD_MemoryManager::tMemoryNode));

	//error check to make sure memory was allocated.
	if (!pNode) return 0;

	//get the memory that was asked for
	pNode->pMemory = malloc(nBytes);

	if(!pNode->pMemory)
	{
		free(pNode);
		return 0;
	}

	// set up the rest of the nodes info
	pNode->pNext = 0;
	pNode->nSize = nBytes;
	pNode->nLine = nLine;

	// find the file that this allocation took place in.
	// __FILE__ returns the full path :( 

	// find the length of the string
	int nLength = (int)strlen(szFile);

	//start from the back and read the string until you finr the first "\"

	int i = 0;
	for( i = nLength; i > (nLength-127) && i > 0; --i)
	{
		if (szFile[i] == '\\')
			break;
	}

	// copy the name over +1 so that we don't copy the '\\' +1 for the null terminator.
	memcpy(&pNode->szFile, &szFile[i+1], (nLength -i)+1);

	// get the MemoryManager singleton instance.
	CSGD_MemoryManager *pMM = CSGD_MemoryManager::GetInstance();

	pMM->m_nCurUsedMem += nBytes;
	pMM->m_nMaxUsedMem += nBytes;

	pNode->pNext			= pMM->m_pMemoryHead;
	pMM->m_pMemoryHead		= pNode;

	//return the memory that was asked for
	return pNode->pMemory;
}


void operator delete(void *pMem)
{
	// error check to make sure the memory can be deleted.
	if(!pMem) return;

	// free the memory
	free(pMem);

	// Variables needed.
	CSGD_MemoryManager *pMM = CSGD_MemoryManager::GetInstance();
	CSGD_MemoryManager::tMemoryNode *pCurrent	= pMM->m_pMemoryHead;
	CSGD_MemoryManager::tMemoryNode *pPrevious	= 0;

	while(pCurrent)
	{
		//compare memory addresses.
		if(pCurrent->pMemory == pMem)
			break;

		pPrevious = pCurrent;
		pCurrent = pCurrent->pNext;
	}

		//error check

		if(!pCurrent) return;

		pMM->m_nCurUsedMem -= pCurrent->nSize;

		//remove it from the list. if previous = 0 then the current is the head.
		if(!pPrevious)
			pMM->m_pMemoryHead = pCurrent->pNext;
		else
			pPrevious->pNext = pCurrent->pNext;

		// delete the nodes memory
		free(pCurrent);
}

void operator delete[](void *pMem)
{
	// error check to make sure the memory can be deleted.
	if(!pMem) return;

	// free the memory
	free(pMem);

	// Variables needed.
	CSGD_MemoryManager *pMM = CSGD_MemoryManager::GetInstance();
	CSGD_MemoryManager::tMemoryNode *pCurrent	= pMM->m_pMemoryHead;
	CSGD_MemoryManager::tMemoryNode *pPrevious	= 0;

	while(pCurrent)
	{
		//compare memory addresses.
		if(pCurrent->pMemory == pMem)
			break;

		pPrevious = pCurrent;
		pCurrent = pCurrent->pNext;
	}

		//error check

		if(!pCurrent) return;

		pMM->m_nCurUsedMem -= pCurrent->nSize;

		//remove it from the list. if previous = 0 then the current is the head.
		if(!pPrevious)
			pMM->m_pMemoryHead = pCurrent->pNext;
		else
			pPrevious->pNext = pCurrent->pNext;

		// delete the nodes memory
		free(pCurrent);
}
#endif // _DEBUG

void CSGD_MemoryManager::LogStatus()
{
	// lets open the file
	ofstream fout(LOGFILE, ios_base::out | ios_base::app);

	// print out a nice little header for our data.
	fout << "======================================\n"
		 << "Memory Usage Report " << __TIMESTAMP__ << '\n'
		 << "Max Used: " << m_nMaxUsedMem << "\tCurrent: "
		 << m_nCurUsedMem << '\n'
		 << "--------------------------------------\n"
		 << "Line\t | Size\t | File\n"
		 << "--------------------------------------\n";

	// is there anything to long
	if(!m_pMemoryHead)
	{
		fout << "No memory Lost\n\n";
		fout.close();
		return;
	}

	tMemoryNode *pCurrent = m_pMemoryHead;

	while(pCurrent)
	{
		//print out the info.
		fout << pCurrent->nLine << '\t' << pCurrent->nSize << '\t'
			<< pCurrent->szFile << '\n';

		//advance the pointer
		pCurrent = pCurrent->pNext;
	}

	fout << "\n\n";
	fout.close();
}

void CSGD_MemoryManager::Flush()
{
	tMemoryNode *pDelete = m_pMemoryHead;

	while (m_pMemoryHead)
	{
		m_pMemoryHead = m_pMemoryHead->pNext;

		m_nCurUsedMem -= pDelete->nSize;
		free(pDelete->pMemory);
		free(pDelete);

		//
		pDelete = m_pMemoryHead;
	}
}

void CSGD_MemoryManager::Shutdown()
{
	m_nMaxUsedMem = 0;
	Flush();
	LogStatus();
}

#ifdef _DEBUG
#define new new(__LINE__,__FILE__)
#endif
