//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// Logging system declarations.
//
// The logging system is a channel-based output mechanism which allows
// subsystems to route their text/diagnostic output to various listeners
//
//===============================================================================

#ifndef LOGGING_H
#define LOGGING_H

#if defined( COMPILER_MSVC )
#pragma once
#endif

#include "color.h"
#include "icommandline.h"
#include <stdio.h>

// For XBX_** functions
#if defined( _X360 )
#include "xbox/xbox_console.h"
#endif

// Used by CColorizedLoggingListener
#if defined( _WIN32 ) || (defined(POSIX) && !defined(_GAMECONSOLE))
#include "tier0/win32consoleio.h"
#endif

/*
	---- Logging System ----

	The logging system is a channel-based mechanism for all code (engine,
	mod, tool) across all platforms to output information, warnings, 
	errors, etc.

	This system supersedes the existing Msg(), Warning(), Error(), DevMsg(), ConMsg() etc. functions.
	There are channels defined in the new system through which all old messages are routed;
	see LOG_GENERAL, LOG_CONSOLE, LOG_DEVELOPER, etc.

	To use the system, simply call one of the predefined macros:

		Log_Msg( ChannelID, [Color], Message, ... )
		Log_Warning( ChannelID, [Color], Message, ... )
		Log_Error( ChannelID, [Color], Message, ... )

	A ChannelID is typically created by defining a logging channel with the
	log channel macros:

		DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_ChannelName, "ChannelName", [Flags], [MinimumSeverity], [Color] );
	
		or

		BEGIN_DEFINE_LOGGING_CHANNEL( LOG_ChannelName, "ChannelName", [Flags], [MinimumSeverity], [Color] );
		ADD_LOGGING_CHANNEL_TAG( "Tag1" );
		ADD_LOGGING_CHANNEL_TAG( "Tag2" );
		END_DEFINE_LOGGING_CHANNEL();

	These macros create a global channel ID variable with the name specified 
	by the first parameter (in this example, LOG_ChannelName).  This channel ID 
	can be used by various LoggingSystem_** functions to manipulate the channel settings.

	The optional [Flags] parameter is an OR'd together set of LoggingChannelFlags_t 
	values (default: 0).

	The optional [MinimumSeverity] parameter is the lowest threshold
	above which messages will be processed (inclusive).  The default is LS_MESSAGE,
	which results in all messages, warnings, and errors being logged. 
	Variadic parameters to the Log_** functions will be ignored if a channel
	is not enabled for a given severity (for performance reasons).

	Logging channels can have their minimum severity modified by name, ID, or tag.

	Logging channels are not hierarchical since there are situations in which
	a channel needs to belong to multiple hierarchies.  Use tags to create
	categories or shallow hierarchies.

	@TODO (Feature wishlist):
	1) Callstack logging support
	2) Registering dynamic channels and unregistering channels at runtime
	3) Sentient robot to clean up the thousands of places using the old/legacy logging system.
*/

//////////////////////////////////////////////////////////////////////////
// Constants, Types, Forward Declares
//////////////////////////////////////////////////////////////////////////

class CLoggingSystem;
class CThreadFastMutex;

//-----------------------------------------------------------------------------
// Maximum length of a sprintf'ed logging message.
//-----------------------------------------------------------------------------
const int MAX_LOGGING_MESSAGE_LENGTH = 2048;

//-----------------------------------------------------------------------------
// Maximum length of a channel or tag name.
//-----------------------------------------------------------------------------
const int MAX_LOGGING_IDENTIFIER_LENGTH = 32;

//-----------------------------------------------------------------------------
// Maximum number of logging channels.  Increase if needed.
//-----------------------------------------------------------------------------
const int MAX_LOGGING_CHANNEL_COUNT = 256;

//-----------------------------------------------------------------------------
// Maximum number of logging tags across all channels.  Increase if needed.
//-----------------------------------------------------------------------------
const int MAX_LOGGING_TAG_COUNT = 1024;

//-----------------------------------------------------------------------------
// Maximum number of characters across all logging tags. Increase if needed.
//-----------------------------------------------------------------------------
const int MAX_LOGGING_TAG_CHARACTER_COUNT = 8192;

//-----------------------------------------------------------------------------
// Maximum number of concurrent logging listeners in a given logging state.
//-----------------------------------------------------------------------------
const int MAX_LOGGING_LISTENER_COUNT = 16;

//-----------------------------------------------------------------------------
// An invalid color set on a channel to imply that it should use
// a device-dependent default color where applicable.
//-----------------------------------------------------------------------------
const Color UNSPECIFIED_LOGGING_COLOR( 0, 0, 0, 0 );

//-----------------------------------------------------------------------------
// An ID returned by the logging system to refer to a logging channel.
//-----------------------------------------------------------------------------
typedef int LoggingChannelID_t;

//-----------------------------------------------------------------------------
// A sentinel value indicating an invalid logging channel ID.
//-----------------------------------------------------------------------------
const LoggingChannelID_t INVALID_LOGGING_CHANNEL_ID = -1;

//-----------------------------------------------------------------------------
// The severity of a logging operation.
//-----------------------------------------------------------------------------
enum LoggingSeverity_t
{
	//-----------------------------------------------------------------------------
	// An informative logging message.
	//-----------------------------------------------------------------------------
	LS_MESSAGE = 0,

	//-----------------------------------------------------------------------------
	// A warning, typically non-fatal
	//-----------------------------------------------------------------------------
	LS_WARNING = 1,

	//-----------------------------------------------------------------------------
	// A message caused by an Assert**() operation.
	//-----------------------------------------------------------------------------
	LS_ASSERT = 2,

	//-----------------------------------------------------------------------------
	// An error, typically fatal/unrecoverable.
	//-----------------------------------------------------------------------------
	LS_ERROR = 3,

	//-----------------------------------------------------------------------------
	// A placeholder level, higher than any legal value.
	// Not a real severity value!
	//-----------------------------------------------------------------------------
	LS_HIGHEST_SEVERITY = 4,
};

//-----------------------------------------------------------------------------
// Action which should be taken by logging system as a result of 
// a given logged message.
//
// The logging system invokes ILoggingResponsePolicy::OnLog() on
// the specified policy object, which returns a LoggingResponse_t.
//-----------------------------------------------------------------------------
enum LoggingResponse_t
{
	LR_CONTINUE,
	LR_DEBUGGER,
	LR_ABORT,
};

//-----------------------------------------------------------------------------
// Logging channel behavior flags, set on channel creation.
//-----------------------------------------------------------------------------
enum LoggingChannelFlags_t
{
	//-----------------------------------------------------------------------------
	// Indicates that the spew is only relevant to interactive consoles.
	//-----------------------------------------------------------------------------
	LCF_CONSOLE_ONLY = 0x00000001,

	//-----------------------------------------------------------------------------
	// Indicates that spew should not be echoed to any output devices.
	// A suitable logging listener must be registered which respects this flag 
	// (e.g. a file logger).
	//-----------------------------------------------------------------------------
	LCF_DO_NOT_ECHO = 0x00000002,
};

//-----------------------------------------------------------------------------
// A callback function used to register tags on a logging channel 
// during initialization.
//-----------------------------------------------------------------------------
typedef void ( *RegisterTagsFunc )();

//-----------------------------------------------------------------------------
// A context structure passed to logging listeners and response policy classes.
//-----------------------------------------------------------------------------
struct LoggingContext_t
{
	// ID of the channel being logged to.
	LoggingChannelID_t m_ChannelID;
	// Flags associated with the channel.
	LoggingChannelFlags_t m_Flags;
	// Severity of the logging event.
	LoggingSeverity_t m_Severity;
	// Color of logging message if one was specified to Log_****() macro.
	// If not specified, falls back to channel color.
	// If channel color is not specified, this value is UNSPECIFIED_LOGGING_COLOR
	// and indicates that a suitable default should be chosen.
	Color m_Color;
};

//-----------------------------------------------------------------------------
// Interface for classes to handle logging output.
//
// The Log() function of this class is called synchronously and serially
// by the logging system on all registered instances of ILoggingListener
// in the current "logging state".
//
// Derived classes may do whatever they want with the message (write to disk,
// write to console, send over the network, drop on the floor, etc.).
//
// In general, derived classes should do one, simple thing with the output
// to allow callers to register multiple, orthogonal logging listener classes.
//-----------------------------------------------------------------------------
class ILoggingListener
{
public:
	virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage ) = 0;
};

//-----------------------------------------------------------------------------
// Interface for policy classes which determine how to behave when a 
// message is logged.
//
// Can return:
//   LR_CONTINUE (continue execution)
//   LR_DEBUGGER (break into debugger if one is present, otherwise continue)
//   LR_ABORT (terminate process immediately with a failure code of 1)
//-----------------------------------------------------------------------------
class ILoggingResponsePolicy
{
public:
	virtual LoggingResponse_t OnLog( const LoggingContext_t *pContext ) = 0;
};

//////////////////////////////////////////////////////////////////////////
// Common Logging Listeners & Logging Response Policies
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
// A basic logging listener which prints to stdout and the debug channel.
//-----------------------------------------------------------------------------
class CSimpleLoggingListener : public ILoggingListener
{
public:
	CSimpleLoggingListener( bool bQuietPrintf = false, bool bQuietDebugger = false ) : 
	  m_bQuietPrintf( bQuietPrintf ), 
		  m_bQuietDebugger( bQuietDebugger ) 
	  { 
	  }

	  virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
	  {
#ifdef _X360
		  if ( !m_bQuietDebugger && XBX_IsConsoleConnected() )
		  {
			  // send to console
			  XBX_DebugString( XMAKECOLOR( 0,0,0 ), pMessage );
		  }
		  else
#endif
		  {
#if !defined( _CERT ) && !defined( DBGFLAG_STRINGS_STRIP )
			  if ( !m_bQuietPrintf )
			  {
				  _tprintf( _T("%s"), pMessage );
			  }
#endif

#ifdef _WIN32
			  if ( !m_bQuietDebugger && Plat_IsInDebugSession() )
			  {
				  Plat_DebugString( pMessage );
			  }
#endif
		  }
	  }

	  // If set to true, does not print anything to stdout.
	  bool m_bQuietPrintf;
	  // If set to true, does not print anything to debugger.
	  bool m_bQuietDebugger;
};

//-----------------------------------------------------------------------------
// A basic logging listener for GUI applications
//-----------------------------------------------------------------------------
class CSimpleWindowsLoggingListener : public ILoggingListener
{
public:
	virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
	{
		if ( Plat_IsInDebugSession() )
		{
			Plat_DebugString( pMessage );
		}
		if ( pContext->m_Severity == LS_ERROR )
		{
			if ( Plat_IsInDebugSession() )
				DebuggerBreak();

			Plat_MessageBox( "Error", pMessage );
		}
	}
};


//-----------------------------------------------------------------------------
// ** NOTE FOR INTEGRATION **
// This was copied over from source 2 rather than integrated because 
// source 2 has more significantly refactored tier0 logging.
//
// A logging listener with Win32 console API color support which which prints 
// to stdout and the debug channel.
//-----------------------------------------------------------------------------
#if !defined(_GAMECONSOLE)
class CColorizedLoggingListener : public CSimpleLoggingListener
{
public:
	CColorizedLoggingListener( bool bQuietPrintf = false, bool bQuietDebugger = false ) : CSimpleLoggingListener( bQuietPrintf, bQuietDebugger )
	{
		InitWin32ConsoleColorContext( &m_ColorContext );
	}

	virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
	{
		if ( !m_bQuietPrintf )
		{
			int nPrevColor = -1;

			if ( pContext->m_Color != UNSPECIFIED_LOGGING_COLOR )
			{
				nPrevColor = SetWin32ConsoleColor( &m_ColorContext,
					pContext->m_Color.r(), pContext->m_Color.g(), pContext->m_Color.b(), 
					MAX( MAX( pContext->m_Color.r(), pContext->m_Color.g() ), pContext->m_Color.b() ) > 128 );
			}

			_tprintf( _T("%s"), pMessage );

			if ( nPrevColor >= 0 )
			{
				RestoreWin32ConsoleColor( &m_ColorContext, nPrevColor );
			}
		}

#ifdef _WIN32
		if ( !m_bQuietDebugger && Plat_IsInDebugSession() )
		{
			Plat_DebugString( pMessage );
		}
#endif
	}

	Win32ConsoleColorContext_t m_ColorContext;
};
#endif // !_GAMECONSOLE


//-----------------------------------------------------------------------------
// Default logging response policy used when one is not specified.
//-----------------------------------------------------------------------------
class CDefaultLoggingResponsePolicy : public ILoggingResponsePolicy
{
public:
	virtual LoggingResponse_t OnLog( const LoggingContext_t *pContext )
	{
		if ( pContext->m_Severity == LS_ASSERT && !CommandLine()->FindParm( "-noassert" ) ) 
		{
			return LR_DEBUGGER;
		}
		else if ( pContext->m_Severity == LS_ERROR )
		{
			return LR_ABORT;
		}
		else
		{
			return LR_CONTINUE;
		}
	}
};

//-----------------------------------------------------------------------------
// A logging response policy which never terminates the process, even on error.
//-----------------------------------------------------------------------------
class CNonFatalLoggingResponsePolicy : public ILoggingResponsePolicy
{
public:
	virtual LoggingResponse_t OnLog( const LoggingContext_t *pContext )
	{
		if ( ( pContext->m_Severity == LS_ASSERT && !CommandLine()->FindParm( "-noassert" ) ) || pContext->m_Severity == LS_ERROR )
		{
			return LR_DEBUGGER;
		}
		else
		{
			return LR_CONTINUE;
		}
	}
};

//////////////////////////////////////////////////////////////////////////
// Central Logging System
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
// The central logging system.
//
// Multiple instances can exist, though all exported tier0 functionality
// specifically works with a single global instance 
// (via GetGlobalLoggingSystem()).
//-----------------------------------------------------------------------------
class CLoggingSystem
{
public:
	struct LoggingChannel_t;

	CLoggingSystem();
	~CLoggingSystem();

	//-----------------------------------------------------------------------------
	// Register a logging channel with the logging system.
	// The same channel can be registered multiple times, but the parameters
	// in each call to RegisterLoggingChannel must either match across all calls
	// or be set to defaults on any given call
	//
	// This function is not thread-safe and should generally only be called 
	// by a single thread. Using the logging channel definition macros ensures 
	// that this is called on the static initialization thread.
	//-----------------------------------------------------------------------------
	LoggingChannelID_t RegisterLoggingChannel( const char *pChannelName, RegisterTagsFunc registerTagsFunc, int flags = 0, LoggingSeverity_t minimumSeverity = LS_MESSAGE, Color spewColor = UNSPECIFIED_LOGGING_COLOR );

	//-----------------------------------------------------------------------------
	// Gets a channel ID from a string name.
	// Performs a simple linear search; cache the value whenever possible
	// or re-register the logging channel to get a global ID.
	//-----------------------------------------------------------------------------
	LoggingChannelID_t FindChannel( const char *pChannelName ) const;

	int GetChannelCount() const { return m_nChannelCount; }

	//-----------------------------------------------------------------------------
	// Gets a pointer to the logging channel description.
	//-----------------------------------------------------------------------------
	LoggingChannel_t *GetChannel( LoggingChannelID_t channelID );
	const LoggingChannel_t *GetChannel( LoggingChannelID_t channelID ) const;
	
	//-----------------------------------------------------------------------------
	// Returns true if the given channel has the specified tag.
	//-----------------------------------------------------------------------------
	bool HasTag( LoggingChannelID_t channelID, const char *pTag ) const { return GetChannel( channelID )->HasTag( pTag ); }
	
	//-----------------------------------------------------------------------------
	// Returns true if the given channel has been initialized.
	// The main purpose is catching m_nChannelCount being zero because no channels have been registered.
	//-----------------------------------------------------------------------------
	bool IsValidChannelID( LoggingChannelID_t channelID ) const { return ( channelID >= 0 ) && ( channelID < m_nChannelCount ); }
	
	//-----------------------------------------------------------------------------
	// Returns true if the given channel will spew at the given severity level.
	//-----------------------------------------------------------------------------
	bool IsChannelEnabled( LoggingChannelID_t channelID, LoggingSeverity_t severity ) const { return IsValidChannelID( channelID ) && GetChannel( channelID )->IsEnabled( severity ); }

	//-----------------------------------------------------------------------------
	// Functions to set the spew level of a channel either directly by ID or 
	// string name, or for all channels with a given tag.
	//
	// These functions are not technically thread-safe but calling them across 
	// multiple threads should cause no significant problems 
	// (the underlying data types being changed are 32-bit/atomic).
	//-----------------------------------------------------------------------------
	void SetChannelSpewLevel( LoggingChannelID_t channelID, LoggingSeverity_t minimumSeverity );
	void SetChannelSpewLevelByName( const char *pName, LoggingSeverity_t minimumSeverity );
	void SetChannelSpewLevelByTag( const char *pTag, LoggingSeverity_t minimumSeverity );

	//-----------------------------------------------------------------------------
	// Gets or sets the color of a logging channel.
	// (The functions are not thread-safe, but the consequences are not 
	// significant.)
	//-----------------------------------------------------------------------------
	Color GetChannelColor( LoggingChannelID_t channelID ) const { return GetChannel( channelID )->m_SpewColor; }
	void SetChannelColor( LoggingChannelID_t channelID, Color color ) { GetChannel( channelID )->m_SpewColor = color; }

	//-----------------------------------------------------------------------------
	// Gets or sets the flags on a logging channel.
	// (The functions are not thread-safe, but the consequences are not 
	// significant.)
	//-----------------------------------------------------------------------------
	LoggingChannelFlags_t GetChannelFlags( LoggingChannelID_t channelID ) const { return GetChannel( channelID )->m_Flags; }
	void SetChannelFlags( LoggingChannelID_t channelID, LoggingChannelFlags_t flags ) { GetChannel( channelID )->m_Flags = flags; }

	//-----------------------------------------------------------------------------
	// Adds a string tag to a channel.
	// This is not thread-safe and should only be called by a RegisterTagsFunc
	// callback passed in to RegisterLoggingChannel (via the 
	// channel definition macros).
	//-----------------------------------------------------------------------------
	void AddTagToCurrentChannel( const char *pTagName );

	//-----------------------------------------------------------------------------
	// Functions to save/restore the current logging state.  
	// Set bThreadLocal to true on a matching Push/Pop call if the intent
	// is to override the logging listeners on the current thread only.
	//
	// Pushing the current logging state onto the state stack results
	// in the current state being cleared by default (no listeners, default logging response policy).
	// Set bClearState to false to copy the existing listener pointers to the new state.
	//
	// These functions which mutate logging state ARE thread-safe and are 
	// guarded by m_StateMutex.
	//-----------------------------------------------------------------------------
	void PushLoggingState( bool bThreadLocal = false, bool bClearState = true );
	void PopLoggingState( bool bThreadLocal = false );

	//-----------------------------------------------------------------------------
	// Registers a logging listener (a class which handles logged messages).
	//-----------------------------------------------------------------------------
	void RegisterLoggingListener( ILoggingListener *pListener );
	
	//-----------------------------------------------------------------------------
	// Removes a logging listener from the registered list
	//-----------------------------------------------------------------------------
	void UnregisterLoggingListener( ILoggingListener *pListener );

	//-----------------------------------------------------------------------------
	// Returns whether the specified logging listener is registered.
	//-----------------------------------------------------------------------------
	bool IsListenerRegistered( ILoggingListener *pListener );

	//-----------------------------------------------------------------------------
	// Clears out all of the current logging state (removes all listeners, 
	// sets the response policy to the default).
	//-----------------------------------------------------------------------------
	void ResetCurrentLoggingState();

	//-----------------------------------------------------------------------------
	// Sets a policy class to decide what should happen when messages of a 
	// particular severity are logged 
	// (e.g. exit on error, break into debugger).
	// If pLoggingResponse is NULL, uses the default response policy class.
	//-----------------------------------------------------------------------------
	void SetLoggingResponsePolicy( ILoggingResponsePolicy *pLoggingResponse );
	
	//-----------------------------------------------------------------------------
	// Logs a message to the specified channel using a given severity and 
	// spew color.  Passing in UNSPECIFIED_LOGGING_COLOR for 'color' allows
	// the logging listeners to provide a default.
	// NOTE: test 'IsChannelEnabled(channelID,severity)' before calling this!
	//-----------------------------------------------------------------------------
	LoggingResponse_t LogDirect( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color color, const tchar *pMessage );
	
	// Internal data to represent a logging tag
	struct LoggingTag_t
	{
		const char *m_pTagName;
		LoggingTag_t *m_pNextTag;
	};

	// Internal data to represent a logging channel.
	struct LoggingChannel_t
	{	
		bool HasTag( const char *pTag ) const
		{
			LoggingTag_t *pCurrentTag = m_pFirstTag;
			while( pCurrentTag != NULL )
			{
				if ( stricmp( pCurrentTag->m_pTagName, pTag ) == 0 )
				{
					return true;
				}
				pCurrentTag = pCurrentTag->m_pNextTag;
			}
			return false;
		}
		bool IsEnabled( LoggingSeverity_t severity ) const { return severity >= m_MinimumSeverity; }
		void SetSpewLevel( LoggingSeverity_t severity ) { m_MinimumSeverity = severity; }

		LoggingChannelID_t m_ID;
		LoggingChannelFlags_t m_Flags; // an OR'd combination of LoggingChannelFlags_t
		LoggingSeverity_t m_MinimumSeverity; // The minimum severity level required to activate this channel.
		Color m_SpewColor;
		char m_Name[MAX_LOGGING_IDENTIFIER_LENGTH];
		LoggingTag_t *m_pFirstTag;
	};

private:
	// Represents the current state of the logger (registered listeners, response policy class, etc.) and can
	// vary from thread-to-thread.  It can also be pushed/popped to save/restore listener/response state.
	struct LoggingState_t
	{
		// Index of the previous entry on the listener set stack.
		int m_nPreviousStackEntry;

		// Number of active listeners in this set.  Cannot exceed MAX_LOGGING_LISTENER_COUNT.
		// If set to -1, implies that this state structure is not in use.
		int m_nListenerCount;
		// Array of registered logging listener objects.
		ILoggingListener *m_RegisteredListeners[MAX_LOGGING_LISTENER_COUNT];

		// Specific policy class to determine behavior of logging system under specific message types.
		ILoggingResponsePolicy *m_pLoggingResponse;
	};

	// These state functions to assume the caller has already grabbed the mutex.
	LoggingState_t *GetCurrentState();
	const LoggingState_t *GetCurrentState() const;

	int FindUnusedStateIndex();
	LoggingTag_t *AllocTag( const char *pTagName );

	int m_nChannelCount;
	LoggingChannel_t m_RegisteredChannels[MAX_LOGGING_CHANNEL_COUNT];

	int m_nChannelTagCount;
	LoggingTag_t m_ChannelTags[MAX_LOGGING_TAG_COUNT];
	
	// Index to first free character in name pool.
	int m_nTagNamePoolIndex;
	// Pool of character data used for tag names.
	char m_TagNamePool[MAX_LOGGING_TAG_CHARACTER_COUNT];

	// Protects all data in this class except the registered channels 
	// (which are supposed to be registered using the macros at static/global init time).
	// It is assumed that this mutex is reentrant safe on all platforms.
	CThreadFastMutex *m_pStateMutex;
	
	// The index of the current "global" state of the logging system.  By default, all threads use this state
	// for logging unless a given thread has pushed the logging state with bThreadLocal == true.
	// If a thread-local state has been pushed, g_nThreadLocalStateIndex (a global thread-local integer) will be non-zero.
	// By default, g_nThreadLocalStateIndex is 0 for all threads.
	int m_nGlobalStateIndex;
	
	// A pool of logging states used to store a stack (potentially per-thread).
	static const int MAX_LOGGING_STATE_COUNT = 16;
	LoggingState_t m_LoggingStates[MAX_LOGGING_STATE_COUNT];

	// Default policy class which determines behavior.
	CDefaultLoggingResponsePolicy m_DefaultLoggingResponse;

	// Default spew function.
	CSimpleLoggingListener m_DefaultLoggingListener;
	
};

//////////////////////////////////////////////////////////////////////////
// Logging Macros
//////////////////////////////////////////////////////////////////////////

// This macro will resolve to the most appropriate overload of LoggingSystem_Log() depending on the number of parameters passed in.
#ifdef DBGFLAG_STRINGS_STRIP
#define InternalMsg( Channel, Severity, /* [Color], Message, */ ... ) do { if ( Severity == LS_ERROR && LoggingSystem_IsChannelEnabled( Channel, Severity ) ) LoggingSystem_Log( Channel, Severity, /* [Color], Message, */ ##__VA_ARGS__ ); } while( 0 )
#else
#define InternalMsg( Channel, Severity, /* [Color], Message, */ ... ) do { if ( LoggingSystem_IsChannelEnabled( Channel, Severity ) ) LoggingSystem_Log( Channel, Severity, /* [Color], Message, */ ##__VA_ARGS__ ); } while( 0 )
#endif

//-----------------------------------------------------------------------------
// New macros, use these!
//
// The macros take an optional Color parameter followed by the message 
// and the message formatting.
// We rely on the variadic macro (__VA_ARGS__) operator to paste in the 
// extra parameters and resolve to the appropriate overload.
//-----------------------------------------------------------------------------
#define Log_Msg( Channel, /* [Color], Message, */ ... ) InternalMsg( Channel, LS_MESSAGE, /* [Color], Message, */ ##__VA_ARGS__ )
#define Log_Warning( Channel, /* [Color], Message, */ ... ) InternalMsg( Channel, LS_WARNING, /* [Color], Message, */ ##__VA_ARGS__ )
#define Log_Error( Channel, /* [Color], Message, */ ... ) InternalMsg( Channel, LS_ERROR, /* [Color], Message, */ ##__VA_ARGS__ )
#ifdef DBGFLAG_STRINGS_STRIP
#define Log_Assert( ... ) LR_CONTINUE
#else
#define Log_Assert( Message, ... ) LoggingSystem_LogAssert( Message, ##__VA_ARGS__ )
#endif


#define DECLARE_LOGGING_CHANNEL( Channel ) extern LoggingChannelID_t Channel

#define DEFINE_LOGGING_CHANNEL_NO_TAGS( Channel, ChannelName, /* [Flags], [Severity], [Color] */ ... ) \
	LoggingChannelID_t Channel = LoggingSystem_RegisterLoggingChannel( ChannelName, NULL, ##__VA_ARGS__ )

#define BEGIN_DEFINE_LOGGING_CHANNEL( Channel, ChannelName, /* [Flags], [Severity], [Color] */ ... ) \
	static void Register_##Channel##_Tags(); \
	LoggingChannelID_t Channel = LoggingSystem_RegisterLoggingChannel( ChannelName, Register_##Channel##_Tags, ##__VA_ARGS__ ); \
	void Register_##Channel##_Tags() \
	{

#define ADD_LOGGING_CHANNEL_TAG( Tag ) LoggingSystem_AddTagToCurrentChannel( Tag )

#define END_DEFINE_LOGGING_CHANNEL() \
	}
	
//////////////////////////////////////////////////////////////////////////
// DLL Exports
//////////////////////////////////////////////////////////////////////////

// For documentation on these functions, please look at the corresponding function
// in CLoggingSystem (unless otherwise specified).
PLATFORM_INTERFACE LoggingChannelID_t LoggingSystem_RegisterLoggingChannel( const char *pName, RegisterTagsFunc registerTagsFunc, int flags = 0, LoggingSeverity_t severity = LS_MESSAGE, Color color = UNSPECIFIED_LOGGING_COLOR ); 

PLATFORM_INTERFACE void LoggingSystem_RegisterLoggingListener( ILoggingListener *pListener );
PLATFORM_INTERFACE void LoggingSystem_UnregisterLoggingListener( ILoggingListener *pListener );
PLATFORM_INTERFACE void LoggingSystem_ResetCurrentLoggingState();
PLATFORM_INTERFACE void LoggingSystem_SetLoggingResponsePolicy( ILoggingResponsePolicy *pResponsePolicy );
// NOTE: PushLoggingState() saves the current logging state on a stack and results in a new clear state
// (no listeners, default logging response policy).
PLATFORM_INTERFACE void LoggingSystem_PushLoggingState( bool bThreadLocal = false, bool bClearState = true );
PLATFORM_INTERFACE void LoggingSystem_PopLoggingState( bool bThreadLocal = false );

PLATFORM_INTERFACE void LoggingSystem_AddTagToCurrentChannel( const char *pTagName );

// Returns INVALID_LOGGING_CHANNEL_ID if not found
PLATFORM_INTERFACE LoggingChannelID_t LoggingSystem_FindChannel( const char *pChannelName );
PLATFORM_INTERFACE int LoggingSystem_GetChannelCount();
PLATFORM_INTERFACE LoggingChannelID_t LoggingSystem_GetFirstChannelID();
// Returns INVALID_LOGGING_CHANNEL_ID when there are no channels remaining.
PLATFORM_INTERFACE LoggingChannelID_t LoggingSystem_GetNextChannelID( LoggingChannelID_t channelID );
PLATFORM_INTERFACE const CLoggingSystem::LoggingChannel_t *LoggingSystem_GetChannel( LoggingChannelID_t channelID );

PLATFORM_INTERFACE bool LoggingSystem_HasTag( LoggingChannelID_t channelID, const char *pTag );

PLATFORM_INTERFACE bool LoggingSystem_IsChannelEnabled( LoggingChannelID_t channelID, LoggingSeverity_t severity );
PLATFORM_INTERFACE void LoggingSystem_SetChannelSpewLevel( LoggingChannelID_t channelID, LoggingSeverity_t minimumSeverity );
PLATFORM_INTERFACE void LoggingSystem_SetChannelSpewLevelByName( const char *pName, LoggingSeverity_t minimumSeverity );
PLATFORM_INTERFACE void LoggingSystem_SetChannelSpewLevelByTag( const char *pTag, LoggingSeverity_t minimumSeverity );

// Color is represented as an int32 due to C-linkage restrictions
PLATFORM_INTERFACE int32 LoggingSystem_GetChannelColor( LoggingChannelID_t channelID );
PLATFORM_INTERFACE void LoggingSystem_SetChannelColor( LoggingChannelID_t channelID, int color );

PLATFORM_INTERFACE LoggingChannelFlags_t LoggingSystem_GetChannelFlags( LoggingChannelID_t channelID );
PLATFORM_INTERFACE void LoggingSystem_SetChannelFlags( LoggingChannelID_t channelID, LoggingChannelFlags_t flags );

//-----------------------------------------------------------------------------
// Logs a variable-argument to a given channel with the specified severity.
// NOTE: if adding overloads to this function, remember that the Log_***
// macros simply pass their variadic parameters through to LoggingSystem_Log().
// Therefore, you need to ensure that the parameters are in the same general 
// order and that there are no ambiguities with the overload.
//-----------------------------------------------------------------------------
PLATFORM_INTERFACE LoggingResponse_t LoggingSystem_Log( LoggingChannelID_t channelID, LoggingSeverity_t severity, const char *pMessageFormat, ... ) FMTFUNCTION( 3, 4 );
PLATFORM_OVERLOAD LoggingResponse_t LoggingSystem_Log( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color spewColor, const char *pMessageFormat, ... ) FMTFUNCTION( 4, 5 );

PLATFORM_INTERFACE LoggingResponse_t LoggingSystem_LogDirect( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color spewColor, const char *pMessage );
PLATFORM_INTERFACE LoggingResponse_t LoggingSystem_LogAssert( const char *pMessageFormat, ... ) FMTFUNCTION( 1, 2 );

#endif // LOGGING_H