
/* ****************************************************************************

 * eID Middleware Project.
 * Copyright (C) 2008-2009 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see
 * http://www.gnu.org/licenses/.

**************************************************************************** */
#include "logbase.h"
#include "eiderrors.h"
#include "mwexception.h"

#include <time.h>
#include <errno.h>
#include "thread.h"
#include "configuration.h"
#include "util.h"
#include "mw_util.h"

#ifndef WIN32
#include <stdlib.h>
#ifdef __APPLE__
#include <PCSC/wintypes.h>
#else
#include <wintypes.h>
#endif
#include "sys/stat.h"
#include "util.h"

#define fwprintf_s fwprintf
#define vfwprintf_s vfwprintf
#define swprintf_s swprintf
#define _stat stat

#define LOG_DIRECTORY_DEFAULT  L"/tmp"
#endif

#define LOG_OPENFAILED_MAXALLOWED  10

namespace eIDMW
{

	tLOG_Level MapLevel(const wchar_t * level)
	{
		if (wcscmp(L"critical", level) == 0)
			return LOG_LEVEL_CRITICAL;
		else if (wcscmp(L"error", level) == 0)
			return LOG_LEVEL_ERROR;
		else if (wcscmp(L"warning", level) == 0)
			return LOG_LEVEL_WARNING;
		else if (wcscmp(L"info", level) == 0)
			return LOG_LEVEL_INFO;
		else if (wcscmp(L"debug", level) == 0)
			return LOG_LEVEL_DEBUG;
		else if (wcscmp(L"none", level) == 0)
			return LEV_LEVEL_NOLOG;
		else
			return LOG_LEVEL_DEFAULT;
	}

/* ******************
*** CLogger Class ***
******************* */
//-----------------------------------------------
// The logger needs a systemwide mutex to control the access to the
// logfile. A systemwide mutex on Windows is a named mutex.
// For Linux/Mac we don't change the current code.
//-----------------------------------------------
#ifdef WIN32
	HANDLE LogMutex;     // named mutex for Windows
#endif

	static CMutex m_mutex;	// mutex for:

	//   - non-windows
	//   - used as automutex for creating a logger instance

	std::unique_ptr < CLogger > CLogger::m_instance;
	bool CLogger::m_bApplicationLeaving = false;

//Default constructor
	CLogger::CLogger()
	{
		m_directory = L".";
		m_prefix = L"ZS";
		m_filesize = 100000;
		m_filenr = 2;
		m_groupinnewfile = false;
		m_maxlevel = LOG_LEVEL_DEFAULT;

		initFromConfig();
	}

//Copy constructor
	CLogger::CLogger(const CLogger & logger)
	{
		*this = logger;
	}

	CLogger & CLogger::operator=(const CLogger & logger)
	{
		if (this != &logger)
		{
			m_directory = logger.m_directory;
			m_prefix = logger.m_prefix;
			m_filesize = logger.m_filesize;
			m_filenr = logger.m_filenr;
			m_maxlevel = logger.m_maxlevel;
			m_groupinnewfile = logger.m_groupinnewfile;
		}
		return *this;
	}

//Destructor
	CLogger::~CLogger()
	{
		m_bApplicationLeaving = true;

		while (m_logStore.size() > 0)
		{
			delete m_logStore[m_logStore.size() - 1];

			m_logStore.pop_back();
		}
#ifdef WIN32
		//--------------------------------
		// Close the mutex handle. The last instance using the named mutex will
		// destroy the mutex
		//--------------------------------
		CloseHandle(LogMutex);
#endif
	}

//Get the singleton instance of the logger
	CLogger & CLogger::instance()
	{
		if (m_bApplicationLeaving)
			throw CMWEXCEPTION(EIDMW_ERR_LOGGER_APPLEAVING);


		if (m_instance.get() == 0)
		{
#ifdef WIN32
			//----------------------------------------------
			// always create the logger mutex. Only the first time the named mutex is
			// used, it is created. After that it is equivalent to opening the mutex
			//----------------------------------------------
			LogMutex = CreateMutex(0, FALSE, L"LogMutex");
#endif
			CAutoMutex autoMutex(&m_mutex);

			m_instance.reset(new CLogger);
		}
		return *m_instance;
	}

//Set the default values
	void CLogger::init(const wchar_t * directory, const wchar_t * prefix,
			   long filesize, long filenr, tLOG_Level maxlevel,
			   bool groupinnewfile)
	{
		m_directory = directory;
		m_prefix = prefix;
		m_filesize = filesize;
		m_filenr = filenr;
		m_maxlevel = maxlevel;
		m_groupinnewfile = groupinnewfile;
	}

	void CLogger::init(const char *directory, const char *prefix,
			   long filesize, long filenr, tLOG_Level maxlevel,
			   bool groupinnewfile)
	{
		init(utilStringWiden(directory).c_str(),
		     utilStringWiden(prefix).c_str(), filesize, filenr,
		     maxlevel, groupinnewfile);
	}

//Set the default values
	void CLogger::initFromConfig()
	{
		CConfig config;

		std::wstring wcsLogDir =
			config.
			GetString(CConfig::
				  EIDMW_CONFIG_PARAM_LOGGING_DIRNAME);
		std::wstring wcsPrefix =
			config.
			GetString(CConfig::EIDMW_CONFIG_PARAM_LOGGING_PREFIX);
		long lFileNbr =
			config.
			GetLong(CConfig::
				EIDMW_CONFIG_PARAM_LOGGING_FILENUMBER);
		long lFileSize =
			config.
			GetLong(CConfig::EIDMW_CONFIG_PARAM_LOGGING_FILESIZE);
		std::wstring wcsMaxLevel =
			config.
			GetString(CConfig::EIDMW_CONFIG_PARAM_LOGGING_LEVEL);
		tLOG_Level maxLevel = MapLevel(wcsMaxLevel.c_str());
		long lGroup =
			config.
			GetLong(CConfig::EIDMW_CONFIG_PARAM_LOGGING_GROUP);

		init(wcsLogDir.c_str(), wcsPrefix.c_str(), lFileSize,
		     lFileNbr, maxLevel, (lGroup ? true : false));
	}

//Retrieve a CLog object by is group name
	CLog & CLogger::getLogW(const wchar_t * group)
	{
		bool find = false;
		unsigned int i;

		for (i = 0; i < m_logStore.size(); i++)
		{
			if (m_logStore[i]->m_group.compare(group) == 0)
			{
				find = true;
				break;
			}
		}

		if (!find)
		{
			CLog *log =
				new CLog(m_directory.c_str(),
					 m_prefix.c_str(), group, m_filesize,
					 m_filenr, m_maxlevel,
					 m_groupinnewfile);
			m_logStore.push_back(log);
			return *log;
		}

		return *m_logStore[i];
	}

	CLog & CLogger::getLogA(const char *group)
	{
		return getLogW(utilStringWiden(group).c_str());
	}

//Write into the log of the group
	void CLogger::writeToGroup(const wchar_t * group, tLOG_Level level,
				   const wchar_t * format, ...)
	{
		CLog & log = getLogW(group);

		if (log.writeLineHeaderW(level))
		{
			va_list args;

			va_start(args, format);
			log.writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLogger::writeToGroup(const char *group, tLOG_Level level,
				   const char *format, ...)
	{
		CLog & log = getLogA(group);

		if (log.writeLineHeaderA(level))
		{
			va_list args;

			va_start(args, format);
			log.writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write into the log of the group with filename and line number
//use with __LINE__,__WFILE__
	void CLogger::writeToGroup(const wchar_t * group, tLOG_Level level,
				   const int line, const wchar_t * file,
				   const wchar_t * format, ...)
	{
		CLog & log = getLogW(group);

		if (log.writeLineHeaderW(level, line, file))
		{
			va_list args;

			va_start(args, format);
			log.writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLogger::writeToGroup(const char *group, tLOG_Level level,
				   const int line, const char *file,
				   const char *format, ...)
	{
		CLog & log = getLogA(group);

		if (log.writeLineHeaderA(level, line, file))
		{
			va_list args;

			va_start(args, format);
			log.writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write into the default log (no group)
	void CLogger::write(tLOG_Level level, const wchar_t * format, ...)
	{
		CLog & log = getLogW(L"");

		if (log.writeLineHeaderW(level))
		{
			va_list args;

			va_start(args, format);
			log.writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLogger::write(tLOG_Level level, const char *format, ...)
	{
		CLog & log = getLogA("");

		if (log.writeLineHeaderA(level))
		{
			va_list args;

			va_start(args, format);
			log.writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write into the default log (no group) with filename and line number
//use with  __LINE__,__WFILE__
	void CLogger::write(tLOG_Level level, const int line,
			    const wchar_t * file, const wchar_t * format, ...)
	{
		CLog & log = getLogW(L"");

		if (log.writeLineHeaderW(level, line, file))
		{
			va_list args;

			va_start(args, format);
			log.writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLogger::write(tLOG_Level level, const int line,
			    const char *file, const char *format, ...)
	{
		CLog & log = getLogA("");

		if (log.writeLineHeaderA(level, line, file))
		{
			va_list args;

			va_start(args, format);
			log.writeLineMessageA(format, args);
			va_end(args);
		}
	}

/* ***************
*** CLog Class ***
**************** */
	long CLog::m_sopenfailed = 0;

//PRIVATE: Default constructor
	CLog::CLog(const wchar_t * directory, const wchar_t * prefix,
		   const wchar_t * group, long filesize, long filenr,
		   tLOG_Level maxlevel, bool groupinnewfile)
	{
		m_f = NULL;
		m_directory = directory;
		m_prefix = prefix;
		m_group = group;
		m_filesize = filesize;
		m_filenr = filenr;
		m_maxlevel = maxlevel;
		m_groupinnewfile = groupinnewfile;
		m_openfailed = 0;
#ifndef WIN32
		m_flock = NULL;
#endif
	}

//Copy constructor
	CLog::CLog(const CLog & log) : m_directory(log.m_directory),
		m_prefix(log.m_prefix), m_group(log.m_group),
		m_filesize(log.m_filesize), m_filenr(log.m_filenr),
		m_maxlevel(log.m_maxlevel), m_groupinnewfile(log.m_groupinnewfile),
		m_openfailed(log.m_openfailed), m_f(log.m_f)
#ifndef WIN32
		, m_flock(log.m_flock)
#endif
	{ }

	CLog & CLog::operator=(const CLog & log)
	{
		if (this != &log)
		{
			m_f = log.m_f;
			m_directory = log.m_directory;
			m_prefix = log.m_prefix;
			m_group = log.m_group;
			m_filesize = log.m_filesize;
			m_filenr = log.m_filenr;
			m_maxlevel = log.m_maxlevel;
			m_groupinnewfile = log.m_groupinnewfile;
			m_openfailed = log.m_openfailed;
#ifndef WIN32
			m_flock = NULL;
#endif
		}
		return *this;
	}

//Destructor
	CLog::~CLog()
	{
	}

//PRIVATE: Return the name of to file to write into
	void CLog::getFilename(std::wstring & filename)
	{
		//Test if the directory exist
		std::wstring directory;

#ifdef WIN32
		DWORD dwError = 0;

		directory = m_directory + L"\\";
		DWORD dwAttr = GetFileAttributes(directory.c_str());

		if (dwAttr == INVALID_FILE_ATTRIBUTES)
			dwError = GetLastError();
		if (dwError == ERROR_FILE_NOT_FOUND
		    || dwError == ERROR_PATH_NOT_FOUND)
		{
			m_directory = L".";
			directory = m_directory + L"\\";
		}
#else
		//      --> TODO : Test if the directory exist
		directory = m_directory;
		struct stat buffer;

		if (stat(utilStringNarrow(directory).c_str(), &buffer))
		{
			// check error code
			m_directory = LOG_DIRECTORY_DEFAULT;
		}
		directory = m_directory + L"/";
#endif

		//Initialize the root filename
		std::wstring root_filename;
		root_filename = directory + m_prefix + L"_";
		if (m_groupinnewfile && m_group.size() > 0)
			root_filename += m_group + L"_";

		wchar_t index[5];

		swprintf_s(index, 5, L"%d", 0);

		//If there is a maximal file size, 
		//  we parse the file from index 0 to m_filenr-1 
		//  until we find one -that doesn't exist 
		//                    or
		//                    -with a size smaller than m_filesize
		//  If we don't, we have to rename the files
		//
		//Else If there is only one file its index is 0
		if (m_filesize > 0)
		{
			//There must be at least 2 files
			if (m_filenr < 2)
				m_filenr = 2;

			std::wstring file;
#ifdef WIN32
			struct _stat results;
#else
			struct stat results;
#endif
			bool find = false;

			for (int i = 0; i < m_filenr; i++)
			{

				swprintf_s(index, 5, L"%d", i);

				file = root_filename + index + L".log";
#ifdef WIN32
				if (_wstat(file.c_str(), &results) != 0
				    || results.st_size < m_filesize)
#else
				if (stat
				    (utilStringNarrow(file).c_str(),
				     &results) != 0
				    || results.st_size < m_filesize)
#endif
				{
					find = true;
					break;
				}
			}
			if (!find)
			{
				renameFiles(root_filename.c_str());
				swprintf_s(index, 5, L"%ld", m_filenr - 1);
			}
		}

		filename = root_filename + index + L".log";

	}

//PRIVATE: Delete file with index 0 et rename all file i to i-1 until m_filenr-1
//After this function, the file with index m_filenr-1 is free (it doesn't exist)
	void CLog::renameFiles(const wchar_t * root_filename)
	{
		//We remove the file 0
		std::wstring src;
		src = root_filename;
		src += L"0.log";
		int removed;

#ifdef WIN32
		removed = _wremove(src.c_str());
#else
		removed = remove(utilStringNarrow(src).c_str());
#endif

		if(removed != 0) {
			// file could not be removed, abort
			return;
		}
		std::wstring dest;
		wchar_t isrc[5];
		wchar_t idest[5];

		//For all file until m_filenr-1
		// 1 become 0
		// 2 become 1
		//i+1 become i
		//m_filenr-1 become m_filenr-2
		for (int i = 0; i < m_filenr; i++)
		{
			int ret;

			swprintf_s(isrc, 5, L"%d", i + 1);
			swprintf_s(idest, 5, L"%d", i);

			//if the source does not exist, we stop
			src = root_filename;
			src += isrc;
			src += L".log";

			dest = root_filename;
			dest += idest;
			dest += L".log";

			//Rename of the file
#ifdef WIN32
			ret = _wrename(src.c_str(), dest.c_str());
#else
			ret = rename(utilStringNarrow(src).c_str(),
			       utilStringNarrow(dest).c_str());
#endif
			if(ret < 0) {
				if(errno == ENOENT) {
					break;
				}
				continue;
			}
		}
	}

//PRIVATE: Open the file with the correct name
	bool CLog::open(bool bWchar)
	{
		if (!canWeTryToOpen())
		{
			incrementOpenFailed();
			return false;
		}
#ifdef WIN32
		WaitForSingleObject(LogMutex, INFINITE);
#else
		m_mutex.Lock();
#endif

		if (m_f)	//Should not happend
		{
			close();
			throw CMWEXCEPTION(EIDMW_ERR_UNKNOWN);
		}
#ifndef WIN32
		m_flock = (struct flock *) malloc(sizeof(struct flock));
		m_flock->l_type = F_WRLCK;	/* F_RDLCK, F_WRLCK, F_UNLCK    */
		m_flock->l_whence = SEEK_SET;	/* SEEK_SET, SEEK_CUR, SEEK_END */
		m_flock->l_start = 0;	/* Offset from l_whence         */
		m_flock->l_len = 0;	/* length, 0 = to EOF           */
		m_flock->l_pid = getpid();	/* our PID                      */
#endif

		int err = 0;

		std::wstring filename;

		int iLoop = 0;

		do		//If the file is locked by another process, we wait
		{
			getFilename(filename);	//We get the file name in the loop because other process may rename the files

#ifdef WIN32
			if (bWchar)
				err = _wfopen_s(&m_f, filename.c_str(),
						L"a, ccs=UTF-8");
			else
				err = fopen_s(&m_f,
					      utilStringNarrow(filename).
					      c_str(), "a");
#else
			m_f = fopen(utilStringNarrow(filename).c_str(),
				    "a, ccs=UTF-8");
			if (m_f == NULL)
				err = errno;
#endif

			if (err != 0 && err != EACCES)
				m_f = NULL;

			if (err == EACCES)
				CThread::SleepMillisecs(20);

			iLoop++;

		} while (err == EACCES && iLoop < 100);

#ifndef WIN32
		// on Linux/Mac we set an advisory lock, i.e. it prevents
		// other processes from using the file only if they are collaborative 
		// and check for the lock, otherwise they can do whatever they like ..
		if (m_f != NULL)
		{
			if (fcntl(fileno(m_f), F_SETLK, m_flock) == -1)	/* set the lock */
			{
				fclose(m_f);
				m_f = NULL;
			}
		}
#endif

		if (!m_f)
		{
#ifdef WIN32
			ReleaseMutex(LogMutex);
#else
			m_mutex.Unlock();
#endif
			incrementOpenFailed();
			return false;
		}

		resetOpenFailed();
		return true;
	}

//PRIVATE: Close the file
	inline void CLog::close()
	{
		if (!m_f)
			throw CMWEXCEPTION(EIDMW_ERR_UNKNOWN);

#ifndef WIN32
		m_flock->l_type = F_UNLCK;	/* tell it to unlock the region */

		if (fcntl(fileno(m_f), F_SETLK, m_flock) == -1)	/* set the region to unlocked */
			throw CMWEXCEPTION(EIDMW_ERR_UNKNOWN);

		free(m_flock);
#endif

		fclose(m_f);

		m_f = NULL;

#ifdef WIN32
		ReleaseMutex(LogMutex);
#else
		m_mutex.Unlock();
#endif
	}

//PRIVATE: Convert the enum into message
	const wchar_t *CLog::getLevel(tLOG_Level level)
	{
		switch (level)
		{
			case LOG_LEVEL_CRITICAL:
				return L"CRITICAL";
			case LOG_LEVEL_ERROR:
				return L"ERROR";
			case LOG_LEVEL_WARNING:
				return L"WARNING";
			case LOG_LEVEL_INFO:
				return L"INFO";
			case LOG_LEVEL_DEBUG:
				return L"DEBUG";
			default:
				return getLevel(LOG_LEVEL_DEFAULT);
		}

	}

//PRIVATE: Get local time in format YYYY-MM-DD hh:mm:ss by default
	void CLog::getLocalTimeW(std::wstring & timestamp,
				 const wchar_t * format)
	{
		time_t rawtime;
		struct tm timeinfo;
		wchar_t buffer[20];

		time(&rawtime);
#ifdef WIN32
		localtime_s(&timeinfo, &rawtime);
#else
		timeinfo = *(localtime(&rawtime));
#endif

		wcsftime(buffer, 20, format, &timeinfo);

		timestamp.assign(buffer);
	}

	void CLog::getLocalTimeA(std::string & timestamp, const char *format)
	{
		time_t rawtime;
		struct tm timeinfo;
		char buffer[20];

		time(&rawtime);
#ifdef WIN32
		localtime_s(&timeinfo, &rawtime);
#else
		timeinfo = *(localtime(&rawtime));
#endif

		strftime(buffer, 20, format, &timeinfo);

		timestamp.assign(buffer);
	}

//Write to log from a variable number of parameter
	void CLog::write(tLOG_Level level, const wchar_t * format, ...)
	{
		if (writeLineHeaderW(level))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::write(tLOG_Level level, const char *format, ...)
	{
		if (writeLineHeaderA(level))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write to log from a va_list
	void CLog::write(tLOG_Level level, const wchar_t * format,
			 va_list args)
	{
		if (writeLineHeaderW(level))
			writeLineMessageW(format, args);
	}

	void CLog::write(tLOG_Level level, const char *format, va_list args)
	{
		if (writeLineHeaderA(level))
			writeLineMessageA(format, args);
	}

//Write to log from a variable number of parameter with filename and line number
	void CLog::write(tLOG_Level level, const int line,
			 const wchar_t * file, const wchar_t * format, ...)
	{
		if (writeLineHeaderW(level, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::write(tLOG_Level level, const int line, const char *file,
			 const char *format, ...)
	{
		if (writeLineHeaderA(level, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write to log from a va_list with filename and line number
	void CLog::write(tLOG_Level level, const int line,
			 const wchar_t * file, const wchar_t * format,
			 va_list args)
	{
		if (writeLineHeaderW(level, line, file))
			writeLineMessageW(format, args);
	}

	void CLog::write(tLOG_Level level, const int line, const char *file,
			 const char *format, va_list args)
	{
		if (writeLineHeaderA(level, line, file))
			writeLineMessageA(format, args);
	}



//ATTENTION : Design for use with macro
//            Must be follow by writeLineMessage to close the file
//Write to log the first part of the line
	bool CLog::writeLineHeaderW(tLOG_Level level, const int line,
				    const wchar_t * file)
	{

		if (level > m_maxlevel)
			return false;

		long lPreviousOpenFailed = getOpenFailed();

		if (!open(true))
			return false;

		std::wstring timestamp;
		getLocalTimeW(timestamp);

		if (lPreviousOpenFailed > 0)
		{
			if (isFileMixingGroups())
			{
				fwprintf_s(m_f,
					   L"%ls - %d - %ls: ...ERROR: This file could not be opened. %ld logging line(s) are missing...\n",
					   timestamp.c_str(),
					   CThread::getCurrentPid(),
					   m_group.c_str(),
					   lPreviousOpenFailed);
			} else
			{
				fwprintf_s(m_f,
					   L"%ls - %d: ...ERROR: This file could not be opened. %ld logging line(s) are missing...\n",
					   timestamp.c_str(),
					   CThread::getCurrentPid(),
					   lPreviousOpenFailed);
			}
		}

		if (isFileMixingGroups())
		{
			if (line > 0 && wcslen(file) > 0)
				fwprintf_s(m_f,
					   L"%ls - %d|%ld - %ls - %ls -'%ls'-line=%d: ",
					   timestamp.c_str(),
					   CThread::getCurrentPid(),
					   CThread::getCurrentThreadId(),
					   m_group.c_str(), getLevel(level),
					   file, line);
			else
				fwprintf_s(m_f,
					   L"%ls - %d|%ld - %ls - %ls: ",
					   timestamp.c_str(),
					   CThread::getCurrentPid(),
					   CThread::getCurrentThreadId(),
					   m_group.c_str(), getLevel(level));
		} else
		{
			if (line > 0 && wcslen(file) > 0)
				fwprintf_s(m_f,
					   L"%ls - %d|%ld - %ls -'%ls'-line=%d: ",
					   timestamp.c_str(),
					   CThread::getCurrentPid(),
					   CThread::getCurrentThreadId(),
					   getLevel(level), file, line);
			else
				fwprintf_s(m_f, L"%ls - %d|%ld - %ls: ",
					   timestamp.c_str(),
					   CThread::getCurrentPid(),
					   CThread::getCurrentThreadId(),
					   getLevel(level));
		}

		return true;

	}

	bool CLog::writeLineHeaderA(tLOG_Level level_in, const int line,
				    const char *file)
	{

		if (level_in > m_maxlevel)
			return false;

		long lPreviousOpenFailed = getOpenFailed();

		if (!open(false))
			return false;

		std::string timestamp;
		getLocalTimeA(timestamp);

		if (lPreviousOpenFailed > 0)
		{
			if (isFileMixingGroups())
			{
				fprintf_s(m_f,
					  "%s - %ld - %ls: ...ERROR: This file could not be opened. %ld logging line(s) are missing...\n",
					  timestamp.c_str(),
					  CThread::getCurrentPid(),
					  m_group.c_str(),
					  lPreviousOpenFailed);
			} else
			{
				fprintf_s(m_f,
					  "%s - %ld: ...ERROR: This file could not be opened. %ld logging line(s) are missing...\n",
					  timestamp.c_str(),
					  CThread::getCurrentPid(),
					  lPreviousOpenFailed);
			}
		}

		std::string level = utilStringNarrow(getLevel(level_in));

		if (isFileMixingGroups())
		{
			std::string group = utilStringNarrow(m_group);

			if (line > 0 && strlen(file) > 0)
				fprintf_s(m_f,
					  "%s - %ld|%ld - %s - %s -'%s'-line=%d: ",
					  timestamp.c_str(),
					  CThread::getCurrentPid(),
					  CThread::getCurrentThreadId(),
					  group.c_str(), level.c_str(), file,
					  line);
			else
				fprintf_s(m_f, "%s - %ld|%ld - %s - %s: ",
					  timestamp.c_str(),
					  CThread::getCurrentPid(),
					  CThread::getCurrentThreadId(),
					  group.c_str(), level.c_str());
		} else
		{
			if (line > 0 && strlen(file) > 0)
				fprintf_s(m_f,
					  "%s - %ld|%ld - %s -'%s'-line=%d: ",
					  timestamp.c_str(),
					  CThread::getCurrentPid(),
					  CThread::getCurrentThreadId(),
					  level.c_str(), file, line);
			else
				fprintf_s(m_f, "%s - %ld|%ld - %s: ",
					  timestamp.c_str(),
					  CThread::getCurrentPid(),
					  CThread::getCurrentThreadId(),
					  level.c_str());
		}

		return true;

	}
//ATTENTION : Design for use with macro
//            Must be preceded by writeLineHeader to close the file
//Write to log the second part of the line
	bool CLog::writeLineMessageW(const wchar_t * format, ...)
	{
		if (!m_f)	//Should not happend, as this method must only be called if the writeLineHeader succeed
			throw CMWEXCEPTION(EIDMW_FILE_NOT_OPENED);

		va_list args;

		va_start(args, format);
		writeLineMessageW(format, args);
		va_end(args);

		return true;
	}

	bool CLog::writeLineMessageA(const char *format, ...)
	{
		if (!m_f)	//Should not happend, as this method must only be called if the writeLineHeader succeed
			throw CMWEXCEPTION(EIDMW_FILE_NOT_OPENED);

		va_list args;

		va_start(args, format);
		writeLineMessageA(format, args);
		va_end(args);

		return true;
	}

//PRIVATE
//Write to log the second part of the line

	void CLog::writeLineMessageW(const wchar_t * format, va_list argList)
	{

		if (!m_f)	//Should not happend, as this method must only be called if the writeLineHeader succeed
			throw CMWEXCEPTION(EIDMW_FILE_NOT_OPENED);

		vfwprintf_s(m_f, format, argList);
		fwprintf_s(m_f, L"%c", '\n');
		close();
	}

	void CLog::writeLineMessageA(const char *format, va_list argList)
	{

		if (!m_f)	//Should not happend, as this method must only be called if the writeLineHeader succeed
			throw CMWEXCEPTION(EIDMW_FILE_NOT_OPENED);

		vfprintf_s(m_f, format, argList);
		fprintf_s(m_f, "%c", '\n');
		close();
	}

//Write Critical level to log
	void CLog::writeCritical(const wchar_t * format, ...)
	{
		if (writeLineHeaderW(LOG_LEVEL_CRITICAL))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::writeCritical(const char *format, ...)
	{
		if (writeLineHeaderA(LOG_LEVEL_CRITICAL))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write Critical level to log with filename and line number
	void CLog::writeCritical(const int line, const wchar_t * file,
				 const wchar_t * format, ...)
	{
		if (writeLineHeaderW(LOG_LEVEL_CRITICAL, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::writeCritical(const int line, const char *file,
				 const char *format, ...)
	{
		if (writeLineHeaderA(LOG_LEVEL_CRITICAL, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write Error level to log
	void CLog::writeError(const wchar_t * format, ...)
	{
		if (writeLineHeaderW(LOG_LEVEL_ERROR))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}

	}

	void CLog::writeError(const char *format, ...)
	{
		if (writeLineHeaderA(LOG_LEVEL_ERROR))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}

	}
//Write Error level to log with filename and line number
	void CLog::writeError(const int line, const wchar_t * file,
			      const wchar_t * format, ...)
	{
		if (writeLineHeaderW(LOG_LEVEL_ERROR, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::writeError(const int line, const char *file,
			      const char *format, ...)
	{
		if (writeLineHeaderA(LOG_LEVEL_ERROR, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write Warning level to log
	void CLog::writeWarning(const wchar_t * format, ...)
	{
		if (writeLineHeaderW(LOG_LEVEL_WARNING))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::writeWarning(const char *format, ...)
	{
		if (writeLineHeaderA(LOG_LEVEL_WARNING))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write Warning level to log with filename and line number
	void CLog::writeWarning(const int line, const wchar_t * file,
				const wchar_t * format, ...)
	{
		if (writeLineHeaderW(LOG_LEVEL_WARNING, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::writeWarning(const int line, const char *file,
				const char *format, ...)
	{
		if (writeLineHeaderA(LOG_LEVEL_WARNING, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write Info level to log
	void CLog::writeInfo(const wchar_t * format, ...)
	{
		if (writeLineHeaderW(LOG_LEVEL_INFO))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::writeInfo(const char *format, ...)
	{
		if (writeLineHeaderA(LOG_LEVEL_INFO))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write Info level to log with filename and line number
	void CLog::writeInfo(const int line, const wchar_t * file,
			     const wchar_t * format, ...)
	{
		if (writeLineHeaderW(LOG_LEVEL_INFO, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::writeInfo(const int line, const char *file,
			     const char *format, ...)
	{
		if (writeLineHeaderA(LOG_LEVEL_INFO, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write Debug level to log
	void CLog::writeDebug(const wchar_t * format, ...)
	{
//    Beeps(1, 1000);
		if (writeLineHeaderW(LOG_LEVEL_DEBUG))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::writeDebug(const char *format, ...)
	{
//    Beeps(1, 1000);
		if (writeLineHeaderA(LOG_LEVEL_DEBUG))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

//Write Debug level to log with filename and line number
	void CLog::writeDebug(const int line, const wchar_t * file,
			      const wchar_t * format, ...)
	{

		if (writeLineHeaderW(LOG_LEVEL_DEBUG, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageW(format, args);
			va_end(args);
		}
	}

	void CLog::writeDebug(const int line, const char *file,
			      const char *format, ...)
	{

		if (writeLineHeaderA(LOG_LEVEL_DEBUG, line, file))
		{
			va_list args;

			va_start(args, format);
			writeLineMessageA(format, args);
			va_end(args);
		}
	}

	bool CLog::isFileMixingGroups()
	{
		return (!m_groupinnewfile || m_group.size() == 0);
	}

	long CLog::getOpenFailed()
	{
		if (isFileMixingGroups())
			return m_sopenfailed;
		else
			return m_openfailed;
	}

	bool CLog::canWeTryToOpen()
	{
		//To avoid delay if open failed 5 times consecutively,
		//we retry only once every 100 times
		if (isFileMixingGroups())
			return (m_sopenfailed <= 5
				|| (m_sopenfailed % 100) == 0);
		else
			return (m_openfailed <= 5
				|| (m_openfailed % 100) == 0);
	}

	void CLog::incrementOpenFailed()
	{
		if (isFileMixingGroups())
			m_sopenfailed++;
		else
			m_openfailed++;
	}

	void CLog::resetOpenFailed()
	{
		if (isFileMixingGroups())
			m_sopenfailed = 0;
		else
			m_openfailed = 0;
	}

}
