#include "SystemLog.h"

NAMESPACE_UPP

////////////////////////////////////////////////////////////////////////////////////////////////
// Global log object
SystemLog SysLog;

// gets platfor-dependent log level
#ifdef PLATFORM_POSIX
int SystemLog::platform(Levels level)
#elif defined(PLATFORM_WIN32)
dword SystemLog::platform(Levels level)
#endif
{
#ifdef PLATFORM_POSIX
	if(level & LEMERGENCY)	return LOG_EMERG;
	if(level & LALERT)		return LOG_ALERT;
	if(level & LCRITICAL)	return LOG_CRIT;
	if(level & LERROR)		return LOG_ERR;
	if(level & LWARNING)	return LOG_WARNING;
	if(level & LNOTICE)		return LOG_NOTICE;
	if(level & LINFO)		return LOG_INFO;
	if(level & LDEBUG)		return LOG_DEBUG;
#elif defined(PLATFORM_WIN32)
	if(level & LEMERGENCY)	return EVENTLOG_ERROR_TYPE;
	if(level & LALERT)		return EVENTLOG_ERROR_TYPE;
	if(level & LCRITICAL)	return EVENTLOG_ERROR_TYPE;
	if(level & LERROR)		return EVENTLOG_ERROR_TYPE;
	if(level & LWARNING)	return EVENTLOG_WARNING_TYPE;
	if(level & LNOTICE)		return EVENTLOG_INFORMATION_TYPE;
	if(level & LINFO)		return EVENTLOG_INFORMATION_TYPE;
	if(level & LDEBUG)		return EVENTLOG_INFORMATION_TYPE;
#endif

	// should not happen....
	return 0;
	
} // END SystemLog::platform()

////////////////////////////////////////////////////////////////////////////////////////////////
// constructor
SystemLog::SystemLog()
{
	// log not opened upon creation
	FOpened = false;
	
	// disables syslog, disables console, enables CErr() and enables Upp log output
	FCoutEnabled = false;
	FCerrEnabled = true;
	FSysLogEnabled = false;
	FUppLogEnabled = true;
	
	// initializes log buffer
	FBuffer = "";
	
	// initializes starting log level
	FLastLevel = LERROR;
	
} // END Constructor class SystemLog
		
////////////////////////////////////////////////////////////////////////////////////////////////
// destructor
SystemLog::~SystemLog()
{
	// closes the active log
	Close();
	
} // END Destructor class SystemLog

////////////////////////////////////////////////////////////////////////////////////////////////
// closes the active log
void SystemLog::Close()
{
	if (FOpened)
	{
		// flushes log buffer
		Flush();
		
#ifdef PLATFORM_POSIX
		closelog();
#elif defined(PLATFORM_WIN32)
		DeregisterEventSource(log);
#endif
		FOpened = false;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////
// opens and initializes log stream
SystemLog &SystemLog::Open(String const &name, String const &dllPath)
{
	// closes, if already opened
	Close();
	
	// stores the user log name
	FLogName = name;
	
#ifdef PLATFORM_POSIX
	openlog(FLogName, LOG_PID, LOG_USER);
#elif defined(PLATFORM_WIN32)
	String path;
	dword eventTypes = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
	dword categoryCount = 0;
	log = NULL;

	path << "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\" << FLogName;
	if (GetWinRegString("EventMessageFile", path, HKEY_LOCAL_MACHINE) != dllPath)
	{
		if (!SetWinRegExpandString(dllPath, "EventMessageFile", path, HKEY_LOCAL_MACHINE))
			return *this;
		if (!SetWinRegInt(eventTypes, "TypesSupported", path, HKEY_LOCAL_MACHINE))
			return *this;
	}
	log = RegisterEventSource(NULL, FLogName);
	if (log == NULL)
		return *this;
#endif

	// marks log stream as opened
	FOpened = true;

	return *this;
}


////////////////////////////////////////////////////////////////////////////////////////////////
// flushes log buffer
void SystemLog::Flush()
{
	// if no log opened, or buffer empty, does nothing
	if (!FOpened || FBuffer == "" || !(FEnabledLevels & FLastLevel))
	{
		FBuffer = "";
		return;
	}
	
	// Inserts log level name on buffer's head
	String msg = LevelName(FLastLevel) + FBuffer;
	
	// wipes buffer
	FBuffer = "";
	
	// outputs buffer to enabled log streams

	// if upp log enabled, log on upp app log file
	if(FUppLogEnabled)
		LOG(msg);
	
	// if CErr is enabled and message is an error one, logs to CErr()
	if(FCerrEnabled && (FLastLevel & LALLERR))
//		Cerr() << FLogName << " : " << msg << "\n";
		fprintf(stderr, "%s : %s\n", (const char *)FLogName, (const char *)msg);
	else if(FCoutEnabled)
		fprintf(stdout, "%s : %s\n", (const char *)FLogName, (const char *)msg);
//		Cout() << FLogName << " : " << msg << "\n";

	// if syslog enabled, logs on syslog
	if(FSysLogEnabled)
	{

#ifdef PLATFORM_POSIX
		syslog(platform(FLastLevel), msg);
#elif defined(PLATFORM_WIN32)
		const char *array[2];
		array[0] = (const char *)msg;
		if (log == NULL)
			return;
		if (!ReportEvent(log, (WORD)platform(FLastLevel), 0, 0x00000001L, NULL, 1, 0, &array[0], NULL))
			return;
#endif
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////
// enable specified level(s)
SystemLog &SystemLog::EnableLevels(byte Levels)
{
	// flushes log buffer if needed
	Flush();
	
	FEnabledLevels |= Levels;
	
	return *this;

} // END SystemLog::EnableLevels()

////////////////////////////////////////////////////////////////////////////////////////////////
// disable specified level(s)
SystemLog &SystemLog::DisableLevels(byte Levels)
{
	// flushes log buffer if needed
	Flush();
	
	FEnabledLevels &= ~Levels;
	
	return *this;
	
} // END SystemLog::DisableLevels()

////////////////////////////////////////////////////////////////////////////////////////////////
// sets enabled lebels to 'Levels'
SystemLog &SystemLog::SetLevels(byte Levels)
{
	// flushes log buffer if needed
	Flush();
	
	FEnabledLevels = Levels;
	
	return *this;
	
} // END SystemLog::SetLevels()

////////////////////////////////////////////////////////////////////////////////////////////////
// gets level name from level
String SystemLog::LevelName(Levels level)
{
	switch(level)
	{
		case LEMERGENCY :
			return "EMERGENCY:: ";
		case LALERT :
			return "ALERT    :: ";
		case LCRITICAL :
			return "CRITICAL :: ";
		case LERROR :
			return "ERROR    :: ";
		case LWARNING :
			return "WARNING  :: ";
		case LNOTICE :
			return "NOTICE   :: ";
		case LINFO :
			return "INFO     :: ";
		case LDEBUG :
			return "DEBUG    :: ";
		default:
			return "UNKNOWN  :: ";
	}
	
} // END SystemLog::LevelName()

////////////////////////////////////////////////////////////////////////////////////////////////
// sets current use level
SystemLog &SystemLog::SetLevel(Levels level)
{
	if(FLastLevel != level)
	{
		Flush();
		FLastLevel = level;
	}
	return *this;

} // END SystemLog::SetLevel()

SystemLog &SystemLog::operator()(Levels level)
{
	if(FLastLevel != level)
	{
		Flush();
		FLastLevel = level;
	}
	return *this;

} // END SystemLog::operator()
	
////////////////////////////////////////////////////////////////////////////////////////////////
// writes log message at required level
SystemLog &SystemLog::WriteLog(Levels level, String const &message)
{
	// nothing to do if log is not opened
	if(!FOpened)
		return *this;

	// checks if level is enabled
	if(!(FEnabledLevels & level))
		return *this;
	
	// if changing log level, flushes previous buffer
	if(FLastLevel != level)
		Flush();
	
	// sets last level as current
	FLastLevel = level;
	
	// appends current message to buffer
	int start = 0;
	int nl = 0;
	while( nl < message.GetCount() && (nl = message.Find('\n', start)) >= 0)
	{
		FBuffer += message.Mid(start, nl-start);
		Flush();
		start = nl+1;
	}
	if(start < message.GetCount())
		FBuffer += message.Mid(start);
	
	return *this;
}

END_UPP_NAMESPACE
