/*
Copyright (C) 2002-2003 Victor Luchits

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.

*/
#include "client.h"

static cgame_export_t *cge;

static mempool_t *cl_gamemodulepool;

/*
===============
CL_GameModule_Error
===============
*/
static void CL_GameModule_Error( char *msg ) {
	Com_Error( ERR_DROP, "%s", msg );
}

/*
===============
CL_GameModule_Print
===============
*/
static void CL_GameModule_Print( char *msg ) {
	Com_Printf( "%s", msg );
}

/*
===============
CL_GameModule_GetConfigString
===============
*/
static void CL_GameModule_GetConfigString( int i, char *str, int size )
{
	if( i < 0 || i >= MAX_CONFIGSTRINGS )
		Com_DPrintf( S_COLOR_RED "CL_GameModule_GetConfigString: i > MAX_CONFIGSTRINGS" );
	if( !str || size <= 0 )
		Com_DPrintf( S_COLOR_RED "CL_GameModule_GetConfigString: NULL string" );
	Q_strncpyz( str, cl.configstrings[i], size );
}

/*
===============
CL_GameModule_NET_GetCurrentState
===============
*/
static void CL_GameModule_NET_GetCurrentState( int *incomingAcknowledged, int *outgoingSequence )
{  
	if( incomingAcknowledged )
		*incomingAcknowledged = cls.netchan.incoming_acknowledged;
	if( outgoingSequence )
		*outgoingSequence = cls.netchan.outgoingSequence;
}

/*
===============
CL_GameModule_NET_GetUserCmd
===============
*/
static void CL_GameModule_NET_GetUserCmd( int frame, usercmd_t *cmd ) {
	if( cmd )
		*cmd = cl.cmds[frame & CMD_MASK];
} 

/*
===============
CL_GameModule_NET_GetCurrentUserCmdNum
===============
*/
static int CL_GameModule_NET_GetCurrentUserCmdNum( void ) {

	return cl.cmdNum + 1; // jalfixme: maybe we should return straight and add + 1 later at cgame
} 

/*
===============
CL_GameModule_MemAlloc
===============
*/
static void *CL_GameModule_MemAlloc( mempool_t *pool, int size, const char *filename, int fileline ) {
	return _Mem_Alloc( pool, size, MEMPOOL_CLIENTGAME, 0, filename, fileline );
}

/*
===============
CL_GameModule_MemFree
===============
*/
static void CL_GameModule_MemFree( void *data, const char *filename, int fileline ) {
	_Mem_Free( data, MEMPOOL_CLIENTGAME, 0, filename, fileline );
}

/*
===============
CL_GameModule_MemAllocPool
===============
*/
static mempool_t *CL_GameModule_MemAllocPool( const char *name, const char *filename, int fileline ) {
	return _Mem_AllocPool( cl_gamemodulepool, name, MEMPOOL_CLIENTGAME, filename, fileline );
}

/*
===============
CL_GameModule_MemFreePool
===============
*/
static void CL_GameModule_MemFreePool( mempool_t **pool, const char *filename, int fileline ) {
	_Mem_FreePool( pool, MEMPOOL_CLIENTGAME, 0, filename, fileline );
}

/*
===============
CL_GameModule_MemEmptyPool
===============
*/
static void CL_GameModule_MemEmptyPool( mempool_t *pool, const char *filename, int fileline ) {
	_Mem_EmptyPool( pool, MEMPOOL_CLIENTGAME, 0, filename, fileline );
}

/*
===============
CL_GameModule_Init
===============
*/
void CL_GameModule_Init (void)
{
	int apiversion;
	int oldState;
	unsigned int start;
	cgame_import_t import;

	// unload anything we have now
	CL_GameModule_Shutdown ();

	cl_gamemodulepool = Mem_AllocPool ( NULL, "Client Game Progs" );

	import.Error = CL_GameModule_Error;
	import.Print = CL_GameModule_Print;

	import.Cvar_Get = Cvar_Get;
	import.Cvar_Set = Cvar_Set;
	import.Cvar_SetValue = Cvar_SetValue;
	import.Cvar_ForceSet = Cvar_ForceSet;
	import.Cvar_VariableString = Cvar_VariableString;
	import.Cvar_VariableValue = Cvar_VariableValue;

	import.Cmd_Argc = Cmd_Argc;
	import.Cmd_Argv = Cmd_Argv;
	import.Cmd_Args = Cmd_Args;

	import.Cmd_AddCommand = Cmd_AddCommand;
	import.Cmd_RemoveCommand = Cmd_RemoveCommand;
	import.Cmd_ExecuteText = Cbuf_ExecuteText;
	import.Cmd_Execute = Cbuf_Execute;

	import.FS_FOpenFile = FS_FOpenFile;
	import.FS_Read = FS_Read;
	import.FS_Write = FS_Write;
	import.FS_vPrintf = FS_vPrintf; // wsw -- PLX
	import.FS_Tell = FS_Tell;
	import.FS_Seek = FS_Seek;
	import.FS_Eof = FS_Eof;
	import.FS_Flush = FS_Flush;
	import.FS_FCloseFile = FS_FCloseFile;
	import.FS_GetFileList = FS_GetFileList;
	import.FS_Gamedir = FS_Gamedir;

	import.Key_GetBindingBuf = Key_GetBindingBuf;
	import.Key_KeynumToString = Key_KeynumToString;

	import.GetConfigString = CL_GameModule_GetConfigString;

	import.Milliseconds = Sys_Milliseconds;

	import.NET_GetUserCmd = CL_GameModule_NET_GetUserCmd;
	import.NET_GetCurrentUserCmdNum = CL_GameModule_NET_GetCurrentUserCmdNum;
	import.NET_GetCurrentState = CL_GameModule_NET_GetCurrentState;

	import.R_UpdateScreen = SCR_UpdateScreen;
	import.R_GetClippedFragments = R_GetClippedFragments;
	import.R_ClearScene = R_ClearScene;
	import.R_AddEntityToScene = R_AddEntityToScene;
	import.R_AddLightToScene = R_AddLightToScene;
	import.R_AddPolyToScene = R_AddPolyToScene;
	import.R_AddLightStyleToScene = R_AddLightStyleToScene;
	import.R_RenderScene = R_RenderScene;
	import.R_RegisterWorldModel = R_RegisterWorldModel;
	import.R_ModelBounds = R_ModelBounds;
	import.R_RegisterModel = R_RegisterModel;
	import.R_RegisterPic = R_RegisterPic;
	import.R_RegisterSkin = R_RegisterSkin;
	import.R_RegisterSkinFile = R_RegisterSkinFile;
	import.R_LerpTag = R_LerpTag;
#ifdef CGAMEGETLIGHTORIGIN
	import.R_LightGridForOrigin = R_LightGridForOrigin;
#endif
	import.R_DrawStretchPic = R_DrawStretchPic;
	import.R_TransformVectorToScreen = R_TransformVectorToScreen;
	import.R_SkeletalGetNumBones = R_SkeletalGetNumBones;
	import.R_SkeletalGetBoneInfo = R_SkeletalGetBoneInfo;
	import.R_SkeletalGetBonePose = R_SkeletalGetBonePose;

	import.CM_NumInlineModels = CM_NumInlineModels;
	import.CM_InlineModel = CM_InlineModel;
	import.CM_BoxTrace = CM_BoxTrace;
	import.CM_TransformedBoxTrace = CM_TransformedBoxTrace;
	import.CM_PointContents = CM_PointContents;
	import.CM_TransformedPointContents = CM_TransformedPointContents;
	import.CM_ModelForBBox = CM_ModelForBBox;
	import.CM_InlineModelBounds = CM_InlineModelBounds;

	import.S_RegisterSound = S_RegisterSound;
	import.S_StartSound = S_StartSound;
	import.S_Update = S_Update;
	import.S_AddLoopSound = S_AddLoopSound;
	import.S_StartBackgroundTrack = S_StartBackgroundTrack;
	import.S_StopBackgroundTrack = S_StopBackgroundTrack;

	import.SCR_RegisterFont = SCR_RegisterFont;
	import.SCR_DrawString = SCR_DrawString;
	import.SCR_DrawStringLen = SCR_DrawStringLen;
	import.SCR_strHeight = SCR_strHeight;
	import.SCR_strWidth = SCR_strWidth;
	import.SCR_StrlenForWidth = SCR_StrlenForWidth;
	
	import.Mem_Alloc = CL_GameModule_MemAlloc;
	import.Mem_Free = CL_GameModule_MemFree;
	import.Mem_AllocPool = CL_GameModule_MemAllocPool;
	import.Mem_FreePool = CL_GameModule_MemFreePool;
	import.Mem_EmptyPool = CL_GameModule_MemEmptyPool;

	cge = ( cgame_export_t * )Sys_LoadGameLibrary( LIB_CGAME, &import );
	if( !cge )
		Com_Error( ERR_DROP, "failed to load client game DLL" );

	apiversion = cge->API ();
	if ( apiversion != CGAME_API_VERSION ) {
		Sys_UnloadGameLibrary( LIB_CGAME );
		Mem_FreePool( &cl_gamemodulepool );
		cge = NULL;
		Com_Error( ERR_DROP, "client game is version %i, not %i", apiversion, CGAME_API_VERSION );
	}

	oldState = cls.state;
	cls.state = CA_LOADING;

	start = Sys_Milliseconds ();
	cge->Init( cl.playernum, viddef.width, viddef.height );
	Com_DPrintf( "CL_GameModule_Init: %.2f seconds\n", (float)(Sys_Milliseconds () - start) * 0.001f );

	cls.state = oldState;
	cls.cgameActive = qtrue;

	// check memory integrity
	Mem_CheckSentinelsGlobal ();

	S_SoundsInMemory ();

	Sys_SendKeyEvents ();	// pump message loop
}

/*
===============
CL_GameModule_Shutdown
===============
*/
void CL_GameModule_Shutdown (void)
{
	if ( !cge )
		return;

	cls.cgameActive = qfalse;

	cge->Shutdown ();
	Sys_UnloadGameLibrary( LIB_CGAME );
	Mem_FreePool( &cl_gamemodulepool );
	cge = NULL;
}

/*
==============
CL_GameModule_InGameMenu
==============
*/
void CL_GameModule_InGameMenu( void )
{
	if( cge )
		cge->InGameMenu();
}

/*
==============
CL_GameModule_LoadLayout
==============
*/
void CL_GameModule_LoadLayout( char *s )
{
	if( cge )
		cge->LoadLayout( s );
}

//==============
//CL_GameModule_NewFrameSnap
//==============
void CL_GameModule_NewFrameSnap( frame_t *frame, frame_t *oldframe ) {
	if( cge )
		cge->NewFrameSnap( frame, oldframe );
}

/*
===============
CL_GameModule_GlobalSound
===============
*/
void CL_GameModule_GlobalSound( vec3_t origin, int entNum, int entChannel, int soundNum, float fvol, float attenuation )
{
	if( cge )
		cge->GlobalSound( origin, entNum, entChannel, soundNum, fvol, attenuation );
}

/*
==============
CL_GameModule_GetEntitySoundOrigin
==============
*/
void CL_GameModule_GetEntitySoundOrigin( int entNum, vec3_t origin )
{
	if( cge )
		cge->GetEntitySoundOrigin( entNum, origin );
}

/*
===============
CL_GameModule_ServerCommand
===============
*/
void CL_GameModule_ServerCommand( void )
{
	if( cge )
		cge->ServerCommand ();
}

/*
===============
CL_GameModule_RenderView
===============
*/
void CL_GameModule_RenderView( float stereo_separation )
{
	if( cge )
		cge->RenderView( cls.trueframetime, cls.realtime, cl.serverTime, stereo_separation, cls.demoplaying );
}

/*
===============
CL_GameModule_Trace
===============
*/
void CL_GameModule_Trace( trace_t *tr, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask )
{
	if( tr && cge )
		cge->Trace( tr, start, mins, maxs, end, passent, contentmask );
}
