/*****************************************************************************
 * $Id: gfx_funcs.hh,v 1.28 2006/08/20 16:24:43 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Various small functions: function macros, inline functions, template functions.
 * @remarks	Include this before gfx_types.hh.
 *          Writing "inline" in template functions does increase speed (gcc 3.4).
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#ifndef GFX_FUNCS_HH
#define GFX_FUNCS_HH 1

namespace gfx {

////////////////////////////////////////////////////////////////////////////////
/////////////////////////// function macros ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Min()/Max() is strongly recommended and faster if either arg is a computation,
// but requires both args to be the same type.
// Macro MIN()/MAX() lets the compiler automatically convert args of different types,
// but be warned: has the well-known side-effect of compiling each arg twice!
template<typename T> inline T Min( T a, T b ) { return (a < b) ? a : b; }
template<typename T> inline T Max( T a, T b ) { return (a > b) ? a : b; }
#ifndef MIN
#define MIN(A,B) ( ((A) < (B)) ? (A) : (B) )
#endif
#ifndef MAX
#define MAX(A,B) ( ((A) > (B)) ? (A) : (B) )
#endif

#ifndef STREQ
#define STREQ(A,B) ( strcmp((A),(B)) == 0 )
#endif
#ifndef STRNE
#define STRNE(A,B) ( strcmp((A),(B)) != 0 )
#endif
#ifndef STREMPTY
#define STREMPTY(S) ( ((S) == NULL) || ((S)[0] == '\0') )
#endif
#ifndef MEMEQ
#define MEMEQ(S1,S2,LEN) ( memcmp((S1),(S2),(LEN)) == 0 )
#endif
#ifndef MENNE
#define MEMNE(S1,S2,LEN) ( memcmp((S1),(S2),(LEN)) != 0 )
#endif
#define ELEMS(ARRAY) ((uint)( sizeof((ARRAY)) / sizeof((ARRAY)[0]) ))

/// True if axis is 0,1,2.
#define CHECK_AXIS( AXIS ) ( ((uint)(AXIS)) < 3 )

/// True if matrix index is in range 0..15.
#define CHECK_MATRIX_IDX( IDX ) ( ((IDX) & ~15) == 0 )

/// Nullifying a pointer after deleting can avoid memory corruption.
#define DELETE_NULL( OBJ )			delete   (OBJ), (OBJ) = NULL
#define DELETE_ARR_NULL( OBJ )		delete[] (OBJ), (OBJ) = NULL

/// Cope with floating-point inaccuracy.
/// Return true if two floats are approximately equal.
#define FEQ( A, B )				( ABS((A) - (B)) < 0.001 )
#define FNE( A, B )				( ABS((A) - (B)) > 0.001 )
#define FEQ_FLOAT_LARGE( A )	( ABS((A) - FLOAT_LARGE) < 100.0 )
#define FEQ_FLOAT_SMALL( A )	( ABS((A) - FLOAT_SMALL) < 100.0 )

/// Helper for writing operator<() to be used with STL.
/// If equal, doesn't return.
/// Uses only operator<() (operator>() isn't normally overridden for STL).
#define RETURN_LT_TRUE_GT_FALSE( X, Y )			\
{												\
	if ( (X) < (Y) ) return true;				\
	if ( (Y) < (X) ) return false;				\
}

/// @def BUSY_BEGIN
/// A busy flag is used to prevent possible infinite recursion
/// in case function A calls B and B calls A.
#ifndef BUSY_BEGIN
#define BUSY_BEGIN  static int busy = 0; if ( busy ) return; ++busy;
#define BUSY_BEGIN_ASSERT static int busy = 0; ASSERT_OR_RETURN( ! busy ); ++busy;
#define BUSY_END	--busy;
#endif // BUSY_BEGIN

/// @def likely
/// Give branch hint to compiler.
#if __GNUC__ >= 3
#define likely(x)	__builtin_expect((x),1)
#define unlikely(x)	__builtin_expect((x),0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#define EX(x) likely((x))	///< abbrev for expected
#define UX(x) unlikely((x))	///< abbrev for unexpected

/// @def PACKMEMB
/// Tell compiler to pack a struct member.
#if __GNUC__
#define PACKMEMB __attribute__ ((packed))
#else
#define PACKMEMB
#endif

/// @def UNUSED
/// Tell compiler a var is unused.
#if GCC_VERSION > 0 && GCC_VERSION >= 30100 // if gcc-3.1 or later
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif

/// Macros that only expand code according to a #define
#if DEBUG
#define DEBUG_CODE( CODE ) CODE
#else
#define DEBUG_CODE( CODE )
#endif

#if DEBUG == 2
#define DEBUG2_CODE( CODE ) CODE // pedantic excruciatingly slow debug code
#else
#define DEBUG2_CODE( CODE )
#endif

#if DEBUG && VERBOSE
#define DEBUG_VERBOSE_CODE( CODE ) CODE
#else
#define DEBUG_VERBOSE_CODE( CODE )
#endif

// C++ stream field formatting.
#if GCC_VERSION > 0 && GCC_VERSION < 30000
#define GFX_OMANIP_FIELD(CHARS) std::setw(CHARS)
#else
#define GFX_OMANIP_FIELD(CHARS) std::setw(CHARS) << std::right
#endif

// Prevent copying an object.  Put these in the private section of a class.
// The copy methods are prototypes without definitions, so usage causes a linker error.
#define PREVENT_COPYING( CLASS )                    \
private:                                            \
    CLASS( const CLASS& );                          \
    CLASS& operator=( const CLASS& );

// These are essentially comment to indicate a class is copyable.
#define COPYABLE( CLASS )          // means class is copyable by user-defined copying methods
#define COPYABLE_DEFAULT( CLASS )  // means class is copyable by compiler-generated copying methods

////////////////////////////////////////////////////////////////////////////////
/////////////////////////// inline functions ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/// Clear memory
static inline void
CLEAR( void* p, uint n )
{
	memset( p, 0, n );
}

/// Absolute functions.  Useful to abstract types and avoid type-conversion.
#if ! defined(__CYGWIN__)
// Faster if these are compiler-intrinsic functions.
// gcc 3/4 compiles the same i686 code for both sets.
// *abs*() are supported by gcc 2.9.
static inline int         ABS( int         x ) { return abs(x);   }
static inline long int    ABS( long int    x ) { return labs(x);  }
static inline float       ABS( float       x ) { return fabsf(x); } // C99
static inline double      ABS( double      x ) { return fabs(x);  }
static inline long double ABS( long double x ) { return fabsl(x); } // C99
#else
static inline int         ABS( int         x ) { return (x < 0) ? -x : x; }
static inline long int    ABS( long int    x ) { return (x < 0) ? -x : x; }
static inline float       ABS( float       x ) { return (x < 0) ? -x : x; }
static inline double      ABS( double      x ) { return (x < 0) ? -x : x; }
static inline long double ABS( long double x ) { return (x < 0) ? -x : x; }
#endif

/// Force a value negative.  Opposite of absolute.
static inline int         NEG( int         x ) { return (x < 0) ? x : -x; }
static inline long int    NEG( long int    x ) { return (x < 0) ? x : -x; }
static inline float       NEG( float       x ) { return (x < 0) ? x : -x; }
static inline double      NEG( double      x ) { return (x < 0) ? x : -x; }
static inline long double NEG( long double x ) { return (x < 0) ? x : -x; }

static inline bool
IfPow2( int n )
{
	// Trick: result is 0 if n is a power of 2.
    return (n & (n-1)) == 0;
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////// template functions ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
// Array template functions:
// - NewArray() applies to CREATING NEW arrays.
// - InitArray(), CopyArray() apply to EXISTING arrays
//------------------------------------------------------------------------------

#define DeleteArray( OBJ ) delete[] (OBJ), (OBJ) = NULL

////////////////////////////////////////////////////////////////////////////////
/// @brief Helper template function to copy an array into another.
/// Applies to existing arrays.
template<typename T>
inline void
CopyArray( T* dest, const T* src, const uint elemCnt )
{
	memcpy( dest, src, elemCnt * sizeof(T) );
}

////////////////////////////////////////////////////////////////////////////////
/// @brief Helper template function to initialize (assign) an array.
/// Applies to an existing array.
/// ######################################################
/// ## DO NOT PASS sizeof(array) WHICH IS A BYTE COUNT! ##
/// ## Use the macro ELEMS(ARRAY).                      ##
/// ######################################################
template<typename T>
inline void
InitArray( T* arr, const uint elemCnt, T val )
{
	for ( uint i = 0; i < elemCnt; ++i ) arr[i] = val;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief Helper template function to create a new copy of an existing array.
/// Applies to a new array.  Array should be freed using delete[].
template<typename T>
inline T*
NewArray( const T* src, const uint elemCnt )
{
	T* copy = new T[elemCnt];
	CopyArray<T>( copy, src, elemCnt );
	return copy;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief Helper template function to create a new array and initialize all elements.
/// Applies to a new array.  Array should be freed using delete[].
template<typename T>
inline T*
NewArray( const uint elemCnt, T val )
{
	T* arr = new T[elemCnt];
	InitArray<T>( arr, elemCnt, val );
	return arr;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief Template class for temporary arrays.
/// The array is automatically reallocated to handle Get() requests.
/// Instantiate only one global object for each type (multiple objects would waste memory).
/// Unlike a STL vector, reallocation doesn't copy values.
template<typename T>
class TempArray
{
public:
	TempArray( void )
	{
		mElems = 500;
		mArray = new T[mElems];
	}

	/// @param elems Initial amount of elements.
	TempArray( uint elems )
	{
		mElems = elems;
		mArray = new T[mElems];
	}

	~TempArray()
	{
		delete [] mArray;
	}

	/// @return A temporary array with at least this amount of elements.
	T* Get( uint elems )
	{
		if ( EX(elems < mElems) )
		{
			// The existing array is sufficient.
			return mArray;
		}
		else
		{
			// Allocate a new larger array.
			delete [] mArray;
			mElems = elems << 1;	// extra to reduce reallocations
			mArray = new T[mElems];
			return mArray;
		}
	}

private:
	T*		mArray;
	uint	mElems;
};

/*****************************************************************************
 * Little helpers.
 *****************************************************************************/
template<typename T>
inline bool
IfInRect( T x, T y, T xmax, T ymax )
{
	return    x >= 0
           && y >= 0
           && x <= xmax
           && y <= ymax;
}
template<typename T>
inline bool
IfInRect( T x, T y, T xmin, T ymin, T xmax, T ymax )
{
	return    x >= xmin
           && y >= ymin
           && x <= xmax
           && y <= ymax;
}

/*****************************************************************************
 * Swap.
 *****************************************************************************/
template<typename T>
inline void
Swap( T& a, T& b )
{
	T a2 = a;
	a = b;
	b = a2;
}

} // namespace gfx

#endif // GFX_FUNCS_HH
