/******************************************************************************
 * $Id: gfx_misc.cc,v 1.56 2006/11/02 02:23:03 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Misc functions.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 ******************************************************************************/

#define GFX_MISC_CC 1
#include "gfx.hh"
#include "gfx_stream.hh"
#include "gfx_rgb.hh"
#include "gfx_gfxsys.hh"
#include "gfx_math.hh"
namespace gfx {

/*****************************************************************************
 * Draw 2D rectangle.
 * @pre Must be called only between GFX_BEGIN_2D() and GFX_END_2D().
 *****************************************************************************/
void
DrawRect2D( const Rect<int>& rect, const RGBA rgba )
{
	// Geometry for GFX_BEGIN_2D() is relative to the viewport (not the window).
	GFX_RGBA( rgba );
	GFX_BEGIN( GP_POLYGON );
	GFX_VERTEX_2D( rect.x1, rect.y1 ); // NW
	GFX_VERTEX_2D( rect.x1, rect.y2 ); // SW
	GFX_VERTEX_2D( rect.x2, rect.y2 ); // SE
	GFX_VERTEX_2D( rect.x2, rect.y1 ); // NE
	GFX_END();
}

/*****************************************************************************
 * Safely process a cmd-line arg.
 * @return Zero if arg idx is invalid.
 *****************************************************************************/
int
Argi( int idx, int argc_, char** argv_ )	// careful, "argv" is a global var in C
{
	if ( idx >= argc_ ) return 0;
	return (int) strtol( argv_[idx], NULL, 0 ); // base=0 to convert decimal/hex/octal
}
float
Argf( int idx, int argc_, char** argv_ )
{
	if ( idx >= argc_ ) return 0;
	return atof( argv_[idx] );
}
string
Argstr( int idx, int argc_, char** argv_ )
{
	if ( idx >= argc_ ) return string( "" );
	return string( argv_[idx] );
}

/*****************************************************************************
 * Functions for marking a command-line arg as "done".
 * For helping to catch unrecognized args.
 * @param	i
 *			argv[i]
 * @param	n
 *			How many consecutive args are "done" (default n=1).
 *****************************************************************************/
static bool sCmdargDone[100];
void
CmdargDone( int i, int n )
{
	for ( int j = 0; j < n; ++j ) sCmdargDone[i+j] = true;
}
bool
IfCmdargDone( int i )
{
	return sCmdargDone[i];
}

/*****************************************************************************
 * Test casting Vector3/Vertex* and fp[3].
 * @remarks
 * REGARDLESS OF THIS CHECK PASSING, CASTING SHOULD BE AVOIDED AND RESTRICTED
 * TO GFX_VERTEX() BECAUSE CASTING WILL CAUSE TYPESIG CHECKS TO FAIL!
 *****************************************************************************/
template<typename V3>
bool
SelfCheckVertexFp( const string& typeName )
{
	// Check that casting fp[3] and Vertex are equivalent.
	V3 v;
	fp ar[3];
	v.x = ar[0] = 123456.0;
	v.y = ar[1] = 5.6;
	v.z = ar[2] = -789.0;
    if (    (reinterpret_cast<V3*>(ar))->x != v.x || (reinterpret_cast<fp*>(&v))[XX] != ar[XX]
         || (reinterpret_cast<V3*>(ar))->y != v.y || (reinterpret_cast<fp*>(&v))[YY] != ar[YY]
         || (reinterpret_cast<V3*>(ar))->z != v.z || (reinterpret_cast<fp*>(&v))[ZZ] != ar[ZZ]
       )
	{
		*gStreamError << "ERROR: struct " << typeName << " != fp[3]"				<< endl
				      << "Compiling with ALTVERTEX=1 is required on this system."	<< endl
				      << "make ALTVERTEX=1"											<< endl;
		return false;
	}
	return true;
}

#if 0 // DEPRECATED
template<typename V3>
bool
SelfCheckVertexGetFP( const string& typeName )
{
	// Check that the fp[3] returned by Vertex.GetFP() is equivalent to .x/.y/.z.
	V3 v;
	v.x = 395.2;
	v.y = 891.3;
	v.z = 113.5;
	fp* arr = v.GetFP();
	if (    v.x != arr[0]
         || v.y != arr[1]
         || v.z != arr[2] )
	{
		*gStreamError << "ERROR: " << typeName << ".GetFP() != fp[3]"				<< endl
				      << "Compiling with ALTVERTEX=1 is required on this system."	<< endl
				      << "make ALTVERTEX=1"											<< endl;
		return false;
	}
	return true;
}
#endif

/*****************************************************************************
 * Self-check.  An executable's main() should call this and exit if this fails.
 * Ensure assumptions of this code hold on this platform.
 *****************************************************************************/
bool
GFX_SELF_CHECK( void )
{
	// Return false immediately if any check fails to stop program.

	if ( sizeof(int) < 4 )
	{
		*gStreamError << "ERROR: sizeof(int) < 4" << endl;
		return false;
	}

	// In case future code assumes uint16 is exactly 2 bytes.
	if ( sizeof(int16)  != 2
      || sizeof(uint16) != 2 )
	{
		*gStreamError << "ERROR: sizeof(int16) != 2" << endl;
		return false;
	}

	// RGBA code assumes uint32 is exactly 4 bytes.
	// Assumption could break on a CPU with a 64-bit word.
	if ( sizeof(int32)  != 4
      || sizeof(uint32) != 4 )
	{
		*gStreamError << "ERROR: sizeof(int32) != 4" << endl;
		return false;
	}

#if GFXSYS_OPENGL
	if ( sizeof(float) != sizeof(GLfloat) )
	{
		*gStreamError << "ERROR: sizeof(float) != sizeof(GLfloat)" << endl;
		return false;
	}
	if ( sizeof(double) != sizeof(GLdouble) )
	{
		*gStreamError << "ERROR: sizeof(double) != sizeof(GLdouble)" << endl;
		return false;
	}
#endif

#if ! ALTVERTEX
	// REGARDLESS OF THIS SELF-CHECKING PASSING, CASTING FROM WorldVertex/EyeVertex
	// TO fp[3] SHOULD BE AVOIDED AND RESTRICTED TO GFX_VERTEX() BECAUSE CASTING
	// MIGHT CAUSE TYPESIG CHECKS TO FAIL!!
	if (    ! SelfCheckVertexFp   <Vector3>(      "Vector3"      )
	     || ! SelfCheckVertexFp   <WorldVertex>(  "EyeVertex"    )
	     || ! SelfCheckVertexFp   <WorldVertex>(  "WorldVertex"  )
#if 0 // DEPRECATED
	     || ! SelfCheckVertexGetFP<Vector3>(      "Vector3"      )
	     || ! SelfCheckVertexGetFP<LocalVertex>(  "LocalVertex"  )
	     || ! SelfCheckVertexGetFP<EyeVertex>(    "EyeVertex"    )
	     || ! SelfCheckVertexGetFP<WorldVertex>(  "WorldVertex"  )
         || ! SelfCheckVertexGetFP<NormalVertex>( "NormalVertex" )
#endif
       )
		return false;
#endif

	// Warn about unpacked structs (packed structs conserve memory).
	DEBUG_CODE( if ( sizeof(Dlink<int>) != sizeof(void*)*3 + 1 )
                    *gStreamDebug << "structs that ideally should be packed aren't (ok though) \n"; )

	// Test CLEAR_BIT_FIELD()'s assumption that a bitfield is equivalent to unsigned int.
	struct Bitfield
	{
		unsigned int b0 : 1;
		unsigned int b1 : 4;
		unsigned int b2 : 16;
	};
	Bitfield bitfield;
	if ( sizeof(bitfield) != sizeof(unsigned int) )
	{
		*gStreamError << "ERROR: sizeof(bitfield) != sizeof(unsigned int)" << endl;
		return false;
	}

	// Ok.
	return true;
}

#if DEBUG
/*****************************************************************************
 * Handler for when an assertion fails.
 *****************************************************************************/
void
GfxAssertHandler( bool cond, const char* fileName, int fileLine )
{
	if ( UX( ! cond ) )
	{
		cerr << "** ASSERTION FAILED **" << endl
             << "** " << fileName << ':' << fileLine << " **" << endl;

		abort();
	}
}
// global that is a func ptr
void (*gGfxAssertHandler)(bool cond, const char* fileName, int fileLine) = GfxAssertHandler;
void GFX_SET_ASSERT_HANDLER( void(*func)(bool cond, const char* fileName, int fileLine) )
{
	gGfxAssertHandler = func;
}
#endif // DEBUG

} // namespace gfx
