/*
*
*    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 2 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
*
*    In addition, as a special exception, the author gives permission to
*    link the code of this program with the Half-Life Game Engine ("HL
*    Engine") and Modified Game Libraries ("MODs") developed by Valve,
*    L.L.C ("Valve").  You must obey the GNU General Public License in all
*    respects for all of the code used other than the HL Engine and MODs
*    from Valve.  If you modify this file, you may extend this exception
*    to your version of the file, but you are not obligated to do so.  If
*    you do not wish to do so, delete this exception statement from your
*    version.
*
*/

#pragma once

#include "basetypes.h"

#include <math.h>
#include <stdio.h>
#include <stdarg.h>

// dll export stuff
#ifdef TIER0_DLL_EXPORT
#define DBG_INTERFACE DLL_EXPORT
#define DBG_OVERLOAD  DLL_GLOBAL_EXPORT
#define DBG_CLASS     DLL_CLASS_EXPORT
#else
#define DBG_INTERFACE DLL_IMPORT
#define DBG_OVERLOAD  DLL_GLOBAL_IMPORT
#define DBG_CLASS     DLL_CLASS_IMPORT
#endif

// Usage model for the Dbg library
//
// 1. Spew.
//
//   Spew can be used in a static and a dynamic mode. The static
//   mode allows us to display assertions and other messages either only
//   in debug builds, or in non-release builds. The dynamic mode allows us to
//   turn on and off certain spew messages while the application is running.
//
//   Static Spew messages:
//
//     Assertions are used to detect and warn about invalid states
//     Spews are used to display a particular status/warning message.
//
//     To use an assertion, use
//
//     Assert((f == 5));
//     AssertMsg((f == 5), ("F needs to be %d here!\n", 5));
//     AssertFunc((f == 5), BadFunc());
//     AssertEquals(f, 5);
//     AssertFloatEquals(f, 5.0f, 1e-3);
//
//     The first will simply report that an assertion failed on a particular
//     code file and line. The second version will display a print-f formatted message
//	   along with the file and line, the third will display a generic message and
//     will also cause the function BadFunc to be executed, and the last two
//	   will report an error if f is not equal to 5 (the last one asserts within
//	   a particular tolerance).
//
//     To use a warning, use
//
//     Warning("Oh I feel so %s all over\n", "yummy");
//
//     Warning will do its magic in only Debug builds. To perform spew in *all*
//     builds, use RelWarning.
//
//	   Three other spew types, Msg, Log, and Error, are compiled into all builds.
//	   These error types do *not* need two sets of parenthesis.
//
//	   Msg("Isn't this exciting %d?", 5);
//	   Error("I'm just thrilled");
//
//   Dynamic Spew messages
//
//     It is possible to dynamically turn spew on and off. Dynamic spew is
//     identified by a spew group and priority level. To turn spew on for a
//     particular spew group, use SpewActivate("group", level). This will
//     cause all spew in that particular group with priority levels <= the
//     level specified in the SpewActivate function to be printed. Use DSpew
//     to perform the spew:
//
//     DWarning("group", level, "Oh I feel even yummier!\n");
//
//     Priority level 0 means that the spew will *always* be printed, and group
//     '*' is the default spew group. If a DWarning is encountered using a group
//     whose priority has not been set, it will use the priority of the default
//     group. The priority of the default group is initially set to 0.
//
//   Spew output
//
//     The output of the spew system can be redirected to an externally-supplied
//     function which is responsible for outputting the spew. By default, the
//     spew is simply printed using printf.
//
//     To redirect spew output, call SpewOutput.
//
//     SpewOutputFunc(OutputFunc);
//
//     This will cause OutputFunc to be called every time a spew message is
//     generated. OutputFunc will be passed a spew type and a message to print.
//     It must return a value indicating whether the debugger should be invoked,
//     whether the program should continue running, or whether the program
//     should abort.
//
// 2. Code activation
//
//   To cause code to be run only in debug builds, use DBG_CODE:
//   An example is below.
//
//   DBG_CODE(
//				{
//					int x = 5;
//					++x;
//				}
//          );
//
//   Code can be activated based on the dynamic spew groups also. Use
//
//   DBG_DCODE("group", level,
//              { int x = 5; ++x; }
//           );
//
// 3. Breaking into the debugger.
//
//   To cause an unconditional break into the debugger in debug builds only, use DBG_BREAK
//
//   DBG_BREAK();
//
//	 You can force a break in any build (release or debug) using
//
//	 DebuggerBreak();

// Various types of spew messages
// I'm sure you're asking yourself why SPEW_ instead of DBG_ ?
// It's because DBG_ is used all over the place in windows.h
// For example, DBG_CONTINUE is defined. Feh.
enum SpewType_t
{
	SPEW_MESSAGE = 0,
	SPEW_WARNING,
	SPEW_ASSERT,
	SPEW_ERROR,
	SPEW_LOG,

	SPEW_TYPE_COUNT
};

enum SpewRetval_t
{
	SPEW_DEBUGGER = 0,
	SPEW_CONTINUE,
	SPEW_ABORT
};

// Type of externally defined function used to display debug spew
typedef SpewRetval_t (*SpewOutputFunc_t)(SpewType_t spewType, const char *pMsg);

// Used to redirect spew output
void SpewOutputFunc(SpewOutputFunc_t func);

// Used ot get the current spew output function
SpewOutputFunc_t GetSpewOutputFunc();

// Used to manage spew groups and subgroups
void SpewActivate(const char *pGroupName, int level);
bool IsSpewActive(const char *pGroupName, int level);

// Used to display messages, should never be called directly.
void _SpewInfo(SpewType_t type, const char *pFile, int line);
SpewRetval_t _SpewMessage(const char *pMsg, ...);
SpewRetval_t _DSpewMessage(const char *pGroupName, int level, const char *pMsg, ...);

// Used to define macros, never use these directly.
#define _Assert(_exp)                                                      \
	do {                                                                   \
		if (!(_exp))                                                       \
		{                                                                  \
			_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__);                    \
			if (_SpewMessage("Assertion Failed: " #_exp) == SPEW_DEBUGGER) \
			{                                                              \
				DebuggerBreak();                                           \
			}                                                              \
		}                                                                  \
	} while (0)

#define _AssertMsg(_exp, _msg)                          \
	do {                                                \
		if (!(_exp))                                    \
		{                                               \
			_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__); \
			if (_SpewMessage(_msg) == SPEW_DEBUGGER)    \
			{                                           \
				DebuggerBreak();                        \
			}                                           \
		}                                               \
	} while (0)

#define _AssertFunc(_exp, _f)                                           \
	do {                                                                \
		if (!(_exp))                                                    \
		{                                                               \
			_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__);                 \
			SpewRetval_t ret = _SpewMessage("Assertion Failed!" #_exp); \
			_f;                                                         \
			if (ret == SPEW_DEBUGGER)                                   \
			{                                                           \
				DebuggerBreak();                                        \
			}                                                           \
		}                                                               \
	} while (0)

#define _AssertEquals(_exp, _expectedValue)                                                       \
	do {                                                                                          \
		if ((_exp) != (_expectedValue))                                                           \
		{                                                                                         \
			_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__);                                           \
			SpewRetval_t ret = _SpewMessage("Expected %d but got %d!", (_expectedValue), (_exp)); \
			if (ret == SPEW_DEBUGGER)                                                             \
			{                                                                                     \
				DebuggerBreak();                                                                  \
			}                                                                                     \
		}                                                                                         \
	} while (0)

#define _AssertFloatEquals(_exp, _expectedValue, _tol)                                            \
	do {                                                                                          \
		if (fabs((_exp) - (_expectedValue)) > (_tol))                                             \
		{                                                                                         \
			_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__);                                           \
			SpewRetval_t ret = _SpewMessage("Expected %f but got %f!", (_expectedValue), (_exp)); \
			if (ret == SPEW_DEBUGGER)                                                             \
			{                                                                                     \
				DebuggerBreak();                                                                  \
			}                                                                                     \
		}                                                                                         \
	} while (0)

// Spew macros...
#ifdef _DEBUG

#define Assert(_exp)                                  _Assert(_exp)
#define AssertMsg(_exp, _msg)                         _AssertMsg(_exp, _msg)
#define AssertFunc(_exp, _f)                          _AssertFunc(_exp, _f)
#define AssertEquals(_exp, _expectedValue)            _AssertEquals(_exp, _expectedValue)
#define AssertFloatEquals(_exp, _expectedValue, _tol) _AssertFloatEquals(_exp, _expectedValue, _tol)
#define Verify(_exp)                                  _Assert(_exp)

#define AssertMsg1(_exp, _msg, a1)                                 _AssertMsg(_exp, CDbgFmtMsg(_msg, a1))
#define AssertMsg2(_exp, _msg, a1, a2)                             _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2))
#define AssertMsg3(_exp, _msg, a1, a2, a3)                         _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3))
#define AssertMsg4(_exp, _msg, a1, a2, a3, a4)                     _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4))
#define AssertMsg5(_exp, _msg, a1, a2, a3, a4, a5)                 _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5))
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6)             _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6))
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6)             _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6))
#define AssertMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7)         _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7))
#define AssertMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8)     _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7, a8))
#define AssertMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7, a8, a9))

#else // _DEBUG

#define Assert(_exp)                                  ((void)0)
#define AssertMsg(_exp, _msg)                         ((void)0)
#define AssertFunc(_exp, _f)                          ((void)0)
#define AssertEquals(_exp, _expectedValue)            ((void)0)
#define AssertFloatEquals(_exp, _expectedValue, _tol) ((void)0)
#define Verify(_exp)                                  (_exp)

#define AssertMsg1(_exp, _msg, a1)                                 ((void)0)
#define AssertMsg2(_exp, _msg, a1, a2)                             ((void)0)
#define AssertMsg3(_exp, _msg, a1, a2, a3)                         ((void)0)
#define AssertMsg4(_exp, _msg, a1, a2, a3, a4)                     ((void)0)
#define AssertMsg5(_exp, _msg, a1, a2, a3, a4, a5)                 ((void)0)
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6)             ((void)0)
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6)             ((void)0)
#define AssertMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7)         ((void)0)
#define AssertMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8)     ((void)0)
#define AssertMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) ((void)0)

#endif // _DEBUG

// These are always compiled in
void Msg(const char *pMsg, ...);
void DMsg(const char *pGroupName, int level, const char *pMsg, ...);

void Warning(const char *pMsg, ...);
void DWarning(const char *pGroupName, int level, const char *pMsg, ...);

void Log(const char *pMsg, ...);
void DLog(const char *pGroupName, int level, const char *pMsg, ...);

void Error(const char *pMsg, ...);

// You can use this macro like a runtime assert macro.
// If the condition fails, then Error is called with the message. This macro is called
// like AssertMsg, where msg must be enclosed in parenthesis:
//
// ErrorIfNot(bCondition, ("a b c %d %d %d", 1, 2, 3));
#define ErrorIfNot(condition, msg) \
	if ((condition))               \
		;                          \
				else               \
	{                              \
		Error msg;                 \
	}

// A couple of super-common dynamic spew messages, here for convenience
// These looked at the "developer" group
void DevMsg(int level, char const* pMsg, ...);
void DevWarning(int level, const char *pMsg, ...);
void DevLog(int level, const char *pMsg, ...);

// default level versions (level 1)
void DevMsg(char const* pMsg, ...);
void DevWarning(const char *pMsg, ...);
void DevLog(const char *pMsg, ...);

// Code macros, debugger interface
#ifdef _DEBUG
#define DBG_CODE(_code)          if (0) ; else { _code }
#define DBG_DCODE(_g, _l, _code) if (IsSpewActive(_g, _l)) { _code } else {}
#define DBG_BREAK()              DebuggerBreak()
#else // _DEBUG
#define DBG_CODE(_code)          ((void)0)
#define DBG_DCODE(_g, _l, _code) ((void)0)
#define DBG_BREAK()              ((void)0)
#endif // _DEBUG

// Macro to assist in asserting constant invariants during compilation
#define UID_PREFIX generated_id_
#define UID_CAT1(a, c) a ## c
#define UID_CAT2(a, c) UID_CAT1(a,c)
#define UNIQUE_ID UID_CAT2(UID_PREFIX, __LINE__)

#ifdef _DEBUG
#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
#define ASSERT_INVARIANT(pred)    static void UNIQUE_ID() { COMPILE_TIME_ASSERT(pred) }
#else
#define COMPILE_TIME_ASSERT(pred)
#define ASSERT_INVARIANT(pred)
#endif

// Templates to assist in validating pointers:
// Have to use these stubs so we don't have to include windows.h here.
void _AssertValidReadPtr(void *ptr, int count = 1);
void _AssertValidWritePtr(void *ptr, int count = 1);
void _AssertValidReadWritePtr(void *ptr, int count = 1);

 void AssertValidStringPtr(const char *ptr, int maxchar = 0xFFFFFF);
template<class T> inline void AssertValidReadPtr(T *ptr, int count = 1)      { _AssertValidReadPtr((void *)ptr, count); }
template<class T> inline void AssertValidWritePtr(T *ptr, int count = 1)     { _AssertValidWritePtr((void *)ptr, count); }
template<class T> inline void AssertValidReadWritePtr(T *ptr, int count = 1) { _AssertValidReadWritePtr((void *)ptr, count); }

#define AssertValidThis() AssertValidReadWritePtr(this, sizeof(*this))

// Macro to protect functions that are not reentrant
#ifdef _DEBUG
class CReentryGuard
{
public:
	CReentryGuard(int *pSemaphore)
		: m_pSemaphore(pSemaphore)
	{
		++(*m_pSemaphore);
	}
	~CReentryGuard()
	{
		--(*m_pSemaphore);
	}

private:
	int *m_pSemaphore;
};

#define ASSERT_NO_REENTRY()          \
	static int fSemaphore##__LINE__; \
	Assert(!fSemaphore##__LINE__);   \
	CReentryGuard ReentryGuard##__LINE__(&fSemaphore##__LINE__)
#else // _DEBUG
#define ASSERT_NO_REENTRY()
#endif // _DEBUG

// Inline string formatter
class CDbgFmtMsg
{
public:
	CDbgFmtMsg(const char *pszFormat, ...)
	{
		va_list arg_ptr;
		va_start(arg_ptr, pszFormat);
		_vsnprintf(m_szBuf, sizeof(m_szBuf) - 1, pszFormat, arg_ptr);
		va_end(arg_ptr);
		m_szBuf[sizeof(m_szBuf) - 1] = '\0';
	}

	operator const char *() const
	{
		return m_szBuf;
	}

private:
	char m_szBuf[256];
};

// Embed debug info in each file.
//#ifdef _WIN32
//#ifdef _DEBUG
//#pragma comment(compiler)
//#pragma comment(exestr,"*** DEBUG file detected, Last Compile: " __DATE__ ", " __TIME__ " ***")
//#endif
//#endif