/******************************************************************************
fgdump - by fizzgig and the foofus.net group
Copyright (C) 2006 by fizzgig
http://www.foofus.net

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
******************************************************************************/
#include "stdafx.h"
#include "LogWriter.h"
#include ".\logwriter.h"

LogWriter::LogWriter()
{
	bEnableFileWrite = false;
	memset(lpszFile, 0, MAX_PATH + 1);
	nVerbosity = 0;
	InitializeCriticalSection(&csLogWrite);
	nCacheID = 0;	// Starting identifier
}

LogWriter::~LogWriter()
{
	StringArray* pArray;
	hash_map <LONG, StringArray*>::iterator pIter;

	// Delete the heap-allocated structures
	for (pIter = map.begin(); pIter != map.end(); pIter++)
	{
		pArray = pIter->second;
		delete pArray;
	}

	// Clean out the map
	map.clear();

	DeleteCriticalSection(&csLogWrite);
}

void LogWriter::SetLogFile(char* szLogFile)
{
	strncpy(lpszFile, (const char*)szLogFile, MAX_PATH);
	
	// Write the start time to the log
	FILE* hFile = fopen(lpszFile, "a");
	SYSTEMTIME st;

	if (hFile == NULL)
	{
		fprintf(stdout, "Error opening output log file %s, disabling further log writing. Error code returned was %d\n", lpszFile, GetLastError());
		bEnableFileWrite = false;
		return;
	}
	GetLocalTime(&st);
	fprintf(hFile, "\n---fgdump session started on %d/%d/%d at %0.2d:%0.2d:%0.2d  ---\n\n", st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond);
	fclose(hFile);

}

void LogWriter::SetWriteToFile(bool bWriteToFile)
{
	bEnableFileWrite = bWriteToFile;
}

void LogWriter::IncreaseVerbosity()
{
	nVerbosity++;
}

void LogWriter::ReportError(ERROR_LEVEL eLevel, char *pMsg, ...) 
{
	// Writes to the log must be synchronized
	EnterCriticalSection(&csLogWrite);

	va_list ap;
	char* buf;
  
	if (pMsg == NULL) 
	{
		LeaveCriticalSection(&csLogWrite);
		return;
	}
	
	if (eLevel <= nVerbosity)
	{
		va_start(ap, pMsg);
		size_t nLen = _vscprintf(pMsg, ap);
		buf = (char*)malloc(nLen + 1);
		memset(buf, 0, nLen + 1);
		_vsnprintf(buf, nLen, pMsg, ap);

		fprintf(stdout, "%s", buf);
		if (bEnableFileWrite)
		{
			// Also print to file
			FILE* hFile = fopen(lpszFile, "a");
			if (hFile == NULL)
			{
				fprintf(stdout, "Error opening output log file %s, disabling further log writing. Error code returned was %d\n", lpszFile, GetLastError());
				bEnableFileWrite = false;
				LeaveCriticalSection(&csLogWrite);
				return;
			}
			fprintf(hFile, "%s", buf);
			fclose(hFile);
		}
		va_end(ap);
		free(buf);
	}
  
	LeaveCriticalSection(&csLogWrite);
	return;
}


int LogWriter::BeginCachedWrite(void)
{
	// Called by a worker thread to get an identifier to perform cached log writes
	// This prevents data from getting muddled with each other. When a thread is done,
	// it calls the FlushCachedWrite function to dump all of its data at once.

	// Give the caller an ID to use when writing cached output
	// Must be a thread safe increment operation here
	InterlockedIncrement(&nCacheID);

	// Allocate a new string array
	StringArray* pNewArray = new StringArray();
	map[nCacheID] = pNewArray;
	
	return nCacheID;
}

void LogWriter::FlushCachedWrite(int nCacheID)
{
	// Writes all pending data to the log
	// Must be synchronized along with calls to ReportError
	StringArray* pArray;

	EnterCriticalSection(&csLogWrite);
	pArray = map[nCacheID];
	char* szTemp = pArray->GetFirstString();
	if (szTemp != NULL)
	{
		while (szTemp != NULL)
		{
			// These are reported at a "critical" level, since they were filtered within the CachedReportError function
			Log.ReportError(CRITICAL, "%s", szTemp);
			szTemp = pArray->GetNextString();
		}
	}

	LeaveCriticalSection(&csLogWrite);
}

void LogWriter::CachedReportError(int nCacheID, ERROR_LEVEL eLevel, char *pMsg, ...) 
{
	// Stores up data to write, which will be actually written out when FlushCachedWrite
	// is called with the same identifier.
	va_list ap;
	char* buf;
 	StringArray* pArray;
 
	if (pMsg == NULL) 
	{
		return;
	}

	if (eLevel <= nVerbosity)
	{
		va_start(ap, pMsg);

		if (nCacheID == -1)
		{
			ReportError(eLevel, pMsg, ap);	// Lets us easily used normal writing in case of a bad cache ID
		}
		else
		{
			size_t nLen = _vscprintf(pMsg, ap);
			buf = (char*)malloc(nLen + 1);
			memset(buf, 0, nLen + 1);
			_vsnprintf(buf, nLen, pMsg, ap);

			// Add buf to string array
			pArray = map[nCacheID];
			pArray->Add(buf);
			free(buf);
		}

		va_end(ap);
	}

}