/* 
 * Copyright (C) 2012 Yee Young Han <websearch@naver.com> (http://blog.naver.com/websearch)
 *
 * 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 3 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

#include "FileLog.h"
#include "TimeUtility.h"
#include "FileUtility.h"
#include <stdarg.h>

#ifndef WIN32
#include <unistd.h>
#endif

extern bool LogFileCompare( const std::string & strFirst, const std::string & strSecond );

CFileLog::CFileLog() : m_sttFd(NULL), m_iLevel(LOG_ERROR), m_iMaxLogSize(DEFAULT_LOG_FILE_SIZE), m_iLogSize(0)
	, m_iIndex(1), m_iMaxFolderSize(DEFAULT_LOG_FOLDER_SIZE), m_iFolderSize(0)
{
	memset( m_szDate, 0, sizeof(m_szDate) );
}

CFileLog::~CFileLog()
{
	Close();
}

/** 
 * @ingroup SipPlatform
 * @brief ·Î±× ÆÄÀÏÀ» ÀúÀåÇÒ µð·ºÅä¸®¸¦ ¼³Á¤ÇÑ´Ù.
 * @param	pszDirName	·Î±× ÆÄÀÏÀ» ÀúÀåÇÒ µð·ºÅä¸®
 * @return ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ±×·¸Áö ¾ÊÀ¸¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CFileLog::Open( const char * pszDirName )
{
	if( pszDirName == NULL ) return false;

	int iLen = (int)strlen( pszDirName );
	bool bRes = false;

	if( iLen < 3 ) return false;

	m_clsMutex.acquire();

	if( m_strDirName.empty() )
	{
#ifdef WIN32
		if( pszDirName[iLen-1] == '\\' )
#else
		if( pszDirName[iLen-1] == '/' )
#endif
		{
			m_strDirName.append( pszDirName, iLen - 1 );
		}
		else
		{
			m_strDirName = pszDirName;
		}

		if( CDirectory::Create( m_strDirName.c_str() ) )
		{
			m_iFolderSize = CDirectory::GetSize( m_strDirName.c_str() );
			bRes = true;
		}
	}

	m_clsMutex.release();

	return bRes;
}

/**
 * @ingroup SipPlatform
 * @brief ·Î±× ÆÄÀÏ ÀúÀåÀ» ÁßÁöÇÑ´Ù.
 * @returns true ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CFileLog::Close( )
{
	m_clsMutex.acquire();
	if( m_strDirName.empty() == false )
	{
		if( m_sttFd != NULL )
		{
			fclose( m_sttFd );
			m_sttFd = NULL;
		}

		m_strDirName.clear();
	}
	m_clsMutex.release();

	return true;
}

/** 
 * @ingroup SipPlatform
 * @brief ·Î±× ÆÄÀÏ¿¡ ·Î±×¸¦ ÀúÀåÇÑ´Ù.
 * @param	iLevel	·Î±× ÆÄÀÏ ·¹º§
 * @param	fmt			·Î±× ÆÄÀÏ¿¡ ÀúÀåÇÒ Æ÷¸Ë ¹®ÀÚ¿­
 * @param	...			fmt Æ÷¸Ë¿¡ ÀÔ·ÂÇÒ ÀÎÀÚµé
 * @return ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ±×·¸Áö ¾ÊÀ¸¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CFileLog::Print( EnumLogLevel iLevel, const char * fmt, ... )
{
	if( ( m_iLevel & iLevel ) == 0 ) return false;

	va_list		ap;
	char		szBuf[LOG_MAX_SIZE];
	char		szDate[9], szHeader[30];
	struct tm	sttTm;

	switch( iLevel )
	{
	case LOG_ERROR:
		snprintf( szHeader, sizeof(szHeader), "[ERROR] " );
		break;
	case LOG_INFO:
		snprintf( szHeader, sizeof(szHeader), "[INFO] " );
		break;
	case LOG_DEBUG:
		snprintf( szHeader, sizeof(szHeader), "[DEBUG] " );
		break;
	case LOG_NETWORK:
		snprintf( szHeader, sizeof(szHeader), "[NETWORK] " );
		break;
	case LOG_SYSTEM:
		snprintf( szHeader, sizeof(szHeader), "[SYSTEM] " );
		break;
	case LOG_SQL:
		snprintf( szHeader, sizeof(szHeader), "[SQL] " );
		break;
	default:
		memset( szHeader, 0, sizeof(szHeader) );
		break;
	}
	
	// ÇöÀçÀÇ ½Ã°£À» ±¸ÇÑ´Ù.
	struct timeval	sttTime;

	gettimeofday( &sttTime, NULL );
#ifdef WIN32
	_localtime32_s( &sttTm, &sttTime.tv_sec );
#else
	localtime_r( &sttTime.tv_sec, &sttTm );	
#endif

	snprintf( szDate, sizeof(szDate), "%04d%02d%02d", sttTm.tm_year + 1900, sttTm.tm_mon + 1, sttTm.tm_mday );

	if( m_clsMutex.acquire() == false ) return false;

	if( m_strDirName.empty() == false && strcmp( m_szDate, szDate ) )
	{
		// ÇöÀçÀÇ ³¯Â¥¿Í Å¬·¡½º¿¡ ÀúÀåµÈ ³¯Â¥°¡ ´Ù¸¥ °æ¿ì¿¡´Â ·Î±× ÆÄÀÏÀ» »õ·Î Á¦ÀÛÇÏ¿©¼­ Open ÇÑ´Ù.
		char	szFileName[FULLPATH_FILENAME_MAX_SIZE];
		
		m_iIndex = 1;

OPEN_FILE:
		m_iLogSize = 0;

		snprintf( szFileName, sizeof(szFileName), "%s%c%04d%02d%02d_%d.txt", m_strDirName.c_str(), DIR_SEP, sttTm.tm_year + 1900, sttTm.tm_mon + 1, sttTm.tm_mday, m_iIndex );
		
		// ÀÌ¹Ì Open µÈ ÆÄÀÏÀÌ ÀÖ´Â °æ¿ì¿¡´Â ÀÌ¸¦ ´Ý´Â´Ù.
		if( m_sttFd ) 
		{
			fclose( m_sttFd );
			m_sttFd = NULL;
		}

		DeleteOldFile();

		if( m_iMaxLogSize > 0 )
		{
			int64_t iFileSize = GetFileSize( szFileName );
			if( iFileSize >= m_iMaxLogSize )
			{
				++m_iIndex;
				goto OPEN_FILE;
			}

			m_iLogSize = (int)iFileSize;
		}
		
		m_sttFd = fopen( szFileName, "ab" );
		if( m_sttFd == NULL )
		{
			printf( "log file(%s) open error\n", szFileName );
		}

		snprintf( m_szDate, sizeof(m_szDate), "%s", szDate );
	}
	else if( m_iMaxLogSize > 0 && m_iLogSize >= m_iMaxLogSize )
	{
		++m_iIndex;
		goto OPEN_FILE;
	}

	m_clsMutex.release();

	va_start( ap, fmt );
	vsnprintf( szBuf, sizeof(szBuf)-1, fmt, ap );
	va_end( ap );

	if( m_clsMutex.acquire() == false ) return false;

	if( m_sttFd )
	{
		int iWrite;

#ifdef WIN32
		iWrite = fprintf( m_sttFd, "[%02d:%02d:%02d.%06u] %s[%X] %s\r\n"
			, sttTm.tm_hour, sttTm.tm_min, sttTm.tm_sec, sttTime.tv_usec, szHeader, GetCurrentThreadId(), szBuf );
#else
		iWrite = fprintf( m_sttFd, "[%02d:%02d:%02d.%06u] %s[%lX] %s\n"
			, sttTm.tm_hour, sttTm.tm_min, sttTm.tm_sec, (unsigned int)sttTime.tv_usec, szHeader, (unsigned long)pthread_self(), szBuf );
#endif
		fflush( m_sttFd );

		m_iLogSize += iWrite;
		m_iFolderSize += iWrite;
	}
	else
	{
#ifdef WIN32
		printf( "[%02d:%02d:%02d] %s%s\r\n", sttTm.tm_hour, sttTm.tm_min, sttTm.tm_sec, szHeader, szBuf );
#else
		printf( "[%02d:%02d:%02d] %s%s\n", sttTm.tm_hour, sttTm.tm_min, sttTm.tm_sec, szHeader, szBuf );
#endif
	}

	m_clsMutex.release();

	return true;
}

/**	
 * @ingroup SipPlatform
 * @brief	·Î±× ·¹º§À» °¡Á®¿Â´Ù.
 * @return ·Î±× ·¹º§À» ¸®ÅÏÇÑ´Ù.
 */
int CFileLog::GetLevel( )
{
	return m_iLevel;
}

/**	
 * @ingroup SipPlatform
 * @brief ·Î±× ÆÄÀÏ¿¡ ÀúÀåÇÒ ·Î±× ·¹º§À» ¼³Á¤ÇÑ´Ù. ¿©·¯ ·Î±×¸¦ ÀúÀåÇÒ °æ¿ì, '|' ¿¬»êÀÚ¸¦ ÀÌ¿ëÇÏ¿©¼­ ¿©·¯ ·Î±× ·¹º§À» ¼³Á¤ÇÒ ¼ö ÀÖ´Ù.
 * @param	iLevel	[in] µð¹ö±× ·Î±×¸¦ ÀúÀåÇÒ °æ¿ì, LOG_DEBUG ¸¦ ¼³Á¤ÇÑ´Ù.
 *				Á¤º¸ ·Î±×¸¦ ÀúÀåÇÒ °æ¿ì, LOG_INFO ¸¦ ¼³Á¤ÇÑ´Ù.
 *				¿¡·¯ ·Î±×¸¦ ÀúÀåÇÒ °æ¿ì, LOG_ERROR ¸¦ ¼³Á¤ÇÑ´Ù.
 */
void CFileLog::SetLevel( int iLevel )
{
	m_iLevel = LOG_ERROR | LOG_SYSTEM;
	m_iLevel |= iLevel;
}

/** 
 * @ingroup SipPlatform
 * @brief ÀÔ·ÂÇÑ ·Î±× ·¹º§ÀÌ ÇöÀç Ãâ·ÂÇÒ ¼ö ÀÖ´Â ·Î±× ·¹º§ÀÎÁö ºÐ¼®ÇÏ¿© ÁØ´Ù.
 * @param	iLevel	[in] ·Î±× ·¹º§
 * @return	ÇöÀç Ãâ·ÂÇÒ ¼ö ÀÖ´Â ·Î±× ·¹º§ÀÎ °æ¿ì¿¡´Â true ¸¦ ¸®ÅÏÇÏ°í ±×·¸Áö ¾ÊÀ¸¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CFileLog::IsPrintLogLevel( EnumLogLevel iLevel )
{
	if( ( m_iLevel & iLevel ) == 0 ) return false;

	return true;
}

/** 
 * @ingroup SipPlatform
 * @brief ·Î±×¸¦ ÀúÀåÇÒ ÃÖ´ë ÆÄÀÏ Å©±â¸¦ ¼³Á¤ÇÑ´Ù. 
 * @param iSize	·Î±×¸¦ ÀúÀåÇÒ ÃÖ´ë ÆÄÀÏ Å©±â
 */
void CFileLog::SetMaxLogSize( int iSize )
{
	if( iSize < MIN_LOG_FILE_SIZE )
	{
		iSize = MIN_LOG_FILE_SIZE;
	}
	else if( iSize > MAX_LOG_FILE_SIZE )
	{
		iSize = MAX_LOG_FILE_SIZE;
	}

	m_iMaxLogSize = iSize;
}

/**
 * @ingroup SipPlatform
 * @brief ·Î±× Æú´õ ÃÖ´ë Å©±â¸¦ ¼³Á¤ÇÑ´Ù.
 * @param iSize ·Î±× Æú´õ ÃÖ´ë Å©±â
 */
void CFileLog::SetMaxFolderSize( int64_t iSize )
{
	// ÃÖ¼Ò 30ÀÏ ·Î±× ÆÄÀÏ ÀúÀå °ø°£À» È®º¸ÇÑ´Ù.
	if( m_iMaxLogSize == 0 )
	{
		if( iSize < ( MIN_LOG_FILE_SIZE * 30 ) )
		{
			iSize = MIN_LOG_FILE_SIZE * 30;
		}
	}
	else
	{
		if( iSize < m_iMaxLogSize * 30 )
		{
			iSize = m_iMaxLogSize * 30;
		}
	}

	m_iMaxFolderSize = iSize;
}

/**
 * @ingroup SipPlatform
 * @brief ·Î±× Æú´õÀÇ Å©±â°¡ ¼³Á¤µÈ Å©±âº¸´Ù Å« °æ¿ì, ¿À·¡µÈ ·Î±× ÆÄÀÏÀ» »èÁ¦ÇÑ´Ù.
 */
void CFileLog::DeleteOldFile( )
{
	if( m_iMaxFolderSize == 0 ) return;
	if( m_strDirName.empty() ) return;

	if( m_iFolderSize >= m_iMaxFolderSize )
	{
		int64_t iWantSize = m_iMaxFolderSize * 8 / 10;
		FILE_LIST clsFileList;
		FILE_LIST::iterator	itList;
		int64_t iFileSize;

		CDirectory::FileList( m_strDirName.c_str(), clsFileList );

		clsFileList.sort( LogFileCompare );

		for( itList = clsFileList.begin(); itList != clsFileList.end(); ++itList )
		{
			std::string strFileName = m_strDirName;

			CDirectory::AppendName( strFileName, itList->c_str() );

			iFileSize = GetFileSize( strFileName.c_str() );

	#ifdef WIN32
			DeleteFile( strFileName.c_str() );
	#else
			unlink( strFileName.c_str() );
	#endif

			m_iFolderSize -= iFileSize;
		
			if( m_iFolderSize < iWantSize ) break;
		}
	}
}
