// DSLog.cpp: implementation of the Log class.
//

#include "stdafx.h"
#include "DSLog.h"

const DWORD CDSLog::modeDebug   =  0x01;
const DWORD CDSLog::modeFile    =  0x02;
const DWORD CDSLog::modeConsole =  0x04;

const CHAR CDSLog::m_szExt[] = ".log";

const static int LINE_BUFFER_SIZE = 1024;

// Create a new dslog object.
//	nMode		- specifies where output should go, using combination
//					of flags above.
//	nFlag		- the flag
//	pszFileName	- if flag CDSLog::modeFile is specified in the type,
//					a filename must be specified here.
//	bAppend		- if logging to a file, whether or not to append to any
//					existing log.
//	bDaily		- if logging to a file, whether or not to replace log every day.

CDSLog::CDSLog(DWORD dwMode, DWORD dwLevel, LPSTR pszFileName, BOOL bAppend, BOOL bDaily)
{
	m_nLastLogTime    = 0;
	m_szLastLogDay[0] = 0;

	m_pszFileName = NULL;
	m_hFile       = NULL;

	m_pszEventSource = NULL;

	m_bDebugMode   = false;
	m_bFileMode    = false;
	m_bConsoleMode = false;
	m_bAppend      = false;
	m_bDaily       = false;

	m_dwLevel = dwLevel;

	SetFile(pszFileName, bAppend, bDaily);
	SetMode(dwMode);
}

CDSLog::~CDSLog()
{
	if (m_pszFileName)
		free(m_pszFileName);

	if (m_pszEventSource)
		free(m_pszEventSource);

    CloseFile();
}

void CDSLog::EventLog(WORD wType, LPSTR pszFormat, ...)
{
	va_list val;

	va_start(val, pszFormat);

	if (m_pszEventSource) {
		HANDLE hEventSource;

		CHAR  szBuf[256];
		LPSTR ppszBuf[2];

		vsprintf(szBuf, pszFormat, val);

		hEventSource = RegisterEventSource(NULL, m_pszEventSource);

		ppszBuf[0] = m_pszEventSource;
		ppszBuf[1] = szBuf;

		if (hEventSource) {
			ReportEvent(
				hEventSource,			// handle of event source
				wType,					// event type
				0,						// event category
				0,						// event ID
				NULL,					// current user's SID
				2,						// strings in 'strings'
				0,						// no bytes of raw data
				(const char **)ppszBuf,	// array of error strings
				NULL);					// no raw data

			DeregisterEventSource(hEventSource);
		}
	}

	if (DSLL_STATE & m_dwLevel)
		_Print(pszFormat, val);

	va_end(val);
}

void CDSLog::EventLogError(LPSTR pszMsg)
{
	DWORD dwError = GetLastError();
	TCHAR szBuf[256];

	wsprintf(szBuf, "%s, error code : %d", pszMsg, dwError);
	EventLog(EVENTLOG_ERROR_TYPE, szBuf);
}

void CDSLog::EventLogWSAError(LPSTR pszMsg)
{
//	DWORD dwError = WSAGetLastError();
//	TCHAR szBuf[256];

//	wsprintf(szBuf, "%s, WSA error code : %d", pszMsg, dwError);
//	EventLog(EVENTLOG_ERROR_TYPE, szBuf);
}

void CDSLog::SetMode(DWORD dwMode)
{
	m_bDebugMode = dwMode & modeDebug;

	if (dwMode & modeFile) {
		if (!m_bFileMode)
			OpenFile();
	}
	else {
		CloseFile();
		m_bFileMode = false;
	}

	if (dwMode & modeConsole) {
		if (!m_bConsoleMode)
			AllocConsole();

		m_bConsoleMode = true;
	}
	else {
		m_bConsoleMode = false;
	}
}

void CDSLog::SetLevel(DWORD dwLevel)
 {
    m_dwLevel = dwLevel;
}

void CDSLog::SetFile(LPSTR pszFileName, BOOL bAppend, BOOL bDaily) 
{
	CloseFile();

	if (m_pszFileName)
		free(m_pszFileName);

	if (pszFileName)
		m_pszFileName = strdup(pszFileName);

	m_bAppend = bAppend;
	m_bDaily  = bDaily;

	if (m_bFileMode)
		OpenFile();
}

void CDSLog::SetEventSource(LPSTR pszEventSource)
{
	if (m_pszEventSource)
		free(m_pszEventSource);

	if (pszEventSource)
		m_pszEventSource = strdup(pszEventSource);
}

void CDSLog::_Print(LPSTR pszFormat, va_list val) 
{
	UINT64 nCurrentTime = _time64(0);

	if (m_bFileMode && m_bDaily && m_hFile) {
		CHAR szDate[7];

		MakeSurfix(szDate);

		if (szDate[6] != m_szLastLogDay[6] || szDate[5] != m_szLastLogDay[5]) {
			CloseFile();
			OpenFile();
		}
	}

	if (nCurrentTime != m_nLastLogTime) {
		m_nLastLogTime = nCurrentTime;
		_PrintLine(_ctime64((__time64_t*)&m_nLastLogTime));
	}

	// - Write the log message
	TCHAR szLine[LINE_BUFFER_SIZE];

	vsprintf(szLine, pszFormat, val);
	_PrintLine(szLine);
}

inline void CDSLog::_PrintLine(LPSTR pszLine) 
{
	if (m_bDebugMode)
		OutputDebugString(pszLine);

	DWORD dwWritten;

	if (m_bConsoleMode)
		WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), pszLine, (DWORD)strlen(pszLine), &dwWritten, NULL);

	if (m_bFileMode && m_hFile)
		WriteFile(m_hFile, pszLine, (DWORD)strlen(pszLine), &dwWritten, NULL);
}

void CDSLog::OpenFile()
{
	if (!m_pszFileName) {
		m_bDebugMode = true;
		m_bFileMode  = false;

		Print(0, "Error opening log file\n");
		return;
	}

	char szFileName[MAX_PATH];

	strcpy(szFileName, m_pszFileName);

	if (m_bDaily) {
		CHAR szDate[7];

		MakeSurfix(szDate);

		strcat(szFileName, szDate);

		strcpy(m_szLastLogDay, szDate);
	}

	strcat(szFileName, m_szExt);

	m_bFileMode = true;

	m_hFile = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (m_hFile == INVALID_HANDLE_VALUE) {
		m_bDebugMode = true;
		m_bFileMode  = false;

		Print(0, "Error opening log file %s\n", szFileName);
		return;
	}

	if (m_bAppend)
		SetFilePointer(m_hFile, 0, NULL, FILE_END);
	else 
		SetEndOfFile(m_hFile);
}

void CDSLog::CloseFile()
{
	if (m_hFile) {
		CloseHandle(m_hFile);
		m_hFile = NULL;
	}
}

void CDSLog::MakeSurfix(CHAR *pszBuf)
{
	WORD wMonth, wDay, wYear;

	SYSTEMTIME st;
	GetLocalTime(&st);

	wYear  = st.wYear % 100;
	wMonth = st.wMonth;
	wDay   = st.wDay;

	pszBuf[0] = (CHAR)(wYear  / 10 + '0');
	pszBuf[1] = (CHAR)(wYear  % 10 + '0');

	pszBuf[2] = (CHAR)(wMonth / 10 + '0');
	pszBuf[3] = (CHAR)(wMonth % 10 + '0');

	pszBuf[4] = (CHAR)(wDay   / 10 + '0');
	pszBuf[5] = (CHAR)(wDay   % 10 + '0');

	pszBuf[6] = 0;
}
