/* AbiSource Program Utilities
 * Copyright (C) 1998 AbiSource, Inc.
 * 
 * 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.
 */
 
#ifndef UT_ASSERT_H
#define UT_ASSERT_H

// TODO move these declarations into platform directories.

#if (defined (WIN32) || defined (_WIN32) || defined (_WIN64))
// The 'cool' win32 assert, at least with VC6, corrups memory (probably calling sprintf on
// a static buffer without checking bounds), so we implement our own assert dialog, which
// is even cooler. TF

#ifdef NDEBUG
#  define UT_ASSERT(x)
#else
// This function is implemented in ut_Win32Misc.cpp. It is not declared in any header file (it is
// only to be referenced from here and we want to reduce the files we include here to a
// bare minimum for performance reasons, as this file gets included from pretty much
// everywhere).
extern int UT_Win32ThrowAssert(const char * pCondition, const char * pFile, int iLine, int iCount);

// The best way of stepping into debugger is by generating the appropriate interupt
// instruction, so if we are on Intel x86, we will issue int 3.  If we are not on x86, we
// will use the win32 DebugBreak() function (the disadvantage of that is that the
// execution is interupted not in our code, but in one of the system libs, so you have to
// step out of that system function to get into our code; its rather non-intuitive.

#  undef UT_DEBUG_BREAK

// I am not sure which is the standard GCC macro for the x86 platform; we are supposed to
// be able to get all the predefined macros by running 'cpp -dM' but it would not work on
// my cygwin
#  if defined(__GNUC__) && (defined(_X86) || defined(__i386) || defined(i386))
     // Inline assembly for GCC on x86
#    define UT_DEBUG_BREAK asm("int3");
#  elif defined(__GNUC__) && (defined(__ia64) || defined(ia64))
#    error "This branch has not been tested."
     // On Itanium we use the intrinsic function __break(); defined in ia64intrin.h
     // I am not sure whether we need to tell the compiler this one is intrinsic (MSVC
     // uses a #pragma for this
     void __break(int);
#    define UT_DEBUG_BREAK __break(0x80016);
#  elif defined(_MSC_VER) && defined(_M_IX86)
     // inline assmebly for MSVC on x86
#    define UT_DEBUG_BREAK _asm {int 3}
#  elif defined(_MSC_VER) && (defined(_M_IA64) || defined(_M_AMD64))
#    error "This branch has not been tested."
     // On Itanium we use the intrinsic function __break();
     // I assume this will also work for AMD64, but I am not 100% sure
     void __break(int);
     // this forces __break() to be generated as inline code (see MSDN)
#    pragma intrinsic (__break) 
#    define UT_DEBUG_BREAK __break(0x80016);
#  endif

# ifndef UT_DEBUG_BREAK
// Some compiler/architecture for which we do not know how to generate inline assembly to pass
// control to the debugger; we use win32s DebugBreak().
// 
// TODO !!! This currently does not work; if someone one day wants to build AW on non-x86
// win32 architecture, they will need to fix this (including <windows.h> from here, which
// is what would work, screws up things in MS Word importer, because the wv library
// redefines some of the win32 structures; but we probably do not want to include
// windows.h from here anyway because of the overhead -- ut_assert.h gets included in
// almost everything.). Really, we want to do something similar as we do for x86.
#  error "Proper implementation needed for this compiler/architecture"

#    ifndef _WINBASE_
       __declspec(dllimport) void __stdcall DebugBreak(void);
#    endif /* ifdef _WINBASE_ */

#    define UT_DEBUG_BREAK DebugBreak();
#  endif /* ifndef UT_DEBUG_BREAK */

// We want to track the number of times we have been through this assert and have the
// option of disabling this assert for the rest of the session; we use the __iCount and
// __bOnceOnly vars for this (this adds a few bytes to the code and footprint, but on
// large scale of things, this is quite negligible for the debug build).
#define UT_ASSERT(x)                                                        \
{                                                                           \
	static bool __bOnceOnly = false;                                        \
	static long __iCount = 0;                                               \
	if(!__bOnceOnly && !(x))                                                \
	{                                                                       \
		__iCount++;                                                         \
		int __iRet = UT_Win32ThrowAssert(#x,__FILE__, __LINE__, __iCount);  \
        if(__iRet == 0)                                                     \
		{                                                                   \
		   UT_DEBUG_BREAK                                                   \
		}                                                                   \
		else if(__iRet < 0)                                                 \
		{                                                                   \
			__bOnceOnly = true;                                             \
		}                                                                   \
	}                                                                       \
}

#endif // ifdef NDEBUG

#elif defined(__QNX__)
	// A QNX variant.
#	ifdef NDEBUG
		// When NDEBUG is defined, assert() does nothing.
		// So we let the system header files take care of it.
#		include <assert.h>
#		define UT_ASSERT assert
#	else
		// Otherwise, we want a slighly modified behavior.
		// We'd like assert() to ask us before crashing.
		// We treat asserts as logic flaws, which are sometimes
		// recoverable, but that should be noted.
#		include <assert.h>
// Please keep the "/**/" to stop MSVC dependency generator complaining.
#		include /**/ "ut_qnxAssert.h"
#		define UT_ASSERT(expr)			\
				((void) ((expr) ||	\
				(UT_QNXAssertMsg(#expr,\
				 __FILE__, __LINE__),	\
				 0)))
#	endif
/* above only useful on Carbon target if build as Mach-O. CFM use alert and Cocoa use UNIX */
#elif (defined(XP_MAC_TARGET_CARBON) && XP_MAC_TARGET_CARBON) && (!defined(CARBON_ON_MACH_O) || (CARBON_ON_MACH_O == 0)) // Carbon on Mach-O as UNIX

#     ifdef NDEBUG
              // When NDEBUG is defined, assert() does nothing.
              // So we let the system header files take care of it.
#             include <assert.h>
#             define UT_ASSERT assert
#   else
              // Otherwise, we want a slighly modified behavior.
              // We'd like assert() to ask us before crashing.
              // We treat asserts as logic flaws, which are sometimes
              // recoverable, but that should be noted.

              // On MacOS this requires toolbox to be initialized. Otherwise, 
              // expect MacBug or a crash if MacBug is not here.

#             include <assert.h>
// Please keep the "/**/" to stop MSVC dependency generator complaining.
#             include /**/ "ut_MacAssert.h"
#             define UT_ASSERT(expr)                  \
                      ((void) ((expr) ||      \
                      (UT_MacAssertMsg(#expr,\
                       __FILE__, __LINE__),   \
                       0)))
#   endif

#else

	// A Unix variant, possibly Gnome.

#	ifdef NDEBUG

		// When NDEBUG is defined, assert() does nothing.
		// So we let the system header files take care of it.
#       if defined(XP_TARGET_COCOA)
// Please keep the "/**/" to stop MSVC dependency generator complaining.
#			include /**/ "xap_CocoaAssert.h"
#			define UT_ASSERT(expr)								\
			((void) ((expr) ||								\
				(XAP_CocoaAssertMsg(#expr,					\
								  __FILE__, __LINE__),		\
				 0)))

#       else
#			include <assert.h>
#			define UT_ASSERT assert
#		endif
#	else
		// Otherwise, we want a slighly modified behavior.
		// We'd like assert() to ask us before crashing.
		// We treat asserts as logic flaws, which are sometimes
		// recoverable, but that should be noted.

#		include <assert.h>
// Please keep the "/**/" to stop MSVC dependency generator complaining.
#		include /**/ "ut_unixAssert.h"
#		define UT_ASSERT(expr)								\
			((void) ((expr) ||								\
				(UT_UnixAssertMsg(#expr,					\
								  __FILE__, __LINE__),		\
				 0)))
#	endif

#endif


/*!
 * This part is not implemented. Same as UT_TODO
 */
#define UT_NOT_IMPLEMENTED		0

/*!
 * This really shouldn't happen
 */
#define UT_SHOULD_NOT_HAPPEN	0

/*!
 * This part is left TODO
 */
#define UT_TODO					0

/*!
 * This line of code should not be reached
 */
#define UT_NOT_REACHED 0

/*!
 * This line of code should not be reached
 */
#define UT_ASSERT_NOT_REACHED() UT_ASSERT(UT_NOT_REACHED)

/*!
 * Trigger a debug assertion, but let the normal flow of code progress
 */
#define UT_ASSERT_HARMLESS(cond) UT_ASSERT(cond)

/*!
 * Just return from a function if this condition fails
 */
#define UT_return_if_fail(cond) if (!(cond)) { UT_ASSERT(cond); return; }

/*!
 * Just return this value from a function if this condition fails
 */
#define UT_return_val_if_fail(cond, val) if (!(cond)) { UT_ASSERT(cond); return (val); }

/*!
 * Throw if this condition fails
 */
#define UT_throw_if_fail(cond, val) if (!(cond)) { UT_ASSERT(cond); throw val; }

#endif /* UT_ASSERT_H */
