/*
	ZLog.hpp
	Author: James Russell

	Purpose: 
	
	Logging utility.  The logging class cannot be instantiated, but instead is utilized through
	static public method calls.  The logging system must be initialized before use and can be 
	shutdown at any time.  Any logging calls placed after the system has been shutdown amount
	to a no-op.

	The logging system is thread-safe.

	License:

	TODO
*/


#ifndef _ZLOG_HPP
#define _ZLOG_HPP

#include <ZSTL/ZPair.hpp>
#include <ZSTL/ZString.hpp>

#include <ZUtil/ZMutex.hpp>
#include <ZUtil/ZSmartPointer.hpp>

#include <iostream>
#include <fstream>

//Typedef to define a log file handle
typedef size_t ZLogFile;

//Logging type enumeration used to specify which level of log is being performed
enum ZLogType
{
	LOG_ERROR,			//Used when an error condition has occurred
	LOG_WARNING,		//Used when a recoverable error condition has occurred
	LOG_INFO,			//Used when significant events have occurred
	LOG_SPAM,			//Used for insignificant events and process flow notification
	LOG_SIZE
};

//Overall Logging Levels
#define ZLOG_LEVEL_NONE			0
#define ZLOG_LEVEL_ERROR		1
#define ZLOG_LEVEL_WARNING		2
#define ZLOG_LEVEL_INFO			3
#define ZLOG_LEVEL_SPAM			4

//If the log level is not defined by the user, then ensure all logging is handled
#ifndef ZLOG_LEVEL
#define ZLOG_LEVEL				ZLOG_LEVEL_SPAM
#endif

//Define used to enumerate the default log file
#define ZLOGFILE_DEFAULT		0 

#if defined(_MSC_VER) || defined(__GNUC__) || defined(__SUNPRO_CC) //C99 style variadic macros

#define SystemLogError(...)		ZLog::Printf(LOG_ERROR,   ZLOGFILE_DEFAULT, 0, 0, __FUNCTION__, __VA_ARGS__)
#define SystemLogWarning(...)	ZLog::Printf(LOG_WARNING, ZLOGFILE_DEFAULT, 0, 0, __FUNCTION__, __VA_ARGS__)
#define SystemLogInfo(...)		ZLog::Printf(LOG_INFO,    ZLOGFILE_DEFAULT, 0, 0, __FUNCTION__, __VA_ARGS__)
#define SystemLogSpam(...)		ZLog::Printf(LOG_SPAM,    ZLOGFILE_DEFAULT, 0, 0, __FUNCTION__, __VA_ARGS__)

#if ZLOG_LEVEL < ZLOG_LEVEL_SPAM
#undef SystemLogSpam
#define SystemLogSpam(...)
#endif
#if ZLOG_LEVEL < ZLOG_LEVEL_INFO
#undef SystemLogInfo
#define SystemLogInfo(...)
#endif
#if ZLOG_LEVEL < ZLOG_LEVEL_WARNING
#undef SystemLogWarning
#define SystemLogWarning(...)
#endif
#if ZLOG_LEVEL < ZLOG_LEVEL_ERROR
#undef SystemLogError
#define SystemLogError(...)
#endif
#if (ZLOG_LEVEL < ZLOG_LEVEL_NONE) || (ZLOG_LEVEL > ZLOG_LEVEL_SPAM)
#error ZLOG_LEVEL Set to Invalid Value!
#endif

#else //Not C99-style variadic macro

#error Define variadic macro flavor for your compiler in ZLog.hpp

#endif

/*
Logging delegate.  Called when a log entry has been made.
*/
class ZLogDelegate
{
public:
	virtual ~ZLogDelegate() { }
	/*
	virtual public ZLogDelegate::Execute

	Delegate execute method.  This is called whenever a logging entry has been made.

	@param _type - the type of logging entry
	@param _file - the log file written to
	@param _logEntry - the logging entry made
	@return (void)
	*/
	virtual void Execute(ZLogType _type, ZLogFile _file, const ZString& _logEntry) = 0;
};

/*
ZEngine logging class.
*/
class ZLog
{
private:
	//Mutex for logging system
	static ZMutex LogLock;
	
	//Boolean indicating the logging system is activated
	static bool bIsInitialized;

	//Array of logging output streams (second pair parameter indicates active / suspended)
	static ZArray< ZPair<std::ofstream*, bool> > LogFiles;

	//Array of logging delegates
	static ZArray< ZPtr<ZLogDelegate> > Delegates;

	//Root path for logging
	static ZString LogFileRoot;

	//Maintained Tick Count
	static uint64_t *UserTick;

	//Maintained Update Frame Count
	static uint64_t *UserUpdate;

	//Tick Estimate when NULL is passed in as Tick
	static uint64_t TickEstimate;

	//Update Frame estimate when NULL is passed in as Update tick
	static uint64_t UpdateFrameEstimate;

	//Private Constructor
	ZLog() { }

	//Private Copy Constructor
	ZLog(const ZLog& _other) { URFP(_other); }

	//Private Destructor
	~ZLog() { }

public:

	/////////////////////////////////
	/* Initialization and Shutdown */
	/////////////////////////////////

	/*
	static public ZFileSystem::Init

	Initialization method for ZLog.  Until this is called, all logging calls return immediately.

	@param _logFileRoot - the directory to place log files in
	@param _defaultLogFile - log file name for the 'default' log file (NULL log file)
	@param _ticks - pointer to a value that will be updated with tick count (NULL to use parameter value only)
	@param _update - pointer to a value that will be updated with update frame count (NULL to use parameter value only)
	@return (void)
	*/
	static void Init(const ZString& _logFileRoot, const ZString& _defaultLogFile, uint64_t* _ticks = NULL, uint64_t* _update = NULL);

	/*
	static public ZFileSystem::Shutdown

	Shutdown method for ZLog.  Closes out the opened log files and flushes the output buffers.  After
	this is called, all logging operations return immediately.
	
	@return (void)
	*/
	static void Shutdown();

	///////////////////////////////
	/* Logging System Operations */
	///////////////////////////////

	/*
	static public ZFileSystem::AddLoggingDelegate

	Adds a logging delegate to the system which is called whenever logging takes
	place.

	@param _delegate - the logging delegate to call when logging takes place
	@return (void)
	*/
	static void AddLoggingDelegate(ZPtr<ZLogDelegate> _delegate);

	/*
	static public ZFileSystem::CreateLogFile

	Creates a log file for use in the system.

	@param _fileName - the name to use for the log file
	@return (ZLOG_FILE) - the created log file (will be NULL if could not create)
	*/
	static ZLogFile CreateLogFile(const ZString& _fileName);
	
	/*
	public static ZLog::Resume
	
	Resumes logging operations on a given log file.
	
	@param _logFile - the file to suspend logging operations on
	@return (void)
	*/
	static void Resume(ZLogFile _logFile);

	/*
	public static ZLog::SuspendLogging
	
	Suspends logging operations on a given log file.
	
	@param _logFile - the file to suspend logging operations on
	@return (void)
	*/
	static void Suspend(ZLogFile _logFile);

	/////////////////////
	/* Logging Methods */
	/////////////////////

	/*
	static public ZFileSystem::Printf

	Varardic logging function, which writes the given string out to the log file.  This version takes varargs in the 
	same way as printf.  This logging function has a limit on the length of the output string of 4096 characters - excess
	characters will be truncated.

	@param _type - the logging type
	@param _file - the file to write to (if ZLOG_DEFAULT, uses the default file)
	@param _ticks - the tick count at which the log file is written
	@param _updateFrame - the update 'frame' during which the log is written
	@param _str - the string to write to the log file
	@param ... - the variable arguments to provide to format _str
	@param args - parsed va_list
	@return (void)
	*/
	static void Printf(ZLogType _type, ZLogFile _file, uint64_t _ticks, uint32_t _updateFrame, const char *_function, const char *_str, ...);

	/*
	static public ZFileSystem::WriteLine

	Writes a string out to the given log file.

	@param _type - the logging type
	@param _file - the file to write to (if ZLOG_DEFAULT, uses the default file)
	@param _ticks - the tick count at which the log file is written
	@param _updateFrame - the update 'frame' during which the log is written
	@param _renderFrame - the render 'frame' during which the log is written
	@param _str - the string to write to the log file
	@return (void)
	*/
	static void WriteLine(ZLogType _type, ZLogFile _file, uint64_t _ticks, uint32_t _updateFrame, const char* _str);
};

#endif


