/*
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.

*/


// ui_menu.c -- main menu interface
#define OEMRESOURCE		// for OCR_* cursor junk

#include "extdll.h"
#include "basemenu.h"
#include "keydefs.h"
#include "menufont.h"	// built-in menu font
#include "utils.h"
#include "menu_btnsbmp_table.h"
//CR
#include "ui_title_anim.h"

cvar_t		*ui_precache;
cvar_t		*ui_showmodels;

uiStatic_t	uiStatic;

char		uiEmptyString[256];
const char	*uiSoundIn	= "media/launch_upmenu1.wav";
const char	*uiSoundOut	= "media/launch_dnmenu1.wav";
const char	*uiSoundLaunch	= "media/launch_select2.wav";
const char	*uiSoundGlow	= "media/launch_glow1.wav";
const char	*uiSoundBuzz	= "media/launch_deny2.wav";
const char	*uiSoundKey	= "media/launch_select1.wav";
const char	*uiSoundRemoveKey	= "media/launch_deny1.wav";
const char	*uiSoundMove	= "";		// Xash3D not use movesound
const char	*uiSoundNull	= "";

int		uiColorHelp	= 0xFFFFFFFF;	// 255, 255, 255, 255	// hint letters color
int		uiPromptBgColor	= 0xFF404040;	// 64,  64,  64,  255	// dialog background color
int		uiPromptTextColor	= 0xFFF0B418;	// 255, 160,  0,  255	// dialog or button letters color
int		uiPromptFocusColor	= 0xFFFFFF00;	// 255, 255,  0,  255	// dialog or button focus letters color
int		uiInputTextColor	= 0xFFC0C0C0;	// 192, 192, 192, 255
int		uiInputBgColor	= 0xFF404040;	// 64,  64,  64,  255	// field, scrollist, checkbox background color
int		uiInputFgColor	= 0xFF555555;	// 85,  85,  85,  255	// field, scrollist, checkbox foreground color
int		uiColorWhite	= 0xFFFFFFFF;	// 255, 255, 255, 255	// useful for bitmaps
int		uiColorDkGrey	= 0xFF404040;	// 64,  64,  64,  255	// shadow and grayed items
int		uiColorBlack	= 0xFF000000;	//  0,   0,   0,  255	// some controls background
int		uiColorConsole	= 0xFFF0B418;	// just for reference 

// color presets (this is nasty hack to allow color presets to part of text)
const int g_iColorTable[8] =
{
0xFF000000, // black
0xFFFF0000, // red
0xFF00FF00, // green
0xFFFFFF00, // yellow
0xFF0000FF, // blue
0xFF00FFFF, // cyan
0xFFF0B418, // dialog or button letters color
0xFFFFFFFF, // white
};

/*
=================
UI_ScaleCoords

Any parameter can be NULL if you don't want it
=================
*/
void UI_ScaleCoords( int *x, int *y, int *w, int *h )
{
	if( x ) *x *= uiStatic.scaleX;
	if( y ) *y *= uiStatic.scaleY;
	if( w ) *w *= uiStatic.scaleX;
	if( h ) *h *= uiStatic.scaleY;
}

/*
=================
UI_CursorInRect
=================
*/
int UI_CursorInRect( int x, int y, int w, int h )
{
	if( uiStatic.cursorX < x )
		return FALSE;
	if( uiStatic.cursorX > x + w )
		return FALSE;
	if( uiStatic.cursorY < y )
		return FALSE;
	if( uiStatic.cursorY > y + h )
		return FALSE;
	return TRUE;
}

/*
=================
UI_DrawPic
=================
*/
void UI_DrawPic( int x, int y, int width, int height, const int color, const char *pic )
{
	HIMAGE hPic = PIC_Load( pic );

	int r, g, b, a;
	UnpackRGBA( r, g, b, a, color );

	PIC_Set( hPic, r, g, b, a );
	PIC_Draw( x, y, width, height );
}

/*
=================
UI_DrawPicAdditive
=================
*/
void UI_DrawPicAdditive( int x, int y, int width, int height, const int color, const char *pic )
{
	HIMAGE hPic = PIC_Load( pic );

	int r, g, b, a;
	UnpackRGBA( r, g, b, a, color );

	PIC_Set( hPic, r, g, b, a );
	PIC_DrawAdditive( x, y, width, height );
}

/*
=================
UI_FillRect
=================
*/
void UI_FillRect( int x, int y, int width, int height, const int color )
{
	int r, g, b, a;
	UnpackRGBA( r, g, b, a, color );

	FillRGBA( x, y, width, height, r, g, b, a );
}

/*
=================
UI_DrawRectangleExt
=================
*/
void UI_DrawRectangleExt( int in_x, int in_y, int in_w, int in_h, const int color, int outlineWidth )
{
	int	x, y, w, h;

	x = in_x - outlineWidth;
	y = in_y - outlineWidth;
	w = outlineWidth;
	h = in_h + outlineWidth + outlineWidth;

	// draw left
	UI_FillRect( x, y, w, h, color );

	x = in_x + in_w;
	y = in_y - outlineWidth;
	w = outlineWidth;
	h = in_h + outlineWidth + outlineWidth;

	// draw right
	UI_FillRect( x, y, w, h, color );

	x = in_x;
	y = in_y - outlineWidth;
	w = in_w;
	h = outlineWidth;

	// draw top
	UI_FillRect( x, y, w, h, color );

	// draw bottom
	x = in_x;
	y = in_y + in_h;
	w = in_w;
	h = outlineWidth;

	UI_FillRect( x, y, w, h, color );
}

/*
=================
UI_DrawString
=================
*/
void UI_DrawString( int x, int y, int w, int h, const char *string, const int color, int forceColor, int charW, int charH, int justify, int shadow )
{
	int	modulate, shadowModulate;
	char	line[1024], *l;
	int	xx, yy, ofsX, ofsY, len, ch;

	if( !string || !string[0] )
		return;

#if 0	// g-cont. disabled 29/06/2011
	// this code do a bad things with prompt dialogues
	// vertically centered
	if( !strchr( string, '\n' ))
		y = y + (( h - charH ) / 2 );
#endif

	if( shadow )
	{
		shadowModulate = PackAlpha( uiColorBlack, UnpackAlpha( color ));

		ofsX = charW / 8;
		ofsY = charH / 8;
	}

	modulate = color;

	yy = y;
	while( *string )
	{
		// get a line of text
		len = 0;
		while( *string )
		{
			if( *string == '\n' )
			{
				string++;
				break;
			}

			line[len++] = *string++;
			if( len == sizeof( line ) - 1 )
				break;
		}
		line[len] = 0;

		// align the text as appropriate
		if( justify == 0 ) xx = x;
		if( justify == 1 ) xx = x + ((w - (ColorStrlen( line ) * charW )) / 2);
		if( justify == 2 ) xx = x + (w - (ColorStrlen( line ) * charW ));

		// draw it
		l = line;
		while( *l )
		{
			if( IsColorString( l ))
			{
				int colorNum = ColorIndex( *(l+1) );

				if( colorNum == 7 && color != 0 )
				{
					modulate = color;
				}
				else if( !forceColor )
				{
					modulate = PackAlpha( g_iColorTable[colorNum], UnpackAlpha( color ));
				}

				l += 2;
				continue;
			}

			ch = *l++;
			ch &= 255;

			// fix for letter ¸
			if( ch == 0xB8 ) ch = (byte)'å';
			if( ch == 0xA8 ) ch = (byte)'Å';

			if( ch != ' ' )
			{
				if( shadow ) TextMessageDrawChar( xx + ofsX, yy + ofsY, charW, charH, ch, shadowModulate, uiStatic.hFont );
				TextMessageDrawChar( xx, yy, charW, charH, ch, modulate, uiStatic.hFont );
			}
			xx += charW;
		}
          	yy += charH;
	}
}

/*
=================
UI_DrawMouseCursor
=================
*/
void UI_DrawMouseCursor( void )
{
	menuCommon_s	*item;
	HICON		hCursor = NULL;
	int		i;

	if( uiStatic.hideCursor ) return;

	for( i = 0; i < uiStatic.menuActive->numItems; i++ )
	{
		item = (menuCommon_s *)uiStatic.menuActive->items[i];

		if ( item->flags & (QMF_INACTIVE|QMF_HIDDEN))
			continue;

		if ( !UI_CursorInRect( item->x, item->y, item->width, item->height ))
			continue;

		if ( item->flags & QMF_GRAYED )
		{
			hCursor = (HICON)LoadCursor( NULL, (LPCTSTR)OCR_NO );
		}
		else
		{
			if( item->type == QMTYPE_FIELD )
				hCursor = (HICON)LoadCursor( NULL, (LPCTSTR)OCR_IBEAM );
		}
		break;
	}

	if( !hCursor ) hCursor = (HICON)LoadCursor( NULL, (LPCTSTR)OCR_NORMAL );

	SET_CURSOR( hCursor );
}

/*
=================
UI_DrawBackground_Callback
=================
*/
void UI_DrawBackground_Callback( void *self )
{
	if (!uiStatic.m_fHaveSteamBackground)
	{
		menuCommon_s *item = (menuCommon_s *)self;
		UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic );
		return;
	}

	int xpos, ypos;
	float xScale, yScale;

	// work out scaling factors
	xScale = ScreenWidth / uiStatic.m_flTotalWidth;
	yScale = ScreenHeight / uiStatic.m_flTotalHeight;

	// iterate and draw all the background pieces
	ypos = 0;
	for (int y = 0; y < BACKGROUND_ROWS; y++)
	{
		xpos = 0;
		for (int x = 0; x < BACKGROUND_COLUMNS; x++)
		{
			bimage_t &bimage = uiStatic.m_SteamBackground[y][x];

			int dx = (int)ceil(xpos * xScale);
			int dy = (int)ceil(ypos * yScale);
			int dw = (int)ceil(bimage.width * xScale);
			int dt = (int)ceil(bimage.height * yScale);

			if (x == 0) dx = 0;
			if (y == 0) dy = 0;

			PIC_Set( bimage.hImage, 255, 255, 255, 255 );
			PIC_Draw( dx, dy, dw, dt );
			xpos += bimage.width;
		}
		ypos += uiStatic.m_SteamBackground[y][0].height;
	}
}

/*
=================
UI_LoadBackgroundImage
=================
*/
void UI_LoadBackgroundImage( void )
{
	int num_background_images = 0;
	char filename[512];

	for( int y = 0; y < BACKGROUND_ROWS; y++ )
	{
		for( int x = 0; x < BACKGROUND_COLUMNS; x++ )
		{
			sprintf( filename, "resource/background/800_%d_%c_loading.tga", y + 1, 'a' + x );
			if (g_engfuncs.pfnFileExists( filename, TRUE ))
				num_background_images++;
		}
	}

	if (num_background_images == (BACKGROUND_COLUMNS * BACKGROUND_ROWS))
		uiStatic.m_fHaveSteamBackground = TRUE;
	else uiStatic.m_fHaveSteamBackground = FALSE;

	if (uiStatic.m_fHaveSteamBackground)
	{
		uiStatic.m_flTotalWidth = uiStatic.m_flTotalHeight = 0.0f;

		for( int y = 0; y < BACKGROUND_ROWS; y++ )
		{
			for( int x = 0; x < BACKGROUND_COLUMNS; x++ )
			{
				bimage_t &bimage = uiStatic.m_SteamBackground[y][x];
				sprintf(filename, "resource/background/800_%d_%c_loading.tga", y + 1, 'a' + x);
				bimage.hImage = PIC_Load( filename, PIC_NOFLIP_TGA );
				bimage.width = PIC_Width( bimage.hImage );
				bimage.height = PIC_Height( bimage.hImage );

				if (y==0) uiStatic.m_flTotalWidth += bimage.width;
				if (x==0) uiStatic.m_flTotalHeight += bimage.height;
			}
		}
	}
	else
	{
		if( g_engfuncs.pfnFileExists( "gfx/shell/splash.bmp", TRUE ))
		{
			// if we doesn't have logo.avi in gamedir we don't want to draw it
			if( !g_engfuncs.pfnFileExists( "media/logo.avi", TRUE ))
				uiStatic.m_fDisableLogo = TRUE;
		}
	}
}

/*
=================
UI_StartSound
=================
*/
void UI_StartSound( const char *sound )
{
	PLAY_SOUND( sound );
}

// =====================================================================


/*
=================
UI_AddItem
=================
*/
void UI_AddItem( menuFramework_s *menu, void *item )
{
	menuCommon_s	*generic = (menuCommon_s *)item;

	if( menu->numItems >= UI_MAX_MENUITEMS )
		HOST_ERROR( "UI_AddItem: UI_MAX_MENUITEMS limit exceeded\n" );

	menu->items[menu->numItems] = item;
	((menuCommon_s *)menu->items[menu->numItems])->parent = menu;
	((menuCommon_s *)menu->items[menu->numItems])->flags &= ~QMF_HASMOUSEFOCUS;
	menu->numItems++;

	switch( generic->type )
	{
	case QMTYPE_SCROLLLIST:
		UI_ScrollList_Init((menuScrollList_s *)item );
		break;
	case QMTYPE_SPINCONTROL:
		UI_SpinControl_Init((menuSpinControl_s *)item );
		break;
	case QMTYPE_CHECKBOX:
		UI_CheckBox_Init((menuCheckBox_s *)item );
		break;
	case QMTYPE_SLIDER:
		UI_Slider_Init((menuSlider_s *)item );
		break;
	case QMTYPE_FIELD:
		UI_Field_Init((menuField_s *)item );
		break;
	case QMTYPE_ACTION:
		UI_Action_Init((menuAction_s *)item );
		break;
	case QMTYPE_BITMAP:
		UI_Bitmap_Init((menuBitmap_s *)item );
		break;
	case QMTYPE_BM_BUTTON:
		UI_PicButton_Init((menuPicButton_s *)item );
		break;
	default:
		HOST_ERROR( "UI_AddItem: unknown item type (%i)\n", generic->type );
	}
}

/*
=================
UI_CursorMoved
=================
*/
void UI_CursorMoved( menuFramework_s *menu )
{
	void (*callback)( void *self, int event );

	if( menu->cursor == menu->cursorPrev )
		return;

	if( menu->cursorPrev >= 0 && menu->cursorPrev < menu->numItems )
	{
		callback = ((menuCommon_s *)menu->items[menu->cursorPrev])->callback;
		if( callback ) callback( menu->items[menu->cursorPrev], QM_LOSTFOCUS );
	}

	if( menu->cursor >= 0 && menu->cursor < menu->numItems )
	{
		callback = ((menuCommon_s *)menu->items[menu->cursor])->callback;
		if( callback ) callback( menu->items[menu->cursor], QM_GOTFOCUS );
	}
}

/*
=================
UI_SetCursor
=================
*/
void UI_SetCursor( menuFramework_s *menu, int cursor )
{
	if(((menuCommon_s *)(menu->items[cursor]))->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN))
		return;

	menu->cursorPrev = menu->cursor;
	menu->cursor = cursor;

	UI_CursorMoved( menu );
}

/*
=================
UI_SetCursorToItem
=================
*/
void UI_SetCursorToItem( menuFramework_s *menu, void *item )
{
	for( int i = 0; i < menu->numItems; i++ )
	{
		if( menu->items[i] == item )
		{
			UI_SetCursor( menu, i );
			return;
		}
	}
}

/*
=================
UI_ItemAtCursor
=================
*/
void *UI_ItemAtCursor( menuFramework_s *menu )
{
	if( menu->cursor < 0 || menu->cursor >= menu->numItems )
		return 0;

	// inactive items can't be has focus
	if( ((menuCommon_s *)menu->items[menu->cursor])->flags & QMF_INACTIVE )
		return 0;

	return menu->items[menu->cursor];
}

/*
=================
UI_AdjustCursor

This functiont takes the given menu, the direction, and attempts to
adjust the menu's cursor so that it's at the next available slot
=================
*/
void UI_AdjustCursor( menuFramework_s *menu, int dir )
{
	menuCommon_s	*item;
	int		wrapped = false;
wrap:
	while( menu->cursor >= 0 && menu->cursor < menu->numItems )
	{
		item = (menuCommon_s *)menu->items[menu->cursor];
		if( item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN|QMF_MOUSEONLY))
			menu->cursor += dir;
		else break;
	}

	if( dir == 1 )
	{
		if( menu->cursor >= menu->numItems )
		{
			if( wrapped )
			{
				menu->cursor = menu->cursorPrev;
				return;
			}

			menu->cursor = 0;
			wrapped = true;
			goto wrap;
		}
	}
	else if( dir == -1 )
	{
		if( menu->cursor < 0 )
		{
			if( wrapped )
			{
				menu->cursor = menu->cursorPrev;
				return;
			}
			menu->cursor = menu->numItems - 1;
			wrapped = true;
			goto wrap;
		}
	}
}

/*
=================
UI_DrawMenu
=================
*/
void UI_DrawMenu( menuFramework_s *menu )
{
	static long	statusFadeTime;
	static menuCommon_s	*lastItem;
	menuCommon_s	*item;
	int		i;

	// draw contents
	for( i = 0; i < menu->numItems; i++ )
	{
		item = (menuCommon_s *)menu->items[i];

		if( item->flags & QMF_HIDDEN )
			continue;

		if( item->ownerdraw )
		{
			// total subclassing, owner draws everything
			item->ownerdraw( item );
			continue;
		}

		switch( item->type )
		{
		case QMTYPE_SCROLLLIST:
			UI_ScrollList_Draw((menuScrollList_s *)item );
			break;
		case QMTYPE_SPINCONTROL:
			UI_SpinControl_Draw((menuSpinControl_s *)item );
			break;
		case QMTYPE_CHECKBOX:
			UI_CheckBox_Draw((menuCheckBox_s *)item );
			break;
		case QMTYPE_SLIDER:
			UI_Slider_Draw((menuSlider_s *)item );
			break;
		case QMTYPE_FIELD:
			UI_Field_Draw((menuField_s *)item );
			break;
		case QMTYPE_ACTION:
			UI_Action_Draw((menuAction_s *)item );
			break;
		case QMTYPE_BITMAP:
			UI_Bitmap_Draw((menuBitmap_s *)item );
			break;
		case QMTYPE_BM_BUTTON:
			UI_PicButton_Draw((menuPicButton_s *)item );
			break;
		}
	}

	// draw status bar
	item = (menuCommon_s *)UI_ItemAtCursor( menu );
	if( item != lastItem )
	{
		// flash on selected button (like in GoldSrc)
		if( item ) item->lastFocusTime = uiStatic.realTime;
		statusFadeTime = uiStatic.realTime;
		lastItem = item;
	}

	if( item && ( item->flags & QMF_HASMOUSEFOCUS && !( item->flags & QMF_NOTIFY )) && ( item->statusText != NULL ))
	{
		// fade it in, but wait a second
		int alpha = bound( 0, ((( uiStatic.realTime - statusFadeTime ) - 1000 ) * 0.001f ) * 255, 255 );
		int r, g, b, x, len;

		GetConsoleStringSize( item->statusText, &len, NULL );

		UnpackRGB( r, g, b, uiColorHelp );
		TextMessageSetColor( r, g, b, alpha );
		x = ( ScreenWidth - len ) * 0.5; // centering

		DrawConsoleString( x, 720 * uiStatic.scaleY, item->statusText );
	}
	else statusFadeTime = uiStatic.realTime;
}

/*
=================
UI_DefaultKey
=================
*/
const char *UI_DefaultKey( menuFramework_s *menu, int key, int down )
{
	const char	*sound = NULL;
	menuCommon_s	*item;
	int		cursorPrev;

	// menu system key
	if( down && key == K_ESCAPE )
	{
		UI_PopMenu();
		return uiSoundOut;
	}

	if( !menu || !menu->numItems )
		return 0;

	item = (menuCommon_s *)UI_ItemAtCursor( menu );
	if( item && !(item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN)))
	{
		switch( item->type )
		{
		case QMTYPE_SCROLLLIST:
			sound = UI_ScrollList_Key((menuScrollList_s *)item, key, down );
			break;
		case QMTYPE_SPINCONTROL:
			sound = UI_SpinControl_Key((menuSpinControl_s *)item, key, down );
			break;
		case QMTYPE_CHECKBOX:
			sound = UI_CheckBox_Key((menuCheckBox_s *)item, key, down );
			break;
		case QMTYPE_SLIDER:
			sound = UI_Slider_Key((menuSlider_s *)item, key, down );
			break;
		case QMTYPE_FIELD:
			sound = UI_Field_Key((menuField_s *)item, key, down );
			break;
		case QMTYPE_ACTION:
			sound = UI_Action_Key((menuAction_s *)item, key, down );
			break;
		case QMTYPE_BITMAP:
			sound = UI_Bitmap_Key((menuBitmap_s *)item, key, down );
			break;
		case QMTYPE_BM_BUTTON:
			sound = UI_PicButton_Key((menuPicButton_s *)item, key, down );
			break;
		}
		if( sound ) return sound; // key was handled
	}

	// system keys are always wait for keys down and never keys up
	if( !down ) return 0;

	// default handling
	switch( key )
	{
	case K_UPARROW:
	case K_KP_UPARROW:
	case K_LEFTARROW:
	case K_KP_LEFTARROW:
		cursorPrev = menu->cursor;
		menu->cursorPrev = menu->cursor;
		menu->cursor--;

		UI_AdjustCursor( menu, -1 );
		if( cursorPrev != menu->cursor )
		{
			UI_CursorMoved( menu );
			if( !(((menuCommon_s *)menu->items[menu->cursor])->flags & QMF_SILENT ))
				sound = uiSoundMove;
		}
		break;
	case K_DOWNARROW:
	case K_KP_DOWNARROW:
	case K_RIGHTARROW:
	case K_KP_RIGHTARROW:
	case K_TAB:
		cursorPrev = menu->cursor;
		menu->cursorPrev = menu->cursor;
		menu->cursor++;

		UI_AdjustCursor(menu, 1);
		if( cursorPrev != menu->cursor )
		{
			UI_CursorMoved(menu);
			if( !(((menuCommon_s *)menu->items[menu->cursor])->flags & QMF_SILENT ))
				sound = uiSoundMove;
		}
		break;
	case K_MOUSE1:
		if( item )
		{
			if((item->flags & QMF_HASMOUSEFOCUS) && !(item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN)))
				return UI_ActivateItem( menu, item );
		}

		break;
	case K_ENTER:
	case K_KP_ENTER:
		if( item )
		{
			if( !(item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN|QMF_MOUSEONLY)))
				return UI_ActivateItem( menu, item );
		}
		break;
	}
	return sound;
}		

/*
=================
UI_ActivateItem
=================
*/
const char *UI_ActivateItem( menuFramework_s *menu, menuCommon_s *item )
{
	if( item->callback )
	{
		item->callback( item, QM_ACTIVATED );

		if( !( item->flags & QMF_SILENT ))
			return uiSoundMove;
	}
	return 0;
}

/*
=================
UI_RefreshServerList
=================
*/
void UI_RefreshServerList( void )
{
	uiStatic.numServers = 0;
	memset( uiStatic.serverAddresses, 0, sizeof( uiStatic.serverAddresses ));
	memset( uiStatic.serverNames, 0, sizeof( uiStatic.serverNames ));

	CLIENT_COMMAND( FALSE, "localservers\n" );
}

/*
=================
UI_RefreshInternetServerList
=================
*/
void UI_RefreshInternetServerList( void )
{
	uiStatic.numServers = 0;
	memset( uiStatic.serverAddresses, 0, sizeof( uiStatic.serverAddresses ));
	memset( uiStatic.serverNames, 0, sizeof( uiStatic.serverNames ));

	CLIENT_COMMAND( FALSE, "internetservers\n" );
}

/*
=================
UI_StartBackGroundMap
=================
*/
bool UI_StartBackGroundMap( void )
{
	static bool	first = TRUE;

	if( !first ) return FALSE;

	first = FALSE;

	// some map is already running
	if( !uiStatic.bgmapcount || CL_IsActive() || gpGlobals->demoplayback )
		return FALSE;

	int bgmapid = RANDOM_LONG( 0, uiStatic.bgmapcount - 1 );

	char cmd[128];
	sprintf( cmd, "maps/%s.bsp", uiStatic.bgmaps[bgmapid] );
	if( !FILE_EXISTS( cmd )) return FALSE; 

	sprintf( cmd, "map_background %s\n", uiStatic.bgmaps[bgmapid] );
	CLIENT_COMMAND( FALSE, cmd );

	return TRUE;
}

// =====================================================================

/*
=================
UI_CloseMenu
=================
*/
void UI_CloseMenu( void )
{
	uiStatic.menuActive = NULL;
	uiStatic.menuDepth = 0;
	uiStatic.visible = false;

	// clearing serverlist
	uiStatic.numServers = 0;
	memset( uiStatic.serverAddresses, 0, sizeof( uiStatic.serverAddresses ));
	memset( uiStatic.serverNames, 0, sizeof( uiStatic.serverNames ));

	UI_ClearButtonStack ();

//	KEY_ClearStates ();
	KEY_SetDest ( KEY_GAME );
}

/*
=================
UI_PushMenu
=================
*/
void UI_PushMenu( menuFramework_s *menu )
{
	int		i;
	menuCommon_s	*item;

	// if this menu is already present, drop back to that level to avoid stacking menus by hotkeys
	for( i = 0; i < uiStatic.menuDepth; i++ )
	{
		if( uiStatic.menuStack[i] == menu )
		{
			uiStatic.menuDepth = i;
			break;
		}
	}

	if( i == uiStatic.menuDepth )
	{
		if( uiStatic.menuDepth >= UI_MAX_MENUDEPTH )
			HOST_ERROR( "UI_PushMenu: menu stack overflow\n" );
		uiStatic.menuStack[uiStatic.menuDepth++] = menu;
	}

	uiStatic.menuActive = menu;
	uiStatic.firstDraw = true;
	uiStatic.enterSound = gpGlobals->time + 0.15;	// make some delay
	uiStatic.visible = true;

	KEY_SetDest ( KEY_MENU );

	menu->cursor = 0;
	menu->cursorPrev = 0;

	// force first available item to have focus
	for( i = 0; i < menu->numItems; i++ )
	{
		item = (menuCommon_s *)menu->items[i];

		if( item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN|QMF_MOUSEONLY))
			continue;

		menu->cursorPrev = -1;
		UI_SetCursor( menu, i );
		break;
	}
}

/*
=================
UI_PopMenu
=================
*/
void UI_PopMenu( void )
{
	UI_StartSound( uiSoundOut );

	uiStatic.menuDepth--;

	if( uiStatic.menuDepth < 0 )
		HOST_ERROR( "UI_PopMenu: menu stack underflow\n" );

	UI_PopPButtonStack();

	if( uiStatic.menuDepth )
	{
		uiStatic.menuActive = uiStatic.menuStack[uiStatic.menuDepth-1];
		uiStatic.firstDraw = true;
	}
	else if ( CL_IsActive( ))
	{
		UI_CloseMenu();
	}
	else
	{
		// never trying the close menu when client isn't connected
		KEY_SetDest( KEY_MENU );
		UI_Main_Menu();
	}

	if( uiStatic.m_fDemosPlayed && uiStatic.m_iOldMenuDepth == uiStatic.menuDepth )
	{
		CLIENT_COMMAND( FALSE, "demos\n" );
		uiStatic.m_fDemosPlayed = false;
		uiStatic.m_iOldMenuDepth = 0;
	}
}

// =====================================================================

/*
=================
UI_UpdateMenu
=================
*/
void UI_UpdateMenu( float flTime )
{
	if( !uiStatic.initialized )
		return;

	UI_DrawFinalCredits ();

	if( !uiStatic.visible )
		return;

	if( !uiStatic.menuActive )
		return;

	uiStatic.realTime = flTime * 1000;
	uiStatic.framecount++;

	if( CVAR_GET_FLOAT( "cl_background" ) && !g_engfuncs.pfnClientInGame())
		return;	// don't draw menu while level is loading

	if( uiStatic.firstDraw )
	{
		// we loading background so skip SCR_Update
		if( UI_StartBackGroundMap( )) return;

		if( uiStatic.menuActive->activateFunc )
			uiStatic.menuActive->activateFunc();
	}

	// draw menu
	if( uiStatic.menuActive->drawFunc )
		uiStatic.menuActive->drawFunc();
	else UI_DrawMenu( uiStatic.menuActive );

	if( uiStatic.firstDraw )
	{
		uiStatic.firstDraw = false;
		static int first = TRUE;
                    
		if( first )
		{
			// if game was launched with commandline e.g. +map or +load ignore the music
			if( !CL_IsActive( ))
				BACKGROUND_TRACK( "gamestartup", "gamestartup" );
			first = FALSE;
		}
	}

	//CR
	UI_DrawTitleAnim();
	//

	// draw cursor
	UI_DrawMouseCursor();

	// delay playing the enter sound until after the menu has been
	// drawn, to avoid delay while caching images
	if( uiStatic.enterSound > 0.0f && uiStatic.enterSound <= gpGlobals->time )
	{
		UI_StartSound( uiSoundIn );
		uiStatic.enterSound = -1;
	}
}

/*
=================
UI_KeyEvent
=================
*/
void UI_KeyEvent( int key, int down )
{
	const char	*sound;

	if( !uiStatic.initialized )
		return;

	if( !uiStatic.visible )
		return;

	if( !uiStatic.menuActive )
		return;

	if( uiStatic.menuActive->keyFunc )
		sound = uiStatic.menuActive->keyFunc( key, down );
	else sound = UI_DefaultKey( uiStatic.menuActive, key, down );

	if( !down ) return;
	if( sound && sound != uiSoundNull )
		UI_StartSound( sound );
}

/*
=================
UI_CharEvent
=================
*/
void UI_CharEvent( int key )
{
	menuFramework_s	*menu;
	menuCommon_s	*item;

	if( !uiStatic.initialized )
		return;

	if( !uiStatic.visible )
		return;

	if( !uiStatic.menuActive )
		return;

	menu = uiStatic.menuActive;

	if( !menu || !menu->numItems )
		return;

	item = (menuCommon_s *)UI_ItemAtCursor( menu );

	if( item && !(item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN)))
	{
		switch( item->type )
		{
		case QMTYPE_FIELD:
			UI_Field_Char((menuField_s *)item, key );
			break;
		}
	}
}

/*
=================
UI_MouseMove
=================
*/
void UI_MouseMove( int x, int y )
{
	int		i;
	menuCommon_s	*item;

	if( !uiStatic.initialized )
		return;

	if( !uiStatic.visible )
		return;

	if( !uiStatic.menuActive )
		return;

	// now menu uses absolute coordinates
	uiStatic.cursorX = x;
	uiStatic.cursorY = y;

	if( UI_CursorInRect( 1, 1, ScreenWidth - 1, ScreenHeight - 1 ))
		uiStatic.mouseInRect = true;
	else uiStatic.mouseInRect = false;

	uiStatic.cursorX = bound( 0, uiStatic.cursorX, ScreenWidth );
	uiStatic.cursorY = bound( 0, uiStatic.cursorY, ScreenHeight );

	// region test the active menu items
	for( i = 0; i < uiStatic.menuActive->numItems; i++ )
	{
		item = (menuCommon_s *)uiStatic.menuActive->items[i];

		if( item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN))
		{
			if( item->flags & QMF_HASMOUSEFOCUS )
			{
				if( !UI_CursorInRect( item->x, item->y, item->width, item->height ))
					item->flags &= ~QMF_HASMOUSEFOCUS;
				else item->lastFocusTime = uiStatic.realTime;
			}
			continue;
		}

		if( !UI_CursorInRect( item->x, item->y, item->width, item->height ))
		{
			item->bPressed = false;
			continue;
		}

		// set focus to item at cursor
		if( uiStatic.menuActive->cursor != i )
		{
			UI_SetCursor( uiStatic.menuActive, i );
			((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursorPrev]))->flags &= ~QMF_HASMOUSEFOCUS;

			if (!(((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->flags & QMF_SILENT ))
				UI_StartSound( uiSoundMove );
		}

		((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->flags |= QMF_HASMOUSEFOCUS;
		((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->lastFocusTime = uiStatic.realTime;
		return;
	}

	// out of any region
	if( uiStatic.menuActive->numItems )
	{
		((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->flags &= ~QMF_HASMOUSEFOCUS;
		((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->bPressed = false;

		// a mouse only item restores focus to the previous item
		if(((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->flags & QMF_MOUSEONLY )
		{
			if( uiStatic.menuActive->cursorPrev != -1 )
				uiStatic.menuActive->cursor = uiStatic.menuActive->cursorPrev;
		}
	}
}

/*
=================
UI_SetActiveMenu
=================
*/
void UI_SetActiveMenu( int fActive )
{
	if( !uiStatic.initialized )
		return;

	// don't continue firing if we leave game
	KEY_ClearStates();
	uiStatic.framecount = 0;

	if( fActive )
	{
		KEY_SetDest( KEY_MENU );
		UI_Main_Menu();
	}
	else
	{
		UI_CloseMenu();
	}
}

/*
=================
UI_AddServerToList
=================
*/
void UI_AddServerToList( netadr_t adr, const char *info )
{
	int	i;

	if( !uiStatic.initialized )
		return;

	if( uiStatic.numServers == UI_MAX_SERVERS )
		return;	// full

	if( stricmp( gMenu.m_gameinfo.gamefolder, Info_ValueForKey( info, "gamedir" )))
		return;

	// ignore if duplicated
	for( i = 0; i < uiStatic.numServers; i++ )
	{
		if( !stricmp( uiStatic.serverNames[i], info ))
			return;
	}

	// add it to the list
	uiStatic.updateServers = true; // info has been updated
	uiStatic.serverAddresses[uiStatic.numServers] = adr;
	strncpy( uiStatic.serverNames[uiStatic.numServers], info, sizeof( uiStatic.serverNames[uiStatic.numServers] ));
	uiStatic.numServers++;
}

/*
=================
UI_IsVisible

Some systems may need to know if it is visible or not
=================
*/
int UI_IsVisible( void )
{
	if( !uiStatic.initialized )
		return false;
	return uiStatic.visible;
}

void UI_GetCursorPos( int *pos_x, int *pos_y )
{
	if( pos_x ) *pos_x = uiStatic.cursorX;
	if( pos_y ) *pos_y = uiStatic.cursorY;
}

void UI_SetCursorPos( int pos_x, int pos_y )
{
//	uiStatic.cursorX = bound( 0, pos_x, ScreenWidth );
//	uiStatic.cursorY = bound( 0, pos_y, ScreenHeight );
	uiStatic.mouseInRect = true;
}

void UI_ShowCursor( int show )
{
	uiStatic.hideCursor = (show) ? false : true;
}

int UI_MouseInRect( void )
{
	return uiStatic.mouseInRect;
}

/*
=================
UI_Precache
=================
*/
void UI_Precache( void )
{
	if( !uiStatic.initialized )
		return;

	if( !ui_precache->value )
		return;

	PIC_Load( UI_LEFTARROW );
	PIC_Load( UI_LEFTARROWFOCUS );
	PIC_Load( UI_RIGHTARROW );
	PIC_Load( UI_RIGHTARROWFOCUS );
	PIC_Load( UI_UPARROW );
	PIC_Load( UI_UPARROWFOCUS );
	PIC_Load( UI_DOWNARROW );
	PIC_Load( UI_DOWNARROWFOCUS );

	if( ui_precache->value == 1 )
		return;

	UI_Main_Precache();
	UI_NewGame_Precache();
	UI_LoadGame_Precache();
	UI_SaveGame_Precache();
	UI_SaveLoad_Precache();
	UI_MultiPlayer_Precache();
	UI_Options_Precache();
	UI_InternetGames_Precache();
	UI_LanGame_Precache();
	UI_PlayerSetup_Precache();
	UI_Controls_Precache();
	UI_AdvControls_Precache();
	UI_GameOptions_Precache();
	UI_CreateGame_Precache();
	UI_Audio_Precache();
	UI_Video_Precache();
	UI_VidOptions_Precache();
	UI_VidModes_Precache();
	UI_CustomGame_Precache();
	UI_Credits_Precache();
}

void UI_ParseColor( char *&pfile, int *outColor )
{
	int	i, color[3];
	char	token[1024];

	memset( color, 0xFF, sizeof( color ));

	for( i = 0; i < 3; i++ )
	{
		pfile = COM_ParseFile( pfile, token );
		if( !pfile ) break;
		color[i] = atoi( token );
	}

	*outColor = PackRGB( color[0], color[1], color[2] );
}

void UI_ApplyCustomColors( void )
{
	char *afile = (char *)LOAD_FILE( "gfx/shell/colors.lst", NULL );
	char *pfile = afile;
	char token[1024];

	if( !afile )
	{
		// not error, not warning, just notify
		Con_Printf( "UI_SetColors: colors.lst not found\n" );
		return;
	}

	while(( pfile = COM_ParseFile( pfile, token )) != NULL )
	{
		if( !stricmp( token, "HELP_COLOR" ))
		{
			UI_ParseColor( pfile, &uiColorHelp );
		}
		else if( !stricmp( token, "PROMPT_BG_COLOR" ))
		{
			UI_ParseColor( pfile, &uiPromptBgColor );
		}
		else if( !stricmp( token, "PROMPT_TEXT_COLOR" ))
		{
			UI_ParseColor( pfile, &uiPromptTextColor );
		}
		else if( !stricmp( token, "PROMPT_FOCUS_COLOR" ))
		{
			UI_ParseColor( pfile, &uiPromptFocusColor );
		}
		else if( !stricmp( token, "INPUT_TEXT_COLOR" ))
		{
			UI_ParseColor( pfile, &uiInputTextColor );
		}
		else if( !stricmp( token, "INPUT_BG_COLOR" ))
		{
			UI_ParseColor( pfile, &uiInputBgColor );
		}
		else if( !stricmp( token, "INPUT_FG_COLOR" ))
		{
			UI_ParseColor( pfile, &uiInputFgColor );
		}
		else if( !stricmp( token, "CON_TEXT_COLOR" ))
		{
			UI_ParseColor( pfile, &uiColorConsole );
		}
	}

	int	r, g, b;

	UnpackRGB( r, g, b, uiColorConsole );
	ConsoleSetColor( r, g, b );

	FREE_FILE( afile );
}

static void UI_LoadBackgroundMapList( void )
{
	if( !g_engfuncs.pfnFileExists( "scripts/chapterbackgrounds.txt", TRUE ))
		return;

	char *afile = (char *)LOAD_FILE( "scripts/chapterbackgrounds.txt", NULL );
	char *pfile = afile;
	char token[1024];

	uiStatic.bgmapcount = 0;

	if( !afile )
	{
		Con_Printf( "UI_LoadBackgroundMapList: chapterbackgrounds.txt not found\n" );
		return;
	}

	while(( pfile = COM_ParseFile( pfile, token )) != NULL )
	{
		// skip the numbers (old format list)
		if( isdigit( token[0] )) continue;

		strncpy( uiStatic.bgmaps[uiStatic.bgmapcount], token, sizeof( uiStatic.bgmaps[0] ));
		if( ++uiStatic.bgmapcount > UI_MAX_BGMAPS )
			break; // list is full
	}

	FREE_FILE( afile );
}

/*
=================
UI_VidInit
=================
*/
int UI_VidInit( void )
{
	UI_Precache ();
		
	uiStatic.scaleX = ScreenWidth / 1024.0f;
	uiStatic.scaleY = ScreenHeight / 768.0f;

	// move cursor to screen center
	uiStatic.cursorX = ScreenWidth >> 1;
	uiStatic.cursorY = ScreenHeight >> 1;
	uiStatic.outlineWidth = 4;
	uiStatic.sliderWidth = 6;

	// all menu buttons have the same view sizes
	uiStatic.buttons_draw_width = UI_BUTTONS_WIDTH;
	uiStatic.buttons_draw_height = UI_BUTTONS_HEIGHT;

	UI_ScaleCoords( NULL, NULL, &uiStatic.outlineWidth, NULL );
	UI_ScaleCoords( NULL, NULL, &uiStatic.sliderWidth, NULL );
	UI_ScaleCoords( NULL, NULL, &uiStatic.buttons_draw_width, &uiStatic.buttons_draw_height );

	// trying to load colors.lst
	UI_ApplyCustomColors ();

	// trying to load chapterbackgrounds.txt
	UI_LoadBackgroundMapList ();

	// register menu font
	uiStatic.hFont = PIC_Load( "#XASH_SYSTEMFONT_001.bmp", menufont_bmp, sizeof( menufont_bmp ));

	UI_LoadBackgroundImage ();
#if 0
	FILE *f;

	// dump menufont onto disk
	f = fopen( "menufont.bmp", "wb" );
	fwrite( menufont_bmp, sizeof( menufont_bmp ), 1, f );
	fclose( f );
#endif

	// reload all menu buttons
	UI_LoadBmpButtons ();

	// now recalc all the menus in stack
	for( int i = 0; i < uiStatic.menuDepth; i++ )
	{
		menuFramework_s *item = uiStatic.menuStack[i];

		// do vid restart for all pushed elements
		if( item && item->vidInitFunc )
			item->vidInitFunc();
	}

	return 1;
}

/*
=================
UI_Init
=================
*/
void UI_Init( void )
{
	// register our cvars and commands
	ui_precache = CVAR_REGISTER( "ui_precache", "0", FCVAR_ARCHIVE );
	ui_showmodels = CVAR_REGISTER( "ui_showmodels", "0", FCVAR_ARCHIVE );

	Cmd_AddCommand( "menu_main", UI_Main_Menu );
	Cmd_AddCommand( "menu_newgame", UI_NewGame_Menu );
	Cmd_AddCommand( "menu_loadgame", UI_LoadGame_Menu );
	Cmd_AddCommand( "menu_savegame", UI_SaveGame_Menu );
	Cmd_AddCommand( "menu_saveload", UI_SaveLoad_Menu );
	Cmd_AddCommand( "menu_multiplayer", UI_MultiPlayer_Menu );
	Cmd_AddCommand( "menu_options", UI_Options_Menu );
	Cmd_AddCommand( "menu_langame", UI_LanGame_Menu );
	Cmd_AddCommand( "menu_internetgames", UI_InternetGames_Menu );
	Cmd_AddCommand( "menu_playersetup", UI_PlayerSetup_Menu );
	Cmd_AddCommand( "menu_controls", UI_Controls_Menu );
	Cmd_AddCommand( "menu_advcontrols", UI_AdvControls_Menu );
	Cmd_AddCommand( "menu_gameoptions", UI_GameOptions_Menu );
	Cmd_AddCommand( "menu_creategame", UI_CreateGame_Menu );
	Cmd_AddCommand( "menu_audio", UI_Audio_Menu );
	Cmd_AddCommand( "menu_video", UI_Video_Menu );
	Cmd_AddCommand( "menu_vidoptions", UI_VidOptions_Menu );
	Cmd_AddCommand( "menu_vidmodes", UI_VidModes_Menu );
	Cmd_AddCommand( "menu_customgame", UI_CustomGame_Menu );

	CHECK_MAP_LIST( TRUE );

	memset( uiEmptyString, ' ', sizeof( uiEmptyString ));	// HACKHACK
	uiStatic.initialized = true;

	// setup game info
	GetGameInfo( &gMenu.m_gameinfo );

	// load custom strings
	UI_LoadCustomStrings();

	//CR
	UI_InitTitleAnim();
}

/*
=================
UI_Shutdown
=================
*/
void UI_Shutdown( void )
{
	if( !uiStatic.initialized )
		return;

	Cmd_RemoveCommand( "menu_main" );
	Cmd_RemoveCommand( "menu_newgame" );
	Cmd_RemoveCommand( "menu_loadgame" );
	Cmd_RemoveCommand( "menu_savegame" );
	Cmd_RemoveCommand( "menu_saveload" );
	Cmd_RemoveCommand( "menu_multiplayer" );
	Cmd_RemoveCommand( "menu_options" );
	Cmd_RemoveCommand( "menu_internetgames" );
	Cmd_RemoveCommand( "menu_langame" );
	Cmd_RemoveCommand( "menu_playersetup" );
	Cmd_RemoveCommand( "menu_controls" );
	Cmd_RemoveCommand( "menu_advcontrols" );
	Cmd_RemoveCommand( "menu_gameoptions" );
	Cmd_RemoveCommand( "menu_creategame" );
	Cmd_RemoveCommand( "menu_audio" );
	Cmd_RemoveCommand( "menu_video" );
	Cmd_RemoveCommand( "menu_vidoptions" );
	Cmd_RemoveCommand( "menu_vidmodes" );
	Cmd_RemoveCommand( "menu_advanced" );
	Cmd_RemoveCommand( "menu_performance" );
	Cmd_RemoveCommand( "menu_network" );
	Cmd_RemoveCommand( "menu_defaults" );
	Cmd_RemoveCommand( "menu_cinematics" );
	Cmd_RemoveCommand( "menu_customgame" );

	memset( &uiStatic, 0, sizeof( uiStatic_t ));
}
