/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena 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 2 of the License,
or (at your option) any later version.

Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
//
/**********************************************************************
	UI_ATOMS.C

	User interface building blocks and support functions.
**********************************************************************/
#include "ui_local.h"

qboolean		m_entersound;		// after a frame, so caching won't disrupt the sound

void QDECL Com_Error( int level, const char *error, ... ) {
	va_list		argptr;
	char		text[1024];

	va_start (argptr, error);
	Q_vsnprintf (text, sizeof(text), error, argptr);
	va_end (argptr);

	trap_Error( va("%s", text) );
}

void QDECL Com_Printf( const char *msg, ... ) {
	va_list		argptr;
	char		text[1024];

	va_start (argptr, msg);
	Q_vsnprintf (text, sizeof(text), msg, argptr);
	va_end (argptr);

	trap_Print( va("%s", text) );
}

void QDECL Com_DPrintf( const char *msg, ... ) {
	va_list		argptr;
	char		text[1024];

	if (!trap_Cvar_VariableValue("developer")) {
		return;			// don't confuse non-developers with techie stuff...
	}

	va_start (argptr, msg);
	Q_vsnprintf (text, sizeof(text), msg, argptr);
	va_end (argptr);

	trap_Print( va("%s", text) );
}

qboolean newUI = qfalse;


/*
=================
UI_ClampCvar
=================
*/
float UI_ClampCvar( float min, float max, float value )
{
	if ( value < min ) return min;
	if ( value > max ) return max;
	return value;
}

/*
=================
UI_StartDemoLoop
=================
*/
void UI_StartDemoLoop( void ) {
	trap_Cmd_ExecuteText( EXEC_APPEND, "d1\n" );
}


char *UI_Argv( int arg ) {
	static char	buffer[MAX_STRING_CHARS];

	trap_Argv( arg, buffer, sizeof( buffer ) );

	return buffer;
}


char *UI_Cvar_VariableString( const char *var_name ) {
	static char	buffer[MAX_STRING_CHARS];

	trap_Cvar_VariableStringBuffer( var_name, buffer, sizeof( buffer ) );

	return buffer;
}



#ifdef TA_SP
void UI_SetBestScores(arcadeScore_t *newInfo, qboolean postGame) {
	trap_Cvar_Set("ui_scoreName", 			newInfo->name);
	trap_Cvar_Set("ui_scoreCharacter", 		newInfo->character);
	trap_Cvar_Set("ui_scoreScore", 			va("%i", newInfo->score));
	trap_Cvar_Set("ui_scoreTeam",			va("%i to %i", newInfo->redScore, newInfo->blueScore));
	trap_Cvar_Set("ui_scoreTime",			va("%02i:%02i", newInfo->time / 60, newInfo->time % 60));
	trap_Cvar_Set("ui_scoreCaptures",		va("%i", newInfo->captures));
	if (postGame) {
		trap_Cvar_Set("ui_scoreName2", 			newInfo->name);
		trap_Cvar_Set("ui_scoreCharacter2", 	newInfo->character);
		trap_Cvar_Set("ui_scoreScore2", 		va("%i", newInfo->score));
		trap_Cvar_Set("ui_scoreTeam2",			va("%i to %i", newInfo->redScore, newInfo->blueScore));
		trap_Cvar_Set("ui_scoreTime2",			va("%02i:%02i", newInfo->time / 60, newInfo->time % 60));
		trap_Cvar_Set("ui_scoreCaptures2",		va("%i", newInfo->captures));
	}
}
#else
void UI_SetBestScores(postGameInfo_t *newInfo, qboolean postGame) {
	trap_Cvar_Set("ui_scoreAccuracy",     va("%i%%", newInfo->accuracy));
#ifndef TURTLEARENA // AWARDS
	trap_Cvar_Set("ui_scoreImpressives",	va("%i", newInfo->impressives));
	trap_Cvar_Set("ui_scoreExcellents", 	va("%i", newInfo->excellents));
#endif
	trap_Cvar_Set("ui_scoreDefends", 			va("%i", newInfo->defends));
	trap_Cvar_Set("ui_scoreAssists", 			va("%i", newInfo->assists));
#ifndef TURTLEARENA // AWARDS
	trap_Cvar_Set("ui_scoreGauntlets", 		va("%i", newInfo->gauntlets));
#endif
	trap_Cvar_Set("ui_scoreScore", 				va("%i", newInfo->score));
	trap_Cvar_Set("ui_scorePerfect",	 		va("%i", newInfo->perfects));
	trap_Cvar_Set("ui_scoreTeam",					va("%i to %i", newInfo->redScore, newInfo->blueScore));
	trap_Cvar_Set("ui_scoreBase",					va("%i", newInfo->baseScore));
	trap_Cvar_Set("ui_scoreTimeBonus",		va("%i", newInfo->timeBonus));
	trap_Cvar_Set("ui_scoreSkillBonus",		va("%i", newInfo->skillBonus));
	trap_Cvar_Set("ui_scoreShutoutBonus",	va("%i", newInfo->shutoutBonus));
	trap_Cvar_Set("ui_scoreTime",					va("%02i:%02i", newInfo->time / 60, newInfo->time % 60));
	trap_Cvar_Set("ui_scoreCaptures",		va("%i", newInfo->captures));
  if (postGame) {
		trap_Cvar_Set("ui_scoreAccuracy2",     va("%i%%", newInfo->accuracy));
#ifndef TURTLEARENA // AWARDS
		trap_Cvar_Set("ui_scoreImpressives2",	va("%i", newInfo->impressives));
		trap_Cvar_Set("ui_scoreExcellents2", 	va("%i", newInfo->excellents));
#endif
		trap_Cvar_Set("ui_scoreDefends2", 			va("%i", newInfo->defends));
		trap_Cvar_Set("ui_scoreAssists2", 			va("%i", newInfo->assists));
#ifndef TURTLEARENA // AWARDS
		trap_Cvar_Set("ui_scoreGauntlets2", 		va("%i", newInfo->gauntlets));
#endif
		trap_Cvar_Set("ui_scoreScore2", 				va("%i", newInfo->score));
		trap_Cvar_Set("ui_scorePerfect2",	 		va("%i", newInfo->perfects));
		trap_Cvar_Set("ui_scoreTeam2",					va("%i to %i", newInfo->redScore, newInfo->blueScore));
		trap_Cvar_Set("ui_scoreBase2",					va("%i", newInfo->baseScore));
		trap_Cvar_Set("ui_scoreTimeBonus2",		va("%i", newInfo->timeBonus));
		trap_Cvar_Set("ui_scoreSkillBonus2",		va("%i", newInfo->skillBonus));
		trap_Cvar_Set("ui_scoreShutoutBonus2",	va("%i", newInfo->shutoutBonus));
		trap_Cvar_Set("ui_scoreTime2",					va("%02i:%02i", newInfo->time / 60, newInfo->time % 60));
		trap_Cvar_Set("ui_scoreCaptures2",		va("%i", newInfo->captures));
	}
}
#endif

void UI_LoadBestScores(const char *map, int game)
{
	char		fileName[MAX_QPATH];
	fileHandle_t f;
#ifdef TA_SP
	static char *gametypeNames[] = {"ffa", "tourney", "single", "team", "ctf", "oneflag", "overload", "harvester"};
	arcadeGameData_t gamedata;
	qboolean	validData;
#else
	postGameInfo_t newInfo;
#endif
	int protocol, protocolLegacy;
	
#ifdef TA_SP
	// compose file name
	Com_sprintf(fileName, MAX_QPATH, "scores/%s_%s.score", map, gametypeNames[game]);
	// see if we have one already
	validData = qfalse;
	if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) {
		trap_FS_Read(&gamedata, sizeof(arcadeGameData_t), f);
		trap_FS_FCloseFile(f);

		if (!Q_strncmp(gamedata.magic, ARCADE_GAMEDATA_MAGIC, ARRAY_LEN(gamedata.magic)))
		{
			if (gamedata.version == ARCADE_GAMEDATA_VERSION) {
				validData = qtrue;
			}
		}
	}

	if (!validData) {
		memset(&gamedata, 0, sizeof(arcadeGameData_t));
	}

	UI_SetBestScores(&gamedata.scores[0], qfalse);
#else
	memset(&newInfo, 0, sizeof(postGameInfo_t));
	Com_sprintf(fileName, MAX_QPATH, "games/%s_%i.game", map, game);
	if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) {
		int size = 0;
		trap_FS_Read(&size, sizeof(int), f);
		if (size == sizeof(postGameInfo_t)) {
			trap_FS_Read(&newInfo, sizeof(postGameInfo_t), f);
		}
		trap_FS_FCloseFile(f);
	}
	UI_SetBestScores(&newInfo, qfalse);
#endif

	uiInfo.demoAvailable = qfalse;

	protocolLegacy = trap_Cvar_VariableValue("com_legacyprotocol");
	protocol = trap_Cvar_VariableValue("com_protocol");

	if(!protocol)
		protocol = trap_Cvar_VariableValue("protocol");
	if(protocolLegacy == protocol)
		protocolLegacy = 0;

	Com_sprintf(fileName, MAX_QPATH, "demos/%s_%d.%s%d", map, game, DEMOEXT, protocol);
	if(trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0)
	{
		uiInfo.demoAvailable = qtrue;
		trap_FS_FCloseFile(f);
	}
	else if(protocolLegacy > 0)
	{
		Com_sprintf(fileName, MAX_QPATH, "demos/%s_%d.%s%d", map, game, DEMOEXT, protocolLegacy);
		if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0)
		{
			uiInfo.demoAvailable = qtrue;
			trap_FS_FCloseFile(f);
		}
	} 
}

/*
===============
UI_ClearScores
===============
*/
void UI_ClearScores(void) {
	char	gameList[4096];
	char *gameFile;
	int		i, len, count, size;
	fileHandle_t f;
#ifdef TA_SP
	arcadeGameData_t newInfo;

	count = trap_FS_GetFileList( "scores", "score", gameList, sizeof(gameList) );

	size = sizeof(arcadeGameData_t);
#else
	postGameInfo_t newInfo;

	count = trap_FS_GetFileList( "games", "game", gameList, sizeof(gameList) );

	size = sizeof(postGameInfo_t);
#endif
	memset(&newInfo, 0, size);

	if (count > 0) {
		gameFile = gameList;
		for ( i = 0; i < count; i++ ) {
			len = strlen(gameFile);
#ifdef TA_SP
			if (trap_FS_FOpenFile(va("scores/%s",gameFile), &f, FS_WRITE) >= 0)
#else
			if (trap_FS_FOpenFile(va("games/%s",gameFile), &f, FS_WRITE) >= 0)
#endif
			{
				trap_FS_Write(&size, sizeof(int), f);
				trap_FS_Write(&newInfo, size, f);
				trap_FS_FCloseFile(f);
			}
			gameFile += len + 1;
		}
	}
	
#ifdef TA_SP
	UI_SetBestScores(&newInfo.scores[0], qfalse);
#else
	UI_SetBestScores(&newInfo, qfalse);
#endif

}



static void	UI_Cache_f( void ) {
	Display_CacheAll();
}

#ifdef TA_SP
// Returns score pos, or -1 if didn't get high enough score to save it.
int UI_AddArcadeScore(arcadeGameData_t *gamedata, arcadeScore_t *score)
{
	int index, i;

	// Find where to put score
	for (index = 0; index < NUM_ARCADE_SCORES; ++index) {
		if (!gamedata->scores[index].time || score->score > gamedata->scores[index].score
			|| (score->score == gamedata->scores[index].score && score->time < gamedata->scores[index].time)) {
			// Empty score, higher score or same score and faster
			break;
		}
	}

	// Not a new high score
	if (index == NUM_ARCADE_SCORES) {
		return -1;
	}

	// Move slots to make room for new score
	for (i = NUM_ARCADE_SCORES-2; i >= index; --i) {
		memcpy(&gamedata->scores[i+1], &gamedata->scores[i], sizeof (arcadeScore_t));
	}

	// Add new score
	memcpy(&gamedata->scores[index], score, sizeof (arcadeScore_t));

	return index;
}
#endif

/*
=======================
UI_CalcPostGameStats
=======================
*/
static void UI_CalcPostGameStats( void ) {
#ifdef TA_SP
	static char *gametypeNames[] = {"ffa", "tourney", "single", "team", "ctf", "oneflag", "overload", "harvester"};
	char		map[MAX_QPATH];
	char		fileName[MAX_QPATH];
	char		info[MAX_INFO_STRING];
	char		name[9];
	fileHandle_t f;
	qboolean	validData;
	int			time, redScore, blueScore;
	arcadeGameData_t *gamedata;
	arcadeScore_t *newScore;
	arcadeGameData_t uiGamedata;
	arcadeScore_t uiNewScore;
	int scoreIndex;
	int gametype;

	gamedata = &uiGamedata;
	newScore = &uiNewScore;

	trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) );
	Q_strncpyz( map, Info_ValueForKey( info, "mapname" ), sizeof(map) );
	gametype = Com_Clamp(0, ARRAY_LEN(gametypeNames), atoi(Info_ValueForKey(info, "g_gametype")));

	// compose file name
	Com_sprintf(fileName, MAX_QPATH, "scores/%s_%s.score", map, gametypeNames[gametype]);
	// see if we have one already
	validData = qfalse;
	if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) {
		trap_FS_Read(gamedata, sizeof(arcadeGameData_t), f);
		trap_FS_FCloseFile(f);

		if (!Q_strncmp(gamedata->magic, ARCADE_GAMEDATA_MAGIC, ARRAY_LEN(gamedata->magic)))
		{
			if (gamedata->version == ARCADE_GAMEDATA_VERSION) {
				validData = qtrue;
			}
		}
	}

	if (!validData) {
		memset(gamedata, 0, sizeof(arcadeGameData_t));
	}

	// Get name
	// TODO: Ask user to enter name
	trap_Cvar_VariableStringBuffer("name", name, sizeof (name));

	// Setup gamedata
	memcpy(gamedata->magic, ARCADE_GAMEDATA_MAGIC, ARRAY_LEN(gamedata->magic));
	gamedata->version = ARCADE_GAMEDATA_VERSION;

	time = (atoi(UI_Argv(1)) - trap_Cvar_VariableValue("ui_matchStartTime")) / 1000;
	redScore = atoi(UI_Argv(2));
	blueScore = atoi(UI_Argv(3));

	// Setup newScore
	Q_strncpyz(newScore->name, name, STRARRAY_LEN(newScore->name));
	Q_strncpyz(newScore->character, UI_Argv(4), STRARRAY_LEN(newScore->character));
	newScore->score = atoi(UI_Argv(5));
	newScore->time = time;

	// CTF
	newScore->captures = atoi(UI_Argv(6));
	newScore->redScore = redScore;
	newScore->blueScore = blueScore;

	// Add the score
	scoreIndex = UI_AddArcadeScore(gamedata, newScore);
	trap_Cvar_Set("ui_scoreIndex", va("%d", scoreIndex));

	// Write updated gamedata
	if (trap_FS_FOpenFile(fileName, &f, FS_WRITE) >= 0) {
		trap_FS_Write(gamedata, sizeof(arcadeGameData_t), f);
		trap_FS_FCloseFile(f);
	}
#else
	char		map[MAX_QPATH];
	char		fileName[MAX_QPATH];
	char		info[MAX_INFO_STRING];
	fileHandle_t f;
	int size, game, time, adjustedTime;
	postGameInfo_t oldInfo;
	postGameInfo_t newInfo;
	qboolean newHigh = qfalse;

	trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) );
	Q_strncpyz( map, Info_ValueForKey( info, "mapname" ), sizeof(map) );
	game = atoi(Info_ValueForKey(info, "g_gametype"));

	// compose file name
	Com_sprintf(fileName, MAX_QPATH, "games/%s_%i.game", map, game);
	// see if we have one already
	memset(&oldInfo, 0, sizeof(postGameInfo_t));
	if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) {
	// if so load it
		size = 0;
		trap_FS_Read(&size, sizeof(int), f);
		if (size == sizeof(postGameInfo_t)) {
			trap_FS_Read(&oldInfo, sizeof(postGameInfo_t), f);
		}
		trap_FS_FCloseFile(f);
	}					 

	newInfo.accuracy = atoi(UI_Argv(3));
#ifdef TURTLEARENA // AWARDS
	newInfo.baseScore = atoi(UI_Argv(4));
	newInfo.perfects = atoi(UI_Argv(5));

	// Extra data for missionpack
	newInfo.defends = atoi(UI_Argv(6));
	newInfo.assists = atoi(UI_Argv(7));
	newInfo.redScore = atoi(UI_Argv(8));
	newInfo.blueScore = atoi(UI_Argv(9));
	time = atoi(UI_Argv(10));
	newInfo.captures = atoi(UI_Argv(11));
#else
	newInfo.impressives = atoi(UI_Argv(4));
	newInfo.excellents = atoi(UI_Argv(5));
	newInfo.defends = atoi(UI_Argv(6));
	newInfo.assists = atoi(UI_Argv(7));
	newInfo.gauntlets = atoi(UI_Argv(8));
	newInfo.baseScore = atoi(UI_Argv(9));
	newInfo.perfects = atoi(UI_Argv(10));
	newInfo.redScore = atoi(UI_Argv(11));
	newInfo.blueScore = atoi(UI_Argv(12));
	time = atoi(UI_Argv(13));
	newInfo.captures = atoi(UI_Argv(14));
#endif

	newInfo.time = (time - trap_Cvar_VariableValue("ui_matchStartTime")) / 1000;
	adjustedTime = uiInfo.mapList[ui_currentMap.integer].timeToBeat[game];
	if (newInfo.time < adjustedTime) { 
		newInfo.timeBonus = (adjustedTime - newInfo.time) * 10;
	} else {
		newInfo.timeBonus = 0;
	}

	if (newInfo.redScore > newInfo.blueScore && newInfo.blueScore <= 0) {
		newInfo.shutoutBonus = 100;
	} else {
		newInfo.shutoutBonus = 0;
	}

	newInfo.skillBonus = trap_Cvar_VariableValue("g_spSkill");
	if (newInfo.skillBonus <= 0) {
		newInfo.skillBonus = 1;
	}
	newInfo.score = newInfo.baseScore + newInfo.shutoutBonus + newInfo.timeBonus;
	newInfo.score *= newInfo.skillBonus;

	// see if the score is higher for this one
	newHigh = (newInfo.redScore > newInfo.blueScore && newInfo.score > oldInfo.score);

	if  (newHigh) {
		// if so write out the new one
		uiInfo.newHighScoreTime = uiInfo.uiDC.realTime + 20000;
		if (trap_FS_FOpenFile(fileName, &f, FS_WRITE) >= 0) {
			size = sizeof(postGameInfo_t);
			trap_FS_Write(&size, sizeof(int), f);
			trap_FS_Write(&newInfo, sizeof(postGameInfo_t), f);
			trap_FS_FCloseFile(f);
		}
	}

	if (newInfo.time < oldInfo.time) {
		uiInfo.newBestTime = uiInfo.uiDC.realTime + 20000;
	}
 #endif
 
	// put back all the ui overrides
	trap_Cvar_Set("capturelimit", UI_Cvar_VariableString("ui_saveCaptureLimit"));
#ifdef NOTRATEDM // frag to score
	trap_Cvar_Set("scorelimit", UI_Cvar_VariableString("ui_saveScoreLimit"));
#else
	trap_Cvar_Set("fraglimit", UI_Cvar_VariableString("ui_saveFragLimit"));
#endif
	trap_Cvar_Set("cg_drawTimer", UI_Cvar_VariableString("ui_drawTimer"));
	trap_Cvar_Set("g_doWarmup", UI_Cvar_VariableString("ui_doWarmup"));
	trap_Cvar_Set("g_Warmup", UI_Cvar_VariableString("ui_Warmup"));
	trap_Cvar_Set("sv_pure", UI_Cvar_VariableString("ui_pure"));
	trap_Cvar_Set("g_friendlyFire", UI_Cvar_VariableString("ui_friendlyFire"));

#ifdef TA_SP
	UI_SetBestScores(&gamedata->scores[0], qtrue);
	UI_ShowPostGame(scoreIndex == 0);
#else
	UI_SetBestScores(&newInfo, qtrue);
	UI_ShowPostGame(newHigh);
#endif


}


/*
=================
UI_ConsoleCommand
=================
*/
qboolean UI_ConsoleCommand( int realTime ) {
	char	*cmd;

	uiInfo.uiDC.frameTime = realTime - uiInfo.uiDC.realTime;
	uiInfo.uiDC.realTime = realTime;

	cmd = UI_Argv( 0 );

	// ensure minimum menu data is available
	//Menu_Cache();

	if ( Q_stricmp (cmd, "ui_test") == 0 ) {
		UI_ShowPostGame(qtrue);
	}

	if ( Q_stricmp (cmd, "ui_report") == 0 ) {
		UI_Report();
		return qtrue;
	}
	
	if ( Q_stricmp (cmd, "ui_load") == 0 ) {
		UI_Load();
		return qtrue;
	}

	if ( Q_stricmp (cmd, "remapShader") == 0 ) {
		if (trap_Argc() == 4) {
			char shader1[MAX_QPATH];
			char shader2[MAX_QPATH];
			char shader3[MAX_QPATH];
			
			Q_strncpyz(shader1, UI_Argv(1), sizeof(shader1));
			Q_strncpyz(shader2, UI_Argv(2), sizeof(shader2));
			Q_strncpyz(shader3, UI_Argv(3), sizeof(shader3));
			
			trap_R_RemapShader(shader1, shader2, shader3);
			return qtrue;
		}
	}

	if ( Q_stricmp (cmd, "postgame") == 0 ) {
		UI_CalcPostGameStats();
		return qtrue;
	}

	if ( Q_stricmp (cmd, "ui_cache") == 0 ) {
		UI_Cache_f();
		return qtrue;
	}

	if ( Q_stricmp (cmd, "ui_teamOrders") == 0 ) {
		//UI_TeamOrdersMenu_f();
		return qtrue;
	}

	return qfalse;
}

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

/*
================
UI_AdjustFrom640

Adjusted for resolution and screen aspect ratio
================
*/
void UI_AdjustFrom640( float *x, float *y, float *w, float *h ) {
	// expect valid pointers
#if 0
	*x = *x * uiInfo.uiDC.scale + uiInfo.uiDC.bias;
	*y *= uiInfo.uiDC.scale;
	*w *= uiInfo.uiDC.scale;
	*h *= uiInfo.uiDC.scale;
#endif

	*x *= uiInfo.uiDC.xscale;
	*y *= uiInfo.uiDC.yscale;
	*w *= uiInfo.uiDC.xscale;
	*h *= uiInfo.uiDC.yscale;

}

void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
	qhandle_t	hShader;

	hShader = trap_R_RegisterShaderNoMip( picname );
	UI_AdjustFrom640( &x, &y, &width, &height );
	trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
}

void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ) {
	float	s0;
	float	s1;
	float	t0;
	float	t1;

	if( w < 0 ) {	// flip about vertical
		w  = -w;
		s0 = 1;
		s1 = 0;
	}
	else {
		s0 = 0;
		s1 = 1;
	}

	if( h < 0 ) {	// flip about horizontal
		h  = -h;
		t0 = 1;
		t1 = 0;
	}
	else {
		t0 = 0;
		t1 = 1;
	}
	
	UI_AdjustFrom640( &x, &y, &w, &h );
	trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, hShader );
}

/*
================
UI_FillRect

Coordinates are 640*480 virtual values
=================
*/
void UI_FillRect( float x, float y, float width, float height, const float *color ) {
	trap_R_SetColor( color );

	UI_AdjustFrom640( &x, &y, &width, &height );
	trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );

	trap_R_SetColor( NULL );
}

void UI_DrawSides(float x, float y, float w, float h) {
	UI_AdjustFrom640( &x, &y, &w, &h );
	trap_R_DrawStretchPic( x, y, 1, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
	trap_R_DrawStretchPic( x + w - 1, y, 1, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
}

void UI_DrawTopBottom(float x, float y, float w, float h) {
	UI_AdjustFrom640( &x, &y, &w, &h );
	trap_R_DrawStretchPic( x, y, w, 1, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
	trap_R_DrawStretchPic( x, y + h - 1, w, 1, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
}
/*
================
UI_DrawRect

Coordinates are 640*480 virtual values
=================
*/
void UI_DrawRect( float x, float y, float width, float height, const float *color ) {
	trap_R_SetColor( color );

  UI_DrawTopBottom(x, y, width, height);
  UI_DrawSides(x, y, width, height);

	trap_R_SetColor( NULL );
}

void UI_SetColor( const float *rgba ) {
	trap_R_SetColor( rgba );
}

void UI_UpdateScreen( void ) {
	trap_UpdateScreen();
}


void UI_DrawTextBox (int x, int y, int width, int lines)
{
	UI_FillRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorBlack );
	UI_DrawRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorWhite );
}

qboolean UI_CursorInRect (int x, int y, int width, int height)
{
	if (uiInfo.uiDC.cursorx < x ||
		uiInfo.uiDC.cursory < y ||
		uiInfo.uiDC.cursorx > x+width ||
		uiInfo.uiDC.cursory > y+height)
		return qfalse;

	return qtrue;
}
