/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code 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 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "precompiled.h"
#pragma hdrstop

/*
===============================================================================

	idLib

===============================================================================
*/

idSys* 			idLib::sys			= NULL;
idCommon* 		idLib::common		= NULL;
idCVarSystem* 	idLib::cvarSystem	= NULL;
idFileSystem* 	idLib::fileSystem	= NULL;
int				idLib::frameNumber	= 0;
bool			idLib::mainThreadInitialized = 0;
ID_TLS			idLib::isMainThread = 0;

char idException::error[2048];

/*
================
idLib::Init
================
*/
void idLib::Init()
{

	assert( sizeof( bool ) == 1 );
	
	isMainThread = 1;
	mainThreadInitialized = 1;	// note that the thread-local isMainThread is now valid
	
	// initialize little/big endian conversion
	Swap_Init();
	
	// init string memory allocator
	idStr::InitMemory();
	
	// initialize generic SIMD implementation
	idSIMD::Init();
	
	// initialize math
	idMath::Init();
	
	// test idMatX
	//idMatX::Test();
	
	// test idPolynomial
#ifdef _DEBUG
	idPolynomial::Test();
#endif
	
	// initialize the dictionary string pools
	idDict::Init();
}

/*
================
idLib::ShutDown
================
*/
void idLib::ShutDown()
{

	// shut down the dictionary string pools
	idDict::Shutdown();
	
	// shut down the string memory allocator
	idStr::ShutdownMemory();
	
	// shut down the SIMD engine
	idSIMD::Shutdown();
}


/*
===============================================================================

	Colors

===============================================================================
*/

idVec4	colorBlack	= idVec4( 0.00f, 0.00f, 0.00f, 1.00f );
idVec4	colorWhite	= idVec4( 1.00f, 1.00f, 1.00f, 1.00f );
idVec4	colorRed	= idVec4( 1.00f, 0.00f, 0.00f, 1.00f );
idVec4	colorGreen	= idVec4( 0.00f, 1.00f, 0.00f, 1.00f );
idVec4	colorBlue	= idVec4( 0.00f, 0.00f, 1.00f, 1.00f );
idVec4	colorYellow	= idVec4( 1.00f, 1.00f, 0.00f, 1.00f );
idVec4	colorMagenta = idVec4( 1.00f, 0.00f, 1.00f, 1.00f );
idVec4	colorCyan	= idVec4( 0.00f, 1.00f, 1.00f, 1.00f );
idVec4	colorOrange	= idVec4( 1.00f, 0.50f, 0.00f, 1.00f );
idVec4	colorPurple	= idVec4( 0.60f, 0.00f, 0.60f, 1.00f );
idVec4	colorPink	= idVec4( 0.73f, 0.40f, 0.48f, 1.00f );
idVec4	colorBrown	= idVec4( 0.40f, 0.35f, 0.08f, 1.00f );
idVec4	colorLtGrey	= idVec4( 0.75f, 0.75f, 0.75f, 1.00f );
idVec4	colorMdGrey	= idVec4( 0.50f, 0.50f, 0.50f, 1.00f );
idVec4	colorDkGrey	= idVec4( 0.25f, 0.25f, 0.25f, 1.00f );

/*
================
PackColor
================
*/
dword PackColor( const idVec4& color )
{
	byte dx = idMath::Ftob( color.x * 255.0f );
	byte dy = idMath::Ftob( color.y * 255.0f );
	byte dz = idMath::Ftob( color.z * 255.0f );
	byte dw = idMath::Ftob( color.w * 255.0f );
	return ( dx << 0 ) | ( dy << 8 ) | ( dz << 16 ) | ( dw << 24 );
}

/*
================
UnpackColor
================
*/
void UnpackColor( const dword color, idVec4& unpackedColor )
{
	unpackedColor.Set( ( ( color >> 0 ) & 255 ) * ( 1.0f / 255.0f ),
					   ( ( color >> 8 ) & 255 ) * ( 1.0f / 255.0f ),
					   ( ( color >> 16 ) & 255 ) * ( 1.0f / 255.0f ),
					   ( ( color >> 24 ) & 255 ) * ( 1.0f / 255.0f ) );
}

/*
================
PackColor
================
*/
dword PackColor( const idVec3& color )
{
	byte dx = idMath::Ftob( color.x * 255.0f );
	byte dy = idMath::Ftob( color.y * 255.0f );
	byte dz = idMath::Ftob( color.z * 255.0f );
	return ( dx << 0 ) | ( dy << 8 ) | ( dz << 16 );
}

/*
================
UnpackColor
================
*/
void UnpackColor( const dword color, idVec3& unpackedColor )
{
	unpackedColor.Set( ( ( color >> 0 ) & 255 ) * ( 1.0f / 255.0f ),
					   ( ( color >> 8 ) & 255 ) * ( 1.0f / 255.0f ),
					   ( ( color >> 16 ) & 255 ) * ( 1.0f / 255.0f ) );
}

/*
===============
idLib::FatalError
===============
*/
void idLib::FatalError( const char* fmt, ... )
{
	va_list		argptr;
	char		text[MAX_STRING_CHARS];
	
	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );
	
	common->FatalError( "%s", text );
}

/*
===============
idLib::Error
===============
*/
void idLib::Error( const char* fmt, ... )
{
	va_list		argptr;
	char		text[MAX_STRING_CHARS];
	
	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );
	
	common->Error( "%s", text );
}

/*
===============
idLib::Warning
===============
*/
void idLib::Warning( const char* fmt, ... )
{
	va_list		argptr;
	char		text[MAX_STRING_CHARS];
	
	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );
	
	common->Warning( "%s", text );
}

/*
===============
idLib::WarningIf
===============
*/
void idLib::WarningIf( const bool test, const char* fmt, ... )
{
	if( !test )
	{
		return;
	}
	
	va_list		argptr;
	char		text[MAX_STRING_CHARS];
	
	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );
	
	common->Warning( "%s", text );
}

/*
===============
idLib::Printf
===============
*/
void idLib::Printf( const char* fmt, ... )
{
	va_list		argptr;
	va_start( argptr, fmt );
	if( common )
	{
		common->VPrintf( fmt, argptr );
	}
	va_end( argptr );
}

/*
===============
idLib::PrintfIf
===============
*/
void idLib::PrintfIf( const bool test, const char* fmt, ... )
{
	if( !test )
	{
		return;
	}
	
	va_list		argptr;
	va_start( argptr, fmt );
	common->VPrintf( fmt, argptr );
	va_end( argptr );
}

/*
===============================================================================

	Byte order functions

===============================================================================
*/

// can't just use function pointers, or dll linkage can mess up
static short( *_BigShort )( short l );
static short( *_LittleShort )( short l );
static int	( *_BigLong )( int l );
static int	( *_LittleLong )( int l );
static float( *_BigFloat )( float l );
static float( *_LittleFloat )( float l );
static void	( *_BigRevBytes )( void* bp, int elsize, int elcount );
static void	( *_LittleRevBytes )( void* bp, int elsize, int elcount );
static void ( *_LittleBitField )( void* bp, int elsize );
static void	( *_SixtetsForInt )( byte* out, int src );
static int	( *_IntForSixtets )( byte* in );

short	BigShort( short l )
{
	return _BigShort( l );
}
short	LittleShort( short l )
{
	return _LittleShort( l );
}
int		BigLong( int l )
{
	return _BigLong( l );
}
int		LittleLong( int l )
{
	return _LittleLong( l );
}
float	BigFloat( float l )
{
	return _BigFloat( l );
}
float	LittleFloat( float l )
{
	return _LittleFloat( l );
}
void	BigRevBytes( void* bp, int elsize, int elcount )
{
	_BigRevBytes( bp, elsize, elcount );
}
void	LittleRevBytes( void* bp, int elsize, int elcount )
{
	_LittleRevBytes( bp, elsize, elcount );
}
void	LittleBitField( void* bp, int elsize )
{
	_LittleBitField( bp, elsize );
}

void	SixtetsForInt( byte* out, int src )
{
	_SixtetsForInt( out, src );
}
int		IntForSixtets( byte* in )
{
	return _IntForSixtets( in );
}

/*
================
ShortSwap
================
*/
short ShortSwap( short l )
{
	byte    b1, b2;
	
	b1 = l & 255;
	b2 = ( l >> 8 ) & 255;
	
	return ( b1 << 8 ) + b2;
}

/*
================
ShortNoSwap
================
*/
short ShortNoSwap( short l )
{
	return l;
}

/*
================
LongSwap
================
*/
int LongSwap( int l )
{
	byte    b1, b2, b3, b4;
	
	b1 = l & 255;
	b2 = ( l >> 8 ) & 255;
	b3 = ( l >> 16 ) & 255;
	b4 = ( l >> 24 ) & 255;
	
	return ( ( int )b1 << 24 ) + ( ( int )b2 << 16 ) + ( ( int )b3 << 8 ) + b4;
}

/*
================
LongNoSwap
================
*/
int	LongNoSwap( int l )
{
	return l;
}

/*
================
FloatSwap
================
*/
float FloatSwap( float f )
{
	union
	{
		float	f;
		byte	b[4];
	} dat1, dat2;
	
	
	dat1.f = f;
	dat2.b[0] = dat1.b[3];
	dat2.b[1] = dat1.b[2];
	dat2.b[2] = dat1.b[1];
	dat2.b[3] = dat1.b[0];
	return dat2.f;
}

/*
================
FloatNoSwap
================
*/
float FloatNoSwap( float f )
{
	return f;
}

/*
=====================================================================
RevBytesSwap

Reverses byte order in place.

INPUTS
   bp       bytes to reverse
   elsize   size of the underlying data type
   elcount  number of elements to swap

RESULTS
   Reverses the byte order in each of elcount elements.
===================================================================== */
void RevBytesSwap( void* bp, int elsize, int elcount )
{
	register unsigned char* p, *q;
	
	p = ( unsigned char* ) bp;
	
	if( elsize == 2 )
	{
		q = p + 1;
		while( elcount-- )
		{
			*p ^= *q;
			*q ^= *p;
			*p ^= *q;
			p += 2;
			q += 2;
		}
		return;
	}
	
	while( elcount-- )
	{
		q = p + elsize - 1;
		while( p < q )
		{
			*p ^= *q;
			*q ^= *p;
			*p ^= *q;
			++p;
			--q;
		}
		p += elsize >> 1;
	}
}

/*
 =====================================================================
 RevBytesSwap

 Reverses byte order in place, then reverses bits in those bytes

 INPUTS
 bp       bitfield structure to reverse
 elsize   size of the underlying data type

 RESULTS
 Reverses the bitfield of size elsize.
 ===================================================================== */
void RevBitFieldSwap( void* bp, int elsize )
{
	int i;
	unsigned char* p, t, v;
	
	LittleRevBytes( bp, elsize, 1 );
	
	p = ( unsigned char* ) bp;
	while( elsize-- )
	{
		v = *p;
		t = 0;
		for( i = 7; i >= 0; i-- )
		{
			t <<= 1;
			v >>= 1;
			t |= v & 1;
		}
		*p++ = t;
	}
}

/*
================
RevBytesNoSwap
================
*/
void RevBytesNoSwap( void* bp, int elsize, int elcount )
{
	return;
}

/*
 ================
 RevBytesNoSwap
 ================
 */
void RevBitFieldNoSwap( void* bp, int elsize )
{
	return;
}

/*
================
SixtetsForIntLittle
================
*/
void SixtetsForIntLittle( byte* out, int src )
{
	byte* b = ( byte* )&src;
	out[0] = ( b[0] & 0xfc ) >> 2;
	out[1] = ( ( b[0] & 0x3 ) << 4 ) + ( ( b[1] & 0xf0 ) >> 4 );
	out[2] = ( ( b[1] & 0xf ) << 2 ) + ( ( b[2] & 0xc0 ) >> 6 );
	out[3] = b[2] & 0x3f;
}

/*
================
SixtetsForIntBig
TTimo: untested - that's the version from initial base64 encode
================
*/
void SixtetsForIntBig( byte* out, int src )
{
	for( int i = 0 ; i < 4 ; i++ )
	{
		out[i] = src & 0x3f;
		src >>= 6;
	}
}

/*
================
IntForSixtetsLittle
================
*/
int IntForSixtetsLittle( byte* in )
{
	int ret = 0;
	byte* b = ( byte* )&ret;
	b[0] |= in[0] << 2;
	b[0] |= ( in[1] & 0x30 ) >> 4;
	b[1] |= ( in[1] & 0xf ) << 4;
	b[1] |= ( in[2] & 0x3c ) >> 2;
	b[2] |= ( in[2] & 0x3 ) << 6;
	b[2] |= in[3];
	return ret;
}

/*
================
IntForSixtetsBig
TTimo: untested - that's the version from initial base64 decode
================
*/
int IntForSixtetsBig( byte* in )
{
	int ret = 0;
	ret |= in[0];
	ret |= in[1] << 6;
	ret |= in[2] << 2 * 6;
	ret |= in[3] << 3 * 6;
	return ret;
}

/*
================
Swap_Init
================
*/
void Swap_Init()
{
	byte	swaptest[2] = {1, 0};
	
	// set the byte swapping variables in a portable manner
	if( *( short* )swaptest == 1 )
	{
		// little endian ex: x86
		_BigShort = ShortSwap;
		_LittleShort = ShortNoSwap;
		_BigLong = LongSwap;
		_LittleLong = LongNoSwap;
		_BigFloat = FloatSwap;
		_LittleFloat = FloatNoSwap;
		_BigRevBytes = RevBytesSwap;
		_LittleRevBytes = RevBytesNoSwap;
		_LittleBitField = RevBitFieldNoSwap;
		_SixtetsForInt = SixtetsForIntLittle;
		_IntForSixtets = IntForSixtetsLittle;
	}
	else
	{
		// big endian ex: ppc
		_BigShort = ShortNoSwap;
		_LittleShort = ShortSwap;
		_BigLong = LongNoSwap;
		_LittleLong = LongSwap;
		_BigFloat = FloatNoSwap;
		_LittleFloat = FloatSwap;
		_BigRevBytes = RevBytesNoSwap;
		_LittleRevBytes = RevBytesSwap;
		_LittleBitField = RevBitFieldSwap;
		_SixtetsForInt = SixtetsForIntBig;
		_IntForSixtets = IntForSixtetsBig;
	}
}

/*
==========
Swap_IsBigEndian
==========
*/
bool Swap_IsBigEndian()
{
	byte	swaptest[2] = {1, 0};
	return *( short* )swaptest != 1;
}


/*
========================
BreakOnListGrowth

debug tool to find uses of idlist that are dynamically growing
========================
*/
void BreakOnListGrowth()
{
}

/*
========================
BreakOnListDefault
========================
*/
void BreakOnListDefault()
{
}
