/********************************************************************************
*		ソースファイル名：Log.cpp												*
*		用            途：ログ出力クラス										*
*		備            考：														*
*		履            歴：														*
********************************************************************************/
/****************************
*	 プリコンパイル設定		*
****************************/
#include "stdafx.h"
/****************************
*   コンパイル環境定数	    *
****************************/
#pragma warning(push)
#pragma warning(disable:4091)
/****************************
*   インクルードファイル    *
****************************/
#include <share.h>
#include "Log.h"
#include "System.h"
#include "Tools.h"
#include <imagehlp.h>
/****************************
*        マクロ定義			*
****************************/
#define	DEFAULT_BUF_SIZE	(1024 * 1024)
/****************************
*        構造体定義		    *
****************************/
/****************************
*        クラス定義		    *
****************************/
/****************************
*         関数 宣言		    *
****************************/
/****************************
*		　変数　宣言	    *
****************************/
/********************************************************************************
*	関数名：CLog																*
*	用　途：デフォルトコンストラクタ											*
*	引き数：なし																*
*	戻り値：なし																*
*	備　考：																	*
********************************************************************************/
CLog::CLog()
{
	m_WriteBuf.SetAllocKind(CMEM_ALLOC_HEAP);
	m_WriteBuf.Alloc(DEFAULT_BUF_SIZE);

	ZeroMemory(m_szPath, sizeof m_szPath);
	m_bEnable		= FALSE;
	m_bPrintDate	= TRUE;
	m_bAutoReturn	= TRUE;
	m_uDeleteSize	= 0;
	m_bFileBackup	= FALSE;
	m_nLevel		= 0;
	m_nMethod		= LOG_LEVEL_QUANTITY;
	m_nShareMode	= 0;
}

/********************************************************************************
*	関数名：CLog																*
*	用　途：コンストラクタ														*
*	引き数：ファイル名、新規作成フラグ、排他モード								*
*	戻り値：なし																*
*	備　考：																	*
********************************************************************************/
CLog::CLog(TCHAR *szFileName, BOOL bNew, int nShareMode)
{
	m_WriteBuf.SetAllocKind(CMEM_ALLOC_HEAP);
	m_WriteBuf.Alloc(DEFAULT_BUF_SIZE);

	ZeroMemory(m_szPath, sizeof m_szPath);
	CreateFile(szFileName, bNew);
	m_bEnable		= TRUE;
	m_bPrintDate	= TRUE;
	m_bAutoReturn	= TRUE;
	m_uDeleteSize	= 0;
	m_bFileBackup	= FALSE;
	m_nLevel		= 0;
}

/********************************************************************************
*	関数名：~CLog																*
*	用　途：デストラクタ														*
*	引き数：なし																*
*	戻り値：なし																*
*	備　考：																	*
********************************************************************************/
CLog::~CLog()
{
}

/********************************************************************************
*	関数名：Enable																*
*	用　途：出力の有効／無効設定												*
*	引き数：有効にする場合はＴＲＵＥ，無効にする場合はＦＡＬＳＥ				*
*	戻り値：なし																*
*	備　考：																	*
********************************************************************************/
void CLog::Enable(BOOL bEnable)
{
	m_bEnable = bEnable;
}

/********************************************************************************
*	関数名：SetConfig															*
*	用　途：ログ出力設定														*
*	引き数：日付出力フラグ、自動改行フラグ、出力有効フラグ、削除サイズ、		*
*			ファイルバックアップフラグ、排他モード								*
*	戻り値：なし																*
*	備　考：削除サイズの単位は（ＭＢ）											*
********************************************************************************/
void CLog::SetConfig(
						BOOL	bPrintDate,
						BOOL	bAutoReturn,
						BOOL	bEnable,
						SIZE_T	uDeleteSize,
						BOOL	bFileBackup,
						INT32	nShareMode
					)
{
	m_bPrintDate	= bPrintDate;
	m_bAutoReturn	= bAutoReturn;
	m_bEnable		= bEnable;
	m_uDeleteSize	= uDeleteSize * 1024UL * 1024UL;
	m_bFileBackup	= bFileBackup;
	m_nShareMode	= nShareMode;
}

/********************************************************************************
*	関数名：SetDeleteSize														*
*	用　途：削除サイズの設定													*
*	引き数：削除サイズ、ファイルバックアップフラグ								*
*	戻り値：なし																*
*	備　考：削除サイズの単位は（ＭＢ）											*
*			ファイルコピー有無フラグがTRUEで、ファイル削除実行時に対象ファイル	*
*			をリネームして残しておく											*
********************************************************************************/
void CLog::SetDeleteSize(SIZE_T uDeleteSize, BOOL bFileBackup)
{
	m_uDeleteSize	= uDeleteSize * 1024UL * 1024UL;
	m_bFileBackup	= bFileBackup;
}

/********************************************************************************
*	関数名：GetWritePtr															*
*	用　途：書き込みバッファの取得												*
*	引き数：サイズ格納場所														*
*	戻り値：書き込みバッファ													*
*	備　考：																	*
********************************************************************************/
TCHAR *CLog::GetWritePtr(SIZE_T *upSize)
{
	if  (upSize == 0) return 0;

	*upSize = m_WriteBuf.Size();

	return (TCHAR *)m_WriteBuf.GetPtr();
}

/********************************************************************************
*	関数名：CreateFile															*
*	用　途：ファイルの作成														*
*	引き数：ファイル名、新規作成フラグ											*
*	戻り値：作成成功でＴＲＵＥ、失敗でＦＡＬＳＥ								*
*	備　考：存在しないフォルダは自動作成する									*
********************************************************************************/
BOOL CLog::CreateFile(TCHAR *szPath, BOOL bNew)
{
	FILE	*fp;
	TCHAR	szTargetPath[_MAX_PATH];
	TCHAR	szDrive[_MAX_DRIVE];
	TCHAR	szDir[_MAX_DIR];

	if ( szPath == 0 ) return FALSE;

	/***** ファイル格納フォルダがなければ作成する *****/
	_tsplitpath(szPath, szDrive, szDir, 0, 0);
	_tcscpy(szTargetPath, szDrive);
	_tcscat(szTargetPath, szDir);

	if ( szPath != m_szPath )
	{
		_tcsncpy_s(m_szPath, _countof(m_szPath), szPath, _countof(m_szPath) - 1);
	}

	if ( !m_bEnable ) return FALSE;

	/***** ディレクトリの作成 *****/
	MakeSureDirectoryPathExists((TCHAR*)szTargetPath);
	
	/***** 新規ファイル? *****/
	bNew |= IsNewFile( m_szPath );

	/***** ファイル作成 *****/
#ifndef _UNICODE
	if( bNew )
	{
		_tfopen_s(&fp, m_szPath, _T("wt"));
	}
	else
	{
		_tfopen_s(&fp, m_szPath, _T("at"));
	}
#else
	if( bNew )
	{
		_tfopen_s(&fp, m_szPath, _T("wt, ccs=UNICODE"));
	}
	else
	{
		_tfopen_s(&fp, m_szPath, _T("at, ccs=UNICODE"));
	}
#endif

	if ( fp == 0 ) return FALSE;

	fclose(fp);	
		
	return TRUE;
}

/********************************************************************************
*	関数名：Print																*
*	用　途：ログ出力															*
*	引き数：書式指定文字列、出力値												*
*	戻り値：なし																*
*	備　考：																	*
********************************************************************************/
void CLog::Print(TCHAR *szFmt, ...)
{
	va_list		pArgPtr;
	TCHAR		szDate[20];
	TCHAR		*szPtr;
	SIZE_T		uSize;
	FILE		*fp = NULL;

	if ( !m_bEnable || _tcslen(m_szPath) <= 0 ) return;

	Restruct();

	szPtr	= (TCHAR *)m_WriteBuf.GetPtr();
	uSize	= m_WriteBuf.Size();

	va_start(pArgPtr, szFmt);
	_vstprintf_s(szPtr, uSize, szFmt, pArgPtr);
	va_end(pArgPtr);
	
	LogOpen(&fp);

	if (fp == 0) return;

	if ( m_bPrintDate )
	{
		DateString(szDate, 20, 0);
		_ftprintf_s(fp,	_T("%s %s%s"), szDate, szPtr, (m_bAutoReturn) ? _T("\n") : _T("\0"));
	}
	else
	{
		_ftprintf_s(fp,	_T("%s%s"), szPtr, (m_bAutoReturn) ? _T("\n") : _T("\0"));
	}

	fclose(fp);
}


/********************************************************************************
*	関数名：Print																*
*	用　途：ログ出力(バッファから直)											*
*	引き数：書式指定文字列、出力値												*
*	戻り値：なし																*
*	備　考：																	*
********************************************************************************/
void CLog::PrintBuffer(TCHAR* pPtr)
{
	TCHAR		szDate[20];
	SIZE_T		uSize;
	FILE		*fp = NULL;

	if ( !m_bEnable || _tcslen(m_szPath) <= 0 ) return;

	Restruct();

	uSize = _tcslen(pPtr);

	if(uSize <= 0){
		return ;
	}

	LogOpen(&fp);

	if (fp == 0) return;

	if ( m_bPrintDate )
	{
		DateString(szDate, 20, 0);
		_ftprintf_s(fp,	_T("%s %s%s"), szDate, pPtr, (m_bAutoReturn) ? _T("\n") : _T("\0"));
	}
	else
	{
		_ftprintf_s(fp,	_T("%s%s"), pPtr, (m_bAutoReturn) ? _T("\n") : _T("\0"));
	}

	fclose(fp);
}


/********************************************************************************
*	関数名：Print																*
*	用　途：ログ出力															*
*	引き数：出力文字列															*
*	戻り値：なし																*
*	備　考：																	*
********************************************************************************/
void CLog::Print(CString &str)
{
	TCHAR		szDate[20];
	FILE		*fp = NULL;

	if ( !m_bEnable || _tcslen(m_szPath) <= 0 ) return;

	Restruct();

	LogOpen(&fp);

	if (fp == 0) return;

	if ( m_bPrintDate )
	{
		DateString(szDate, 20, 0);
		_ftprintf_s(fp,	_T("%s %s%s"), szDate, (LPCTSTR)str, (m_bAutoReturn) ? _T("\n") : _T("\0"));
	}
	else
	{
		_ftprintf_s(fp,	_T("%s%s"), (LPCTSTR)str, (m_bAutoReturn) ? _T("\n") : _T("\0"));
	}

	fclose(fp);
}

/********************************************************************************
*	関数名：Print																*
*	用　途：ログ出力															*
*	引き数：																	*
*	戻り値：なし																*
*	備　考：既に保有している文字列を出力する									*
********************************************************************************/
void CLog::Print()
{
	TCHAR		szDate[20];
	FILE		*fp = NULL;
	TCHAR		*szPtr;

	if ( !m_bEnable || _tcslen(m_szPath) <= 0 ) return;

	Restruct();
	
	LogOpen(&fp);

	if (fp == 0) return;

	szPtr	= (TCHAR *)m_WriteBuf.GetPtr();

	if ( m_bPrintDate )
	{
		DateString(szDate, 20, 0);
		_ftprintf_s(fp,	_T("%s %s%s"), szDate, szPtr, (m_bAutoReturn) ? _T("\n") : _T("\0"));
	}
	else
	{
		_ftprintf_s(fp,	_T("%s%s"), szPtr, (m_bAutoReturn) ? _T("\n") : _T("\0"));
	}

	fclose(fp);
}

/********************************************************************************
*	関数名：Print																*
*	用　途：レベル付ログ出力													*
*	引き数：出力レベル、書式指定文字列、出力値									*
*	戻り値：なし																*
*	備　考：																	*
********************************************************************************/
void CLog::Print(int nLevel, TCHAR *szFmt, ...)
{
	va_list		pArgPtr;
	TCHAR		*szPtr;
	SIZE_T		uSize;

	if ( !CheckLevel(nLevel) ) return;

	szPtr	= (TCHAR *)m_WriteBuf.GetPtr();
	uSize	= m_WriteBuf.Size();

	va_start(pArgPtr, szFmt);
	_vstprintf_s(szPtr, uSize, szFmt, pArgPtr);
	va_end(pArgPtr);

	Print();
}

/********************************************************************************
*	関数名：Print																*
*	用　途：出力レベル付ログ出力												*
*	引き数：出力文字列															*
*	戻り値：なし																*
*	備　考：																	*
********************************************************************************/
void CLog::Print(INT32 nLevel, CString &str)
{
	if ( !CheckLevel(nLevel) ) return;

	Print(str);
}

/********************************************************************************
*	関数名：Print																*
*	用　途：出力レベル付ログ出力												*
*	引き数：出力レベル															*
*	戻り値：なし																*
*	備　考：既に保有している文字列を出力する									*
********************************************************************************/
void CLog::Print(INT32 nLevel)
{
	if ( !CheckLevel(nLevel) ) return;

	Print();
}

/********************************************************************************
*	関数名：Restruct															*
*	用　途：ファイル再構築														*
*	引き数：																	*
*	戻り値：																	*
*	備　考：ファイルが削除サイズ以上になっていればファイルを新規に作成する		*
********************************************************************************/
void CLog::Restruct()
{
	CFileFind	find;
	CString		str;
	BOOL		bFind;
	UINT64		uLength;
	SYSTEMTIME	tTime;
	CString		csBackupName;
	CFile		file;
	TCHAR		szDrive[_MAX_DRIVE];
	TCHAR		szDir[_MAX_DIR];
	TCHAR		szFname[_MAX_FNAME];
	TCHAR		szExt[_MAX_EXT];

	if ( m_uDeleteSize <= 0 ) return;

	bFind =	find.FindFile(m_szPath);
	if ( bFind )
	{
		find.FindNextFile();
		uLength = find.GetLength();

		// 対象ファイルのサイズが規定の削除を超えているか判定する
		if ( uLength >= m_uDeleteSize )
		{
			// 削除対象ファイルのバックアップ指示の有無を判定する
			if(m_bFileBackup == TRUE){
				// バックアップ指示有りの場合、対象ファイルをリネームして残しておく

				// 対象のファイル名を構成要素に分解する
				_tsplitpath(m_szPath, szDrive, szDir, szFname, szExt);

				// 現在の日時文字列を生成する
				GetLocalTime(&tTime);
				str.Format(	_T("%04u%02u%02u%02u%02u%02u"),
							tTime.wYear,
							tTime.wMonth,
							tTime.wDay,
							tTime.wHour,
							tTime.wMinute,
							tTime.wSecond
						);
				// ファイル名の末尾に現在の日時文字列を連結する
				csBackupName  = szDrive;
				csBackupName += szDir;
				csBackupName += szFname;
				csBackupName += _T("_");
				csBackupName += str;
				csBackupName += szExt;

				// コピー元ファイルをコピー先ファイルにリネームする
				try{
					file.Rename(m_szPath, csBackupName);
				}
				catch(CFileException* pEx){
					// リネームエラーは無視する
					pEx->Delete();
				}
			}

			// 対象ファイルを削除(新規作成)する
			CreateFile(m_szPath, TRUE);
			str.Format(_T("ファイルサイズが許容量(%llu bytes)を超えたので削除しました。"), m_uDeleteSize);
			Print(str);
		}
	}
	else
	{
		CreateFile(m_szPath, TRUE);
	}

	find.Close();
}

/********************************************************************************
*	関数名：SetLevel															*
*	用　途：出力レベルの設定													*
*	引き数：新たに設定する出力レベル、レベル比較方法							*
*	戻り値：設定前の出力レベル													*
*	備　考：レベル以上のものしか出力しない										*
********************************************************************************/
int CLog::SetLevel(INT32 nNewLevel, INT32 nMethod)
{
	INT32 nOldLevel;
	
	nOldLevel	= m_nLevel;
	m_nLevel	= nNewLevel;
	m_nMethod	= nMethod;

	return nOldLevel;
}

/********************************************************************************
*	関数名：CheckLevel															*
*	用　途：出力レベルの検査定													*
*	引き数：出力レベル															*
*	戻り値：出力可能なレベルであればＴＲＵＥ、不可能であればＦＡＬＳＥ			*
*	備　考：レベル以上のものしか出力しない										*
********************************************************************************/
BOOL CLog::CheckLevel(INT32 nLevel)
{
	switch(m_nMethod)
	{
	case LOG_LEVEL_QUANTITY:
		return (nLevel >= m_nLevel) ? TRUE : FALSE; 
	case LOG_LEVEL_KIND:
		return (nLevel & m_nLevel) ? TRUE : FALSE; 
	}

	return FALSE;
}

/********************************************************************************
*	関数名：LogOpen																*
*	用　途：ログファイルのオープン												*
*	引き数：FILE構造体のポインタのポインタ										*
*	戻り値：失敗時、引数の値はNULLになります。									*
*	備　考：設定された排他制御に基づいてオープンします。						*
*			※排他制御設定がある場合、ファイル出力がUNICODEになるかどうか不明	*
********************************************************************************/
void CLog::LogOpen(FILE** fp)
{
	CString		strFileMode;
	BOOL		bNew;

	bNew = IsNewFile( m_szPath ) ;

#ifndef _UNICODE
	if( bNew )
	{
		strFileMode = _T("wt");
	}
	else
	{
		strFileMode = _T("at");
	}
#else
	if( bNew )
	{
		strFileMode = _T("wt, ccs=UNICODE");
	}
	else
	{
		strFileMode = _T("at, ccs=UNICODE");
	}
#endif

	if (m_nShareMode == 0)
	{
		// 従来の方法
		_tfopen_s(fp, m_szPath, strFileMode);
	}
	else
	{
		// 排他モードを設定する場合
		//
		// ※fsopenがUNICODE入出力に対応しているかどうか不明
		//
		*fp = _tfsopen(m_szPath, strFileMode, m_nShareMode);
	}
}


/********************************************************************************
*	関数名：IsNewFile															*
*	用　途：新規ファイルかどうかをチェックする									*
*	引き数：ファイル名															*
*	戻り値：新規ファイル(TRUE) : 既存ファイル(FALSE)							*
*	備　考：※ファイルがロックされている等の場合もオープンが失敗するため		*
*			　新規ファイルとして扱われるので注意								*
********************************************************************************/
BOOL CLog::IsNewFile(LPCTSTR szFileName)
{
	FILE   *tpFile = NULL;
	errno_t	nError;

	/***** 一旦開いてみる			*****/
	/***** 開けたら既存ファイル有り	*****/
#ifndef _UNICODE
	nError = _tfopen_s(&tpFile, szFileName, _T("rt"));
#else
	nError = _tfopen_s(&tpFile, szFileName, _T("rt, ccs=UNICODE"));
#endif
	if (nError != 0)
	{
		/***** ファイルがありません(ENOENT)なら新規ファイル *****/
		return (nError == ENOENT);
	}

	/***** 開けたので既存ファイル *****/
	fclose( tpFile );

	return FALSE;
}


/****************************
*   コンパイル環境定数	    *
****************************/
#pragma warning(pop)
