// Log.h: interface for the CLog class.
//
//////////////////////////////////////////////////////////////////////
/*
��־��CLog
��������������־����࣬ʹ��ʱ���Զ����죬����ΪҪ��֤��־�����һ����������Ҫ�ֶ��ͷ�
���ܣ�д��־�����TRACE���ݵ�dbgview,console��
ʹ�ã�
	ʹ�ú���
		static void SetOutputLogFileName(LPCTSTR szFileName);
		static void SetOutputConsole(BOOL bFlag);
		static void SetOutputDbgView(BOOL bFlag);
		static void SetOutputLogFile(BOOL bFlag);
		�����������־�ķ�ʽ
	ʹ��TRACE��ʹ�ú���static void WriteLog(const char* format, ...);д��־���ɽ��ܿɱ����
�ͷţ��ڳ����˳�ʱ��ExitInstance()��������󣩵���CLog::Release()�ͷ����ü���
*/
#if !defined(AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_)
#define AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

//#include "dbghlpapi.h"

#ifdef TRACE
#undef TRACE
#define TRACE CLog::WriteLog
#endif

/*
#define TRACEFUNCNAME \
	CString str = _T("");\
	INIT_SYM(TRUE);\
	str.Format("File %s, line %d, funcname %s\n", __FILE__, __LINE__, __FUNCNAME__);\
	OutputDebugString(str);\
	UNINIT_SYM();\
*/
#define MAX_OUTPUT_LEN 4096
#define MAX_FILE_LEN 1024 * 1024 * 10
static TCHAR g_szFileName[1024] = {0};

class CLog
{
private:	
	double exe_time; 
	LARGE_INTEGER freq; 
	LARGE_INTEGER start_t, stop_t; 
	BOOL m_bOutputLogFile;
	BOOL m_bOutputDbgView;
	BOOL m_bOutputConsole;
	BOOL m_bConsoleOpened, m_bDbgviewOpened, m_bLogFileOpened;
	BOOL m_bRunning;
	FILE *m_pLogFile;
	PROCESS_INFORMATION pi;
	static CRITICAL_SECTION m_cs;
	static CLock m_lockForSingleton;
	static char m_ansiBuf[MAX_OUTPUT_LEN];
public:
	static CString GetPrivateLogPath(){
		static CString strPrivateMapFolder = _T("");
		static BOOL b = TRUE;
		if(b){
			strPrivateMapFolder.Format(_T("%s\\Log"), GetModuleFilePath());
			b = FALSE;
		}
		return strPrivateMapFolder;
	}

	static CLog* GetInstance(){
		m_lockForSingleton.Lock();
		static CLog log;
		m_lockForSingleton.UnLock();
		return &log;
	}

	~CLog(){
		m_bRunning = FALSE;
		//WaitTillThreadExited(m_handle);
		if(m_bConsoleOpened)
			OpenConsole(FALSE);
		if(m_bDbgviewOpened)
			OpenDbgview(FALSE);
		if(m_bLogFileOpened)
			CloseLogFile();	
		DeleteCriticalSection(&m_cs);
#ifdef _DEBUG
		TCHAR buf[1024], out[1024];
		wsprintf(buf, _T("%s\n"), _T("CLog::~CLog() destruction"));
		FormatBuf(buf, out, 1024);
		OutputDebugString(out);
#endif
	}

	static void SetOutputLogFileName(LPCTSTR szLastName){
		lstrcpy(g_szFileName, szLastName);
	}

	static void SetOutputConsole(BOOL bFlag){
		CLog* plog = CLog::GetInstance();
		plog->m_bOutputConsole = bFlag;
		plog->OpenConsole(bFlag);
	}

	static void SetOutputDbgView(BOOL bFlag){
		CLog* plog = CLog::GetInstance();
		plog->m_bOutputDbgView = bFlag;
		//plog->OpenDbgview(bFlag);
	}

	static void SetOutputLogFile(BOOL bFlag){
		CLog* plog = CLog::GetInstance();
		plog->m_bOutputLogFile = bFlag;
		if(plog->m_bOutputLogFile && lstrlen(g_szFileName) == 0)
			plog->CreateFileName();
	}

	static void WriteLog(const TCHAR* format, ...)	{
		try{
			CLog* plog = CLog::GetInstance();
			if(plog == NULL)
				return;
			CLocalLock lock(&m_cs);
			if(plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole)			{
				static TCHAR buf[MAX_OUTPUT_LEN], output[MAX_OUTPUT_LEN], *p;
				p = buf;
				va_list args;
				va_start(args, format);
				p += _vsntprintf(p, sizeof (buf) - 1, format, args);
				va_end(args);
				while ( (p > buf) && isspace(*(p-1)) )
					*--p = _T('\0');
				*p++ = _T('\r');
				*p++ = _T('\n');
				*p = _T('\0');
				WORD out_len = plog->FormatBuf(buf, output);
				plog->Output(output, out_len);
			}
		}
		catch(...){
			AfxMessageBox(_T("CLog::WriteLog"));
			ASSERT(0);
		}
	}

	static void WriteLogW(const wchar_t* format, ...)	{
		try{
			CLog* plog = CLog::GetInstance();
			if(plog == NULL)
				return;
			CLocalLock lock(&m_cs);
			if(plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole)			{
				static wchar_t buf[MAX_OUTPUT_LEN], *p;
				p = buf;
				va_list args;
				va_start(args, format);
				p += _vsnwprintf(p, sizeof (buf) - 1, format, args);
				va_end(args);
				while ( (p > buf) && iswspace(*(p-1)) )
					*--p = '\0';
				*p++ = '\r';
				*p++ = '\n';
				*p = '\0';
#if defined(_UNICODE) || defined(UNICODE)
				wchar_t output[MAX_OUTPUT_LEN];
				WORD out_len = plog->FormatBuf(buf, output);
				plog->Output(output, out_len);
#else
				char *aBuf = UnicodeToAnsi(buf);
				char output[MAX_OUTPUT_LEN];
				WORD out_len = plog->FormatBuf(aBuf, output);
				plog->Output(output, out_len);
				SAFEDELETEARR(aBuf);
#endif
			}
		}
		catch(...){
			AfxMessageBox(_T("CLog::WriteLog"));
			ASSERT(0);
		}
	}

	static void WriteLogA(const char* format, ...)	{
		try{
			CLog* plog = CLog::GetInstance();
			if(plog == NULL)
				return;
			CLocalLock lock(&m_cs);
			if(plog->m_bOutputLogFile || plog->m_bOutputDbgView || plog->m_bOutputConsole)			{
				static char buf[MAX_OUTPUT_LEN], *p;
				p = buf;
				va_list args;
				va_start(args, format);
				p += _vsnprintf(p, sizeof (buf) - 1, format, args);
				va_end(args);
				while ( (p > buf) && isspace(*(p-1)) )
					*--p = '\0';
				*p++ = '\r';
				*p++ = '\n';
				*p = '\0';
#if defined(_UNICODE) || defined(UNICODE)
				wchar_t *wBuf = AnsiToUtf16(buf);
				wchar_t output[MAX_OUTPUT_LEN];
				WORD out_len = plog->FormatBuf(wBuf, output);
				plog->Output(output, out_len);
				SAFEDELETEARR(wBuf);
#else
				char output[MAX_OUTPUT_LEN];
				WORD out_len = plog->FormatBuf(buf, output);
				plog->Output(output, out_len);
#endif
			}
		}
		catch(...){
			AfxMessageBox(_T("CLog::WriteLog"));
			ASSERT(0);
		}
	}

protected:
	CLog(): m_pLogFile(NULL), m_bOutputLogFile(FALSE), m_bOutputDbgView(FALSE), 
		m_bOutputConsole(FALSE), m_bConsoleOpened(FALSE), m_bDbgviewOpened(FALSE), 
		m_bLogFileOpened(FALSE), m_bRunning(TRUE){	
		QueryPerformanceFrequency(&freq); 
		//OutputDebugString(_T("The frequency of your pc is 0x%X.\n"), freq.QuadPart); 
		QueryPerformanceCounter(&start_t); 
		memset(g_szFileName, 0, sizeof(g_szFileName));
		TCHAR out[128] = {0};
		wsprintf(out, _T("CLog construction addr: %p\n"), this);
		OutputDebugString(out);
		memset(&pi, 0, sizeof(pi));
		InitializeCriticalSection(&m_cs);
	}

	LPCTSTR GetAppRunTime()	{
		static TCHAR szTime[128];
		memset(szTime, 0, sizeof szTime);

		QueryPerformanceCounter(&stop_t);
		exe_time = 1e3*(stop_t.QuadPart-start_t.QuadPart)/freq.QuadPart;
		WORD day, hour, min, sec;

		sec		= static_cast<unsigned short>(static_cast<unsigned long>(exe_time / 1000) % 60);
		min		= static_cast<unsigned short>(static_cast<unsigned long>(exe_time / 1000 / 60) % 60);
		hour	= static_cast<unsigned short>((exe_time / 1000 / 60) / 60);
		day		= static_cast<unsigned short>(exe_time / 1000.0 / 60.0 / 60.0 / 24);

		double ms = exe_time - (int)(exe_time) + ((int)(exe_time) % 1000);
		wsprintf(szTime, _T("%dday%02d:%02d:%02ds.%3.6fms"), day, hour, min, sec, ms);
		return szTime;
	}

	void Output(const TCHAR *out, WORD out_len)	{
		if(m_bOutputLogFile){
			OutputFile(out, out_len);
		}
#ifdef _DEBUG
		OutputDebugString(out);
#else
		if(m_bOutputDbgView){
			OutputDebugString(out);
		}
#endif
		if(m_bOutputConsole){
			_tprintf(out);
		}
	}

	WORD FormatBuf(const TCHAR *oldBuf, TCHAR *newBuf, WORD max_new_buff_len = MAX_OUTPUT_LEN){
		static SYSTEMTIME st;
		static TCHAR sztime[128];
		memset(sztime, 0, sizeof sztime);
		GetLocalTime(&st);
		wsprintf(sztime, _T("HB %04d-%02d-%02d-w%d %02d:%02d:%02d.%03d ---- "), 
			st.wYear, st.wMonth, st.wDay, (st.wDayOfWeek != 0) ? st.wDayOfWeek : 7, 
			st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
		lstrcpy(newBuf, sztime);
		if(lstrlen(sztime) + lstrlen(oldBuf) < max_new_buff_len){
			lstrcat(newBuf, oldBuf);
		}
		return static_cast<unsigned short>(lstrlen(newBuf));
	}

	void OutputFile(const TCHAR *buf, WORD buf_len){
		if(m_pLogFile == NULL)
			m_pLogFile = OpenLogFile();
		if(m_pLogFile != NULL){
			fseek(m_pLogFile, 0, SEEK_END);
			long filelen = ftell(m_pLogFile);
			if(filelen >= MAX_FILE_LEN){
				fclose(m_pLogFile);
				CreateFileName();
				m_pLogFile = OpenLogFile();
			}
		}
		if(m_pLogFile != NULL){
			fseek(m_pLogFile, 0, SEEK_END);
#if defined(_UNICODE) || defined(UNICODE)
			if(UnicodeToAnsiUseCharArray(buf, m_ansiBuf, MAX_OUTPUT_LEN)){
				fwrite(m_ansiBuf, 1, strlen(m_ansiBuf), m_pLogFile);
			}else{
				char *ansiOut = UnicodeToAnsi(buf);
				fwrite(ansiOut, 1, strlen(ansiOut), m_pLogFile);
				SAFEDELETEARR(ansiOut);
			}
#else
			fwrite(buf, 1, buf_len, m_pLogFile);
#endif
			fflush(m_pLogFile);
		}
	}

	BOOL OpenDbgview(BOOL bOpen){
		static STARTUPINFO si;
		static LPCTSTR lpszAppName = _T("dbgview.exe");
		if(bOpen){
			if(!m_bDbgviewOpened){
				memset(&si,0,sizeof(STARTUPINFO));
				si.cb = sizeof(STARTUPINFO);
				si.dwFlags = STARTF_USESHOWWINDOW;
				si.wShowWindow = SW_SHOW;
				if(CreateProcess(lpszAppName,NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))				{
					m_bDbgviewOpened = TRUE;
				}
			}
		}else if(m_bDbgviewOpened){
			CloseHandle(pi.hThread);
			CloseHandle(pi.hProcess);
			m_bDbgviewOpened = FALSE;
		}

		return bOpen ? m_bDbgviewOpened : !m_bDbgviewOpened;
	}

	FILE* OpenLogFile(){
		FILE *pfile = NULL;
		pfile = _tfopen(g_szFileName, _T("r"));
		if(pfile == NULL){
			pfile = _tfopen(g_szFileName, _T("wb"));
			if(pfile == NULL){
				AfxMessageBox(_T("Create log file failed."));
				return NULL;
			}
		}else{
			fclose(pfile);
			pfile = _tfopen(g_szFileName, _T("ab"));
			if(pfile == NULL){
				AfxMessageBox(_T("Open log file failed."));
				return NULL;
			}
		}
		if(pfile != NULL)
			m_bLogFileOpened = TRUE;
		return pfile;
	}

	void CreateFileName(){
		SYSTEMTIME st;
		GetLocalTime(&st);
		CString path = GetPrivateLogPath();
		CreateDirectory(path, NULL);
		wsprintf(g_szFileName, _T("%s\\%04d-%02d-%02d-w%d-%02d-%02d-%02d"),	path, 
			st.wYear, st.wMonth, st.wDay, (st.wDayOfWeek != 0) ? st.wDayOfWeek : 7, 
			st.wHour, st.wMinute, st.wSecond);
		lstrcat(g_szFileName, _T(".log"));
	}

	void CloseLogFile(){
		if(m_pLogFile != NULL){
			fflush(m_pLogFile);
			fclose(m_pLogFile);
			m_pLogFile = NULL;
		}
	}

	BOOL OpenConsole(BOOL bOpen){
		static FILE *pConsole = NULL;
		if(bOpen){
			if(!m_bConsoleOpened){
				if(AllocConsole()){
					SetConsoleTitle(_T("output"));
					/*COORD coord;
					coord.X = 120;
					coord.Y = 60;
					HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
					FillConsoleOutputAttribute(hStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
						120, coord, NULL);
					SetConsoleScreenBufferSize(hStdOut, coord);
					SMALL_RECT se;
					se.Left = 0;
					se.Top = 0;
					se.Right = 100;
					se.Bottom = 100;
					SetConsoleWindowInfo(hStdOut, FALSE, &se);
					*/
					pConsole = _tfreopen(_T("CONOUT$"), _T("w"), stdout);
					m_bConsoleOpened = TRUE;
				}
			}
			return m_bConsoleOpened;
		}else{
			if(m_bConsoleOpened){
				fclose(pConsole);
				if(FreeConsole()){				
					m_bConsoleOpened = FALSE;
				}
			}
			return !m_bConsoleOpened;
		}
	}

	LPCTSTR GetLastTimeLogFileName(){
		return g_szFileName;
	}
};

CRITICAL_SECTION CLog::m_cs;
//int CLog::m_nRef = 0;
char CLog::m_ansiBuf[MAX_OUTPUT_LEN] = {0};
CLock CLog::m_lockForSingleton;

#endif // !defined(AFX_ERRORLOG_H__46D664F1_E737_46B7_9813_2EF1415FF884__INCLUDED_)