/*
Copyright (C) 1997-2001 Id Software, 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.

*/

// Main windowed and fullscreen graphics interface module. This module
// is used for both the software and OpenGL rendering versions of the
// Quake refresh engine.


#include "client.h"
#include "vid_public.h"

// Structure containing functions exported from refresh DLL
refAPI_t	ref;

// Console variables that we need to access from this module
cvar_t		*vid_ref;			// Name of Refresh DLL loaded
cvar_t		*vid_autorestart;

#ifdef REF_HARD_LINKED
qboolean Ref_APISetupCallback( api_type_t type, void *api );
#else
// Global variables used internally by this module
static void		*reflib_library;		// Handle to refresh DLL 
#endif

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

LOADING / SHUTDOWN

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

/*
==============
CL_FreeRefresh
==============
*/
static void CL_FreeRefresh( void ) {
#ifndef REF_HARD_LINKED
	Sys_FreeModule( reflib_library );
	reflib_library = NULL;
#endif
	memset( &ref, 0, sizeof( ref ) );
	cls.ref_initialized = qfalse;
}

#ifndef REF_HARD_LINKED

/*
==============
CL_RefSetupCallback
==============
*/
static qboolean CL_RefSetupCallback( api_type_t type, void *api ) {
	switch( type ) {
	case API_CMD:
		Cmd_FillAPI( ( cmdAPI_t * )api );
		break;
	case API_CVAR:
		Cvar_FillAPI( ( cvarAPI_t * )api );
		break;
	case API_FS:
		FS_FillAPI( ( fsAPI_t * )api );
		break;
	case API_COMMON:
		Com_FillAPI( ( commonAPI_t * )api );
		break;
	case API_SYSTEM:
		Sys_FillAPI( ( sysAPI_t * )api );
		break;
	case API_VIDEO_SOFTWARE:
		Video_FillSWAPI( ( vidSWAPI_t * )api );
		break;
#ifndef _WIN32_WCE
	case API_VIDEO_OPENGL:
		Video_FillGLAPI( ( vidGLAPI_t * )api );
		break;
#endif
	default:
		Com_Error( ERR_FATAL, "CL_RefSetupCallback: bad api type" );
	}

	return qtrue;
}

#endif

/*
==============
CL_LoadRefresh
==============
*/
static qboolean CL_LoadRefresh( const char *name ) {
#ifndef REF_HARD_LINKED
	moduleEntry_t entry;
	moduleInfo_t info;
	moduleCapability_t caps;
	APISetupCallback_t callback;
#endif
	
	if( cls.ref_initialized ) {
		ref.Shutdown( qtrue );
		CL_FreeRefresh();
	}

	Com_Printf( "------- Loading %s -------\n", name );

#ifdef REF_HARD_LINKED
#ifdef SOFTWARE_RENDERER
	Video_FillSWAPI( &video );
#else
	Video_FillGLAPI( &video );
#endif

	Ref_APISetupCallback( API_REFRESH, &ref );
#else
	
	if( ( entry = Sys_LoadModule( name, &reflib_library ) ) == NULL ) {
		Com_WPrintf( "Couldn't load %s\n", name );
		return qfalse;
	}

	entry( MQ_GETINFO, &info );
	if( info.api_version != MODULES_APIVERSION ) {
		Com_WPrintf( "%s has incompatible api_version: %i, should be %i\n",
			name, info.api_version, MODULES_APIVERSION );
		goto fail;
	}

	caps = ( moduleCapability_t )entry( MQ_GETCAPS, NULL );
	if( !( caps & MCP_REFRESH ) ) {
		Com_WPrintf( "%s doesn't have REFRESH capability\n", name );
		goto fail;
	}

	callback = ( APISetupCallback_t )entry( MQ_SETUPAPI, ( void * )CL_RefSetupCallback );
	if( !callback ) {
		Com_WPrintf( "%s returned NULL callback\n", name );
		goto fail;
	}

	callback( API_REFRESH, &ref );
#endif

	cls.ref_initialized = qtrue;
	if( !ref.Init( qtrue ) ) {
		goto fail;
	}

	Com_Printf( "------------------------------------\n" );
	
	return qtrue;

fail:
	CL_FreeRefresh();
	return qfalse;
}

/*
============
CL_PumpEvents
============
*/
void CL_PumpEvents( void ) {
	if( !cls.ref_initialized ) {
        return;
    }
    
	Video_PumpEvents();

    if( cvar_latchedModified & ( 1 << CVAR_SYSTEM_VIDEO ) ) {
        if( vid_autorestart->integer ) {
            Cbuf_AddText( "vid_restart\n" );
        }
        cvar_latchedModified &= ~( 1 << CVAR_SYSTEM_VIDEO );
    }

}

#if( defined REF_HARD_LINKED ) && ( defined OPENGL_RENDERER )
#define DEFAULT_REFRESH_DRIVER	"newgl"
#else
#define DEFAULT_REFRESH_DRIVER	"soft"
#endif

/*
============
CL_InitRefresh
============
*/
void CL_InitRefresh( void ) {
	if( cls.ref_initialized ) {
		return;
	}

	/* Create the video variables so we know how to start the graphics drivers */
	vid_autorestart = Cvar_Get( "vid_autorestart", "0", 0 );

#ifdef REF_HARD_LINKED
	vid_ref = Cvar_Get( "vid_ref", DEFAULT_REFRESH_DRIVER, CVAR_ROM );
	if( !CL_LoadRefresh( "ref_" DEFAULT_REFRESH_DRIVER ) ) {
		Com_Error( ERR_FATAL, "Couldn't load built-in video driver!" );
	}
#else
	vid_ref = Cvar_Get( "vid_ref", DEFAULT_REFRESH_DRIVER, CVAR_ARCHIVE|CVAR_LATCHED );
	vid_ref->subsystem = CVAR_SYSTEM_VIDEO;
		
	/* Start the graphics mode and load refresh DLL */
	while( 1 ) {
		char buffer[MAX_QPATH];

		if( !strcmp( vid_ref->string, "gl" ) ) {
			/* Temporary hack until I port ref_gl to the new refresh API */
			strcpy( buffer, "ref_newgl" );
		} else {
			Com_sprintf( buffer, sizeof( buffer ), "ref_%s", vid_ref->string );
		}
		if( CL_LoadRefresh( buffer ) ) {
			break;
		}

		if( !strcmp( vid_ref->string, DEFAULT_REFRESH_DRIVER ) ) {
			Com_Error( ERR_FATAL, "Couldn't fall back to %s driver!", buffer );
		}

		Com_Printf( "Falling back to default...\n" );
		Cvar_Set( "vid_ref", DEFAULT_REFRESH_DRIVER );	
	}
#endif

	cvar_latchedModified &= ~( 1 << CVAR_SYSTEM_VIDEO );

}

/*
============
CL_ShutdownRefresh
============
*/
void CL_ShutdownRefresh( void ) {
	if( !cls.ref_initialized ) {
		return;
	}

	ref.Shutdown( qtrue );
	CL_FreeRefresh();

	Z_LeakTest( TAG_RENDERER );
}



