#ifndef __DEBUG_H__
#define __DEBUG_H__

//	Abstract:	This file provides the following macros:
//
//				ASSERT(p) - If p is false and DEBUG is true drops into the 
//				debugger, if ASSERTS_THROW is true throws a XAssertException exception.
//
//				VERIFY(p) - Like ASSERT except that p is evaluated in release builds.
//
//				REQUIRE(p) - Like VERIFY except that it exits the app if the test fails
//				(this is needed because the boot strap code can't safely throw).
//
//				COMPILE_CHECK(p) - An ASSERT that fires at compile time. This is useful
//				for doing things like verifying the layout of items within a struct.
//				Note that this check is also done in release builds.
//
//				DEBUGSTR(formatStr, n1, n2, ...) - Drops into the debugger and
//				displays a string.
//
//				TRACE(formatStr, n1, n2, ...) - Writes a string to a debug window
//				without suspending the program.
//
//				TRACEFLOW(category, formatStr, n1, n2, ...) - Like TRACE, but with 
//				the addition of a category string. The category string allows users 
//				to turn off entire categories of messages. Note that DEBUGSTR, TRACE, 
//				and TRACEFLOW may be used with boolean expressions by tacking an _IF 
//				on the end (eg DEBUGSTR_IF(x < 0, "Sqrt was passed a negative argument.")).
//
//				PRECONDITION(p)  - Used in conjunction with POSTCONDITION to verify 
//				the integrity of an object: each public method should include one call to 
//				PRECONDITION and another to POSTCONDITION. The idea here is to check to 
//				see if the object stays in a valid state given valid arguments. Note that 
//				these macros call a method named Invariant. This method should use ASSERT's 
//				to check to see if the object is in a sane state. See ZInvariant.h for
//				more details.
//
//				CHECK_INVARIANT() - Calls the Invariant. This is useful in ctors because
//				PRECONDITION doesn't call the dtor (PRECONDITION constructs an object on
//				the stack which calls the Invariant when the function exits).

#pragma once      // Open only once per build

#define RUNTIME_EXPORT 

RUNTIME_EXPORT std::wstring ToStr(bool value);				// I originally used iostreams, but that caused a ridiculous amount of bloat in object files (but much less so in the exe)
RUNTIME_EXPORT std::wstring ToStr(char value);
inline 		   std::wstring ToStr(wchar_t value)						{return std::wstring(1, value);}
RUNTIME_EXPORT std::wstring ToStr(int16_t value, int32_t fieldWidth = 1);
RUNTIME_EXPORT std::wstring ToStr(uint16_t value, int32_t fieldWidth = 1);
RUNTIME_EXPORT std::wstring ToStr(int32_t value, int32_t fieldWidth = 1);
RUNTIME_EXPORT std::wstring ToStr(uint32_t value, int32_t fieldWidth = 1);
RUNTIME_EXPORT std::wstring ToStr(float value, int32_t precision = 6);
RUNTIME_EXPORT std::wstring ToStr(double_t value, int32_t precision = 6);
RUNTIME_EXPORT std::wstring ToStr(const char* value);
inline 		   std::wstring ToStr(const wchar_t* value)					{return value;}
RUNTIME_EXPORT std::wstring ToStr(const std::string& value);
inline 		   std::wstring ToStr(const std::wstring& value)			{return value;}


#ifdef _DEBUG
	#define	DEBUG					1
#else		
	#define	DEBUG					0
	
	#if	!defined(NDEBUG)
		#define	NDEBUG							// used by <assert.h>
	#endif
#endif

#ifndef ASSERTS_THROW
#define ASSERTS_THROW				0			
#endif
// #define ASSERTS_THROW               1
// Synch with ANSI definitions.
#if DEBUG && defined(NDEBUG)
	#error DEBUG and NDEBUG are out of sync!
#endif

#if !DEBUG && !defined(NDEBUG)
	#error DEBUG and NDEBUG are out of sync!
#endif

// Synch with MSVC.
#if _MSC_VER && DEBUG != defined(_DEBUG)
	#error DEBUG and _DEBUG are out of sync!
#endif

#ifndef _lint
	#define COMPILE_CHECK(p)	{struct _CC {char a[(p) ? 1 : -1];};} (void) 0
#else
	#define COMPILE_CHECK(p)					
#endif

#if DEBUG
	
	void AssertFailed(const char* expr, const char* file, int line);
	
	#undef assert

	#define ASSERT(p)					do {if (!(p)) AssertFailed(#p, __FILE__, __LINE__);} while (false)

	#define ASSERT_IF(p, x)				do {if ((p) && !(x)) AssertFailed(#x, __FILE__, __LINE__);} while (false)
	#define assert(p) 					ASSERT(p)
	#define VERIFY(p)					ASSERT(p)
	#define REQUIRE(p)					ASSERT(p)

	#define	PRECONDITION(p)				ASSERT(p)
	#define	POSTCONDITION(p)			ASSERT(p)

    #ifdef NO_NESTED_QUOTES
          #define PRECONDITION2(a,b)	ASSERT(a)
          #define POSTCONDITION2(a,b)	ASSERT(a)
    #else
          #define PRECONDITION2(a,b)	ASSERT((b, (a) !=0))
          #define POSTCONDITION2(a,b)	ASSERT((b, (a) !=0))
    #endif

#else	// #if DEBUG


	#if ASSERTS_THROW
		void AssertFailed(const char*, const char*, int);

		#define ASSERT(p)				do {if (!(p)) AssertFailed(#p, __FILE__, __LINE__);} while (false)

		#define ASSERT_IF(p, x)			do {if ((p) && !(x)) AssertFailed(#x, __FILE__, __LINE__);} while (false)
		#define VERIFY(p)				ASSERT(p)

		#define	PRECONDITION(p)			ASSERT(p)
		#define	POSTCONDITION(p)		ASSERT(p)

        #define PRECONDITION2(a,b)	    ASSERT(a)
        #define POSTCONDITION2(a,b)	    ASSERT(a)
	#else
		#define	ASSERT(p)				((void) 0)

		#define	ASSERT_IF(p, x)			((void) 0)
		#define VERIFY(p)				do {if (p) 0;} while (false)

		#define	PRECONDITION(p)			((void) 0)
		#define	POSTCONDITION(p)		((void) 0)

        #define PRECONDITION2(a,b)
        #define POSTCONDITION2(a,b)
	#endif

	void RequireFailed(const char*, const char*, int);
	
	#define REQUIRE(p)					do {if (!(p)) RequireFailed(#p, __FILE__, __LINE__);} while (false)


#endif	// DEBUG

#ifdef _CONSOLE
#define TRACE
#endif
	


#endif /* __DEBUG_H__ */
