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

*/

#include "ui_local.h"

void M_RefreshScrollWindowList( void );

//pinging
#define PING_ONCE
static unsigned int nextServerTime = 0;
static unsigned int nextPingTime = 0;

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

JOIN SERVER MENU

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

static menuframework_s s_joinserver_menu;

static menucommon_t *menuitem_fullfilter;
static menucommon_t *menuitem_emptyfilter;
static menucommon_t *menuitem_skillfilter;
static menucommon_t *menuitem_passwordfilter;
static menucommon_t *menuitem_gametypefilter;
static menucommon_t *menuitem_maxpingfilter;
#ifdef BATTLEYE
static menucommon_t *menuitem_battleyefilter;
#endif

typedef struct server_s
{
	char		hostname[80];
	char		map[80];
	int			curuser;
	int			maxuser;
	int			bots;
	char		gametype[80];
	int			skilllevel;
	int			id;
	qboolean	password;
	unsigned int ping;
	unsigned int ping_retries;
	qboolean	displayed;
#ifdef BATTLEYE
	int			battleye;
#endif
	char		address[80];

	struct server_s *pnext;

} server_t;

server_t		*servers;
int				numServers;

char	ui_responseToken[MAX_TOKEN_CHARS];

//==================
//UI_GetResponseToken
//==================
char *UI_GetResponseToken( char **data_p )
{
	int		c;
	int		len;
	unsigned backlen;
	char	*data;

	data = *data_p;
	len = 0;
	ui_responseToken[0] = 0;

	if( !data )
	{
		*data_p = NULL;
		return "";
	}

	backlen = strlen( data );
	if( backlen < strlen("\\EOT") )
	{
		*data_p = NULL;
		return "";
	}

skipbackslash:
	c = *data;
	if( c == '\\' ) {
		if( data[1] == '\\' ) {
			data += 2;
			goto skipbackslash;
		}
	}

	if( !c ) {
		*data_p = NULL;
		return "";
	}

	do
	{
		if( len < MAX_TOKEN_CHARS ) {
			ui_responseToken[len] = c;
			len++;
		}
		data++;
		c = *data;
	}while( c && c != '\\' );

	if( len == MAX_TOKEN_CHARS ) {
		len = 0;
	}
	ui_responseToken[len] = 0;

	*data_p = data;
	return ui_responseToken;
}

//==================
//FreeServerlist
//==================
void M_FreeServerlist( void )
{
	server_t *ptr;

	while( servers ) {
		ptr = servers;
		servers = ptr->pnext;
		UI_Free(ptr);
	}

	servers = NULL;
	numServers = 0;

	M_RefreshScrollWindowList();
}

//==================
//M_RegisterServerItem
//==================
server_t *M_RegisterServerItem( char *adr, char *info )
{
	server_t *newserv, *checkserv;

	//check for the address being already in the list
	checkserv = servers;
	while( checkserv ) {
		if( !Q_stricmp( adr, checkserv->address ) )
			return checkserv;
		checkserv = checkserv->pnext;
	}

	newserv = (server_t*)UI_Malloc(sizeof(server_t));

	Q_strncpyz( newserv->address, adr, sizeof(newserv->address) );

	// fill in fields with empty msgs, in case any of them isn't received
	Q_snprintfz( newserv->hostname, sizeof(newserv->hostname), "Unnamed Server" );
	Q_snprintfz( newserv->map, sizeof(newserv->map), "Unknown" );
	Q_snprintfz( newserv->gametype, sizeof(newserv->gametype), "Unknown" );
	newserv->curuser = -1; newserv->maxuser = -1;
	newserv->skilllevel = 1;
	newserv->password = qfalse;
	newserv->bots = 0;
	newserv->ping = 9999;
	newserv->ping_retries = 0;
#ifdef BATTLEYE
	newserv->battleye = 0;
#endif

	// set up the actual new item
	newserv->pnext = servers;
	servers = newserv;
	newserv->id = numServers;
	numServers++;

	return newserv;
}

//==================
//M_AddToServerList
// The client sent us a new server adress for our list
//==================
void M_AddToServerList( char *adr, char *info )
{
	server_t *newserv;
	int		l;
	char	*pntr;
	char	*tok;
	qboolean need_refresh = qfalse;

	newserv = M_RegisterServerItem( adr, info );

	// parse the info string
	pntr = info;
	if( strstr(pntr, "\\") == NULL ) // temp: check for old formatting
	{
		pntr = info;
		l = strlen(pntr) - 15;
		Q_snprintfz( newserv->hostname, l, "%s", pntr );
		newserv->hostname[l] = '\0';
		pntr += l;
		l = strlen(pntr) - 6;
		Q_snprintfz( newserv->map, l, "%s", pntr );
		newserv->map[l] = '\0';
		pntr += l;
		sscanf( pntr, "%d/%d", &newserv->curuser, &newserv->maxuser );

	} else { // new formatting
		pntr = info;

		while( &pntr ) {
			tok = UI_GetResponseToken( &pntr );
			if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
				break;

			// got cmd
			if( !Q_stricmp(tok, "n") ) {
				tok = UI_GetResponseToken( &pntr );
				if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
					break;

				if( Q_stricmp( newserv->hostname, tok ) ) {
					need_refresh = qtrue;
					Q_snprintfz( newserv->hostname, sizeof(newserv->hostname), tok );
				}
				continue;
			}

			// got cmd
			if( !Q_stricmp(tok, "m") ) {
				tok = UI_GetResponseToken( &pntr );
				if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
					break;

				while( *tok == ' ' )tok++; // remove spaces in front of the gametype name

				if( Q_stricmp( newserv->map, tok ) ) {
					need_refresh = qtrue;
					Q_snprintfz( newserv->map, sizeof(newserv->map), tok );
				}
				continue;
			}

			// got cmd
			if( !Q_stricmp(tok, "u") ) {
				int curuser, maxuser;
				tok = UI_GetResponseToken( &pntr );
				if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
					break;

				sscanf( tok, "%d/%d", &curuser, &maxuser );
				if( curuser != newserv->curuser || maxuser != newserv->maxuser ) {
					need_refresh = qtrue;
					newserv->curuser = curuser;
					newserv->maxuser = maxuser;
				}
				continue;
			}

			// got cmd
			if( !Q_stricmp(tok, "b") ) {
				int bots;
				tok = UI_GetResponseToken( &pntr );
				if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
					break;
				bots = atoi(tok);
				if( bots != newserv->bots ) {
					need_refresh = qtrue;
					newserv->bots = bots;
				}
				continue;
			}

			// got cmd
			if( !Q_stricmp(tok, "g") ) {
				tok = UI_GetResponseToken( &pntr );
				if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
					break;

				while( *tok == ' ' )tok++; // remove spaces in front of the gametype name

				if( Q_stricmp( newserv->gametype, tok ) ) {
					need_refresh = qtrue;
					Q_snprintfz( newserv->gametype, sizeof(newserv->gametype), tok );
				}
				continue;
			}

			// got cmd
			if( !Q_stricmp(tok, "s") ) {
				int skilllevel;
				tok = UI_GetResponseToken( &pntr );
				if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
					break;
				skilllevel = atoi(tok);
				if( skilllevel != newserv->skilllevel ) {
					need_refresh = qtrue;
					newserv->skilllevel = skilllevel;
				}
				continue;
			}

			// got cmd
			if( !Q_stricmp(tok, "p") ) {
				int password;
				tok = UI_GetResponseToken( &pntr );
				if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
					break;
				password = (atoi(tok));
				if( password != newserv->password ) {
					need_refresh = qtrue;
					newserv->password = password;
				}
				continue;
			}

#ifdef BATTLEYE
			// got cmd
			if( !Q_stricmp(tok, "be") ) {
				int battleye;
				tok = UI_GetResponseToken( &pntr );
				if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
					break;
				battleye = (atoi(tok));
				if( battleye != newserv->battleye ) {
					need_refresh = qtrue;
					newserv->battleye = battleye;
				}
				continue;
			}
#endif
			// got cmd
			if( !Q_stricmp(tok, "ping") ) {
				int ping;
				tok = UI_GetResponseToken( &pntr );
				if( !tok || !strlen(tok) || !Q_stricmp(tok, "EOT"))
					break;
				ping = (atoi(tok));
				if( ping != newserv->ping || newserv->ping_retries == 0 ) {
					need_refresh = qtrue;
#ifdef PING_ONCE
					newserv->ping = ping;
					nextServerTime = uis.time;
#else
					if( newserv->ping < 999 && ping < 999 ) {
						newserv->ping = (newserv->ping + ping) * 0.5;
					} else {
						newserv->ping = ping;
					}
#endif
					if( newserv->ping > 999 )
						newserv->ping = 999;
				}
				continue;
			}

			Com_Printf( "UI:M_AddToServerList(%s): Unknown token:\"%s\"\n", adr, tok );
		}
	}

	if( need_refresh )
		M_RefreshScrollWindowList();
}
#define SERVER_PINGING_MINRETRY 50
#define SERVER_PINGING_MAXRETRYTIMEOUTS 1

//==================
//GetNextServerToPing - Get a non yet pinged server
//==================
server_t *GetNextServerToPing( void ) {
	server_t *server;

	server = servers;
	while( server ) {
		if( server->ping > 999 && server->ping_retries < SERVER_PINGING_MAXRETRYTIMEOUTS )
			return server;
		server = server->pnext;
	}

	return NULL;
}
//==================
//PingServers
//==================
void PingServers( void )
{
	static server_t *pingingServer = NULL;
	server_t *server;

	if( nextServerTime > uis.time ) {
		// it's not time to move into the next server yet, keep trying current
		if( pingingServer ) {
			if( nextPingTime > uis.time )
				return;

			nextPingTime = uis.time + SERVER_PINGING_MINRETRY;
			trap_Cmd_ExecuteText( EXEC_APPEND, va( "pingserver %s", pingingServer->address ) );
		}
		return;
	}

	if( nextServerTime <= uis.time && pingingServer && pingingServer->ping > SERVER_PINGING_TIMEOUT )
		UI_Printf( "Server %s timed out\n", pingingServer?pingingServer->address:"");

	nextServerTime = uis.time + SERVER_PINGING_MINRETRY;

	server = GetNextServerToPing();
	if( !server ) {
		pingingServer = NULL;
		return;
	}

	server->ping_retries++;
	pingingServer = server;
	
#ifdef PING_ONCE
	trap_Cmd_ExecuteText( EXEC_APPEND, va( "pingserver %s", pingingServer->address ) );
	nextPingTime = nextServerTime;
#else
	nextPingTime = uis.time + (SERVER_PINGING_MINRETRY/2);
#endif
}

//==================
//ResetAllPings
//==================
void ResetAllPings( void ) {
	server_t *server;

	server = servers;
	while( server ) {
		server->ping = 9999;
		server->ping_retries = 0;
		server = server->pnext;
	}
	nextServerTime = uis.time;
}

//==================
//SearchGames
//==================
void SearchGames( char *s )
{
	char	*mlist, *master;
	int		ignore_empty = 0;
	int		ignore_full = 0;

#ifdef PING_ONCE
	// clear all ping values so they are retried
	ResetAllPings();
#endif

	//"show all", "is", "is not", 0
	if( menuitem_emptyfilter )
		ignore_empty = (menuitem_emptyfilter->curvalue == 2);
	if( menuitem_fullfilter )
		ignore_full = (menuitem_fullfilter->curvalue == 2);

	mlist = trap_Cvar_VariableString("masterservers");
	if( !mlist || !(*mlist) ) {
		Menu_SetStatusBar( &s_joinserver_menu, "No master server defined" );
		return;
	}

	if( !Q_stricmp( s, "local" ) ) {
		// send out request
		trap_Cmd_ExecuteText( EXEC_APPEND, va("requestservers %s %s %s\n", s, 
			ignore_full ? "" : "full",
			ignore_empty ? "" : "empty" ) );
		return;
	}

	// request to each ip on the list
	while( mlist )
	{
		master = COM_Parse( &mlist );
		if ( !master || !(*master) )
			break;

		// send out request
		trap_Cmd_ExecuteText( EXEC_APPEND, va("requestservers %s %s %s %s %s\n", s, 
			master,
			GAMENAME,
			ignore_full ? "" : "full",
			ignore_empty ? "" : "empty" ) );
	}
}

//=============================================================================
// scroll list
//=============================================================================

m_itemslisthead_t serversScrollList;

//==================
//GetBestNextPingServer
//==================
server_t *GetBestNextPingServer( server_t *server ) {
	server_t	*ptr = servers, *best = NULL;
	unsigned int	minping = 0, bestping = 1000; // > 1000 = not pinged, < 1000 = pinged
	if( server )
		minping = server->ping;

	while( ptr ) {
		if( ptr->displayed == qfalse && ptr->ping >= minping ) {
			if( ptr->ping < bestping ) {
				best = ptr;
				bestping = ptr->ping;
			}
		}
		ptr = ptr->pnext;
	}

	if( best != NULL )
		best->displayed = qtrue;

	return best;
}

//==================
//M_RefreshScrollWindowList
//==================
void M_RefreshScrollWindowList( void )
{
	server_t		*ptr = NULL;
	qboolean		add;
	int				addedItems = 0;

	UI_FreeScrollItemList( &serversScrollList );
	// mark all servers as not added
	ptr = servers;
	while( ptr ) {
		ptr->displayed = qfalse;
		ptr = ptr->pnext;
	}

	while( (ptr = GetBestNextPingServer( ptr )) != NULL ) {
		add = qtrue;
		// ignore if full (local)
		if( menuitem_fullfilter && menuitem_fullfilter->curvalue ) {
			int full = ( ptr->curuser && (ptr->curuser == ptr->maxuser) );
			if( menuitem_fullfilter->curvalue - 1 == full ) {
				add = qfalse;
			}
		}
		// ignore if empty (local)
		if( menuitem_emptyfilter && menuitem_emptyfilter->curvalue ) {
			int empty = ( ptr->curuser != 0 );
			if( menuitem_emptyfilter->curvalue - 1 != empty ) {
				add = qfalse;
			}
		}
		// ignore if it has password and we are filtering those
		if( menuitem_passwordfilter && menuitem_passwordfilter->curvalue ) {
			if( menuitem_passwordfilter->curvalue - 1 == ptr->password ) {
				add = qfalse;
			}
		}
#ifdef BATTLEYE
		// ignore if it has battleye and we are filtering those
		if( menuitem_battleyefilter && menuitem_battleyefilter->curvalue ) {
			if( menuitem_battleyefilter->curvalue - 1 != ptr->battleye ) {
				add = qfalse;
			}
		}
#endif
		// ignore if it's of filtered ping
		if( menuitem_maxpingfilter && (int)trap_Cvar_VariableValue("ui_filter_ping") ) {
			if( ptr->ping > trap_Cvar_VariableValue("ui_filter_ping") ) {
				add = qfalse;
			}
		}

		// ignore if it's of filtered gametype
		if( menuitem_gametypefilter && menuitem_gametypefilter->curvalue ) {
			if( Q_stricmp(menuitem_gametypefilter->itemnames[menuitem_gametypefilter->curvalue], ptr->gametype) ) {
				add = qfalse;
			}
		}

		// ignore if it's of filtered skill
		if( menuitem_skillfilter && menuitem_skillfilter->curvalue ) {
			if( menuitem_skillfilter->curvalue - 1 != ptr->skilllevel ) {
				add = qfalse;
			}
		}

		if( add )UI_AddItemToScrollList( &serversScrollList, va("%i", addedItems++), (void *)ptr );
	}
}

//=============================================================================
// Servers window
//=============================================================================

static int MAX_MENU_SERVERS = 12;

static int	scrollbar_curvalue = 0;

#define	NO_SERVER_STRING	""
#define	NO_MAP_STRING	"..."
#define	NO_USERS_STRING	"..."
#define NO_GAMETYPE_STRING "..."


//==================
//M_Connect_UpdateFiltersSettings
//==================
void M_Connect_UpdateFiltersSettings( menucommon_t *menuitem ) {
	// remember filter options by using cvars
	if( menuitem_fullfilter )trap_Cvar_SetValue( "ui_filter_full", menuitem_fullfilter->curvalue );
	if( menuitem_emptyfilter )trap_Cvar_SetValue( "ui_filter_empty", menuitem_emptyfilter->curvalue );
	if( menuitem_passwordfilter )trap_Cvar_SetValue( "ui_filter_password", menuitem_passwordfilter->curvalue );
	if( menuitem_skillfilter )trap_Cvar_SetValue( "ui_filter_skill", menuitem_skillfilter->curvalue );
	if( menuitem_gametypefilter )trap_Cvar_SetValue( "ui_filter_gametype", menuitem_gametypefilter->curvalue );
	if( menuitem_maxpingfilter )trap_Cvar_Set( "ui_filter_ping", UI_GetMenuitemFieldBuffer(menuitem_maxpingfilter) );
#ifdef BATTLEYE
	if( menuitem_battleyefilter )trap_Cvar_SetValue( "ui_filter_battleye", menuitem_battleyefilter->curvalue );
#endif

	M_RefreshScrollWindowList();
}

//==================
//M_Connect_UpdateScrollbar
//==================
void M_Connect_UpdateScrollbar( menucommon_t *menuitem ) {
	scrollbar_curvalue = menuitem->curvalue;
	menuitem->maxvalue = max( 0, serversScrollList.numItems - MAX_MENU_SERVERS );
}

//==================
//GetInforServerFunc
//==================
void GetInforServerFunc( menucommon_t *menuitem ) {
	char	buffer[128];
	server_t	 *server = NULL;
	m_listitem_t *listitem;

	menuitem->localdata[1] = menuitem->localdata[0] + scrollbar_curvalue;
	listitem = UI_FindItemInScrollListWithId( &serversScrollList, menuitem->localdata[1] );
	if( listitem && listitem->data )
		server = (server_t *)listitem->data;
	if( server ) {
		Q_snprintfz( buffer, sizeof(buffer), "getinfo %s\n", server->address );
		trap_Cmd_ExecuteText( EXEC_APPEND, buffer );
	}
}

//==================
//M_Connect_Joinserver
//==================
void M_Connect_Joinserver( menucommon_t *menuitem ) {
	char	buffer[128];
	server_t *server = NULL;
	m_listitem_t *listitem;

	menuitem->localdata[1] = menuitem->localdata[0] + scrollbar_curvalue;
	listitem = UI_FindItemInScrollListWithId( &serversScrollList, menuitem->localdata[1] );
	if( listitem && listitem->data )
		server = (server_t *)listitem->data;
	if( server ) {
		Q_snprintfz( buffer, sizeof(buffer), "connect %s\n", server->address );
		trap_Cmd_ExecuteText( EXEC_APPEND, buffer );
	}
}

//==================
//M_UpdateSeverButton
//==================
void M_UpdateSeverButton( menucommon_t *menuitem ) {
	server_t *server = NULL;
	m_listitem_t *listitem;

	menuitem->localdata[1] = menuitem->localdata[0] + scrollbar_curvalue;
	listitem = UI_FindItemInScrollListWithId( &serversScrollList, menuitem->localdata[1] );
	if( listitem && listitem->data )
		server = (server_t *)listitem->data;
	if( server ) {
		Q_snprintfz( menuitem->title , MAX_STRING_CHARS, "%s%-3.3s %-5.5s %s%-6.6s %s%-12.12s %s%-32.32s%s",
			S_COLOR_WHITE, va( "%i", server->ping ),
			va( "%i/%i", server->curuser, server->maxuser ),
			S_COLOR_YELLOW, server->gametype,
			S_COLOR_ORANGE, server->map,
			S_COLOR_WHITE, server->hostname, S_COLOR_WHITE
			);
	} else
		Q_snprintfz(menuitem->title, MAX_STRING_CHARS, NO_SERVER_STRING);
}

static int lastSearchType = 0;
static void SearchGamesFunc( menucommon_t *menuitem )
{
	menucommon_t *typemenuitem = UI_MenuItemByName( "m_connect_search_type" );
	if( !typemenuitem )
		return;

	clamp( typemenuitem->curvalue, 0, 1 );
	if( lastSearchType != typemenuitem->curvalue ) {
		lastSearchType = typemenuitem->curvalue;
		M_FreeServerlist();
	}

	SearchGames( typemenuitem->itemnames[typemenuitem->curvalue] );
}

#ifdef BATTLEYE
#ifdef WIN32
void ToggleCLBattleyeFunc( menucommon_t *menuitem )
{
	trap_Cvar_SetValue( "cl_battleye", (menuitem->curvalue != 0) );
}
#endif
#endif

//==================
//JoinServer_MenuInit
//==================
void JoinServer_MenuInit( void )
{
	menucommon_t *menuitem;
	menucommon_t *menuitem_filters_background;
	int i;
	int y = 0;
	int yoffset;
	static char sbar[64];
	int	scrollwindow_width, scrollwindow_height;

	static char *filter_names[] =
	{
		"show all", "show only", "don't show", 0
	};

	static char *search_names[] =
	{
		"global", "local", 0
	};

	static char *gametype_filternames[] =
	{
		"show all",
		"dm",
		"duel",
		"tdm",
		"ctf",
		"race",
		"midair",
		0
	};

	static char *skill_filternames[] =
	{
		"show all",
		"beginner",
		"expert",
		"hardcore",
		0
	};

	static char *battleye_filternames[] =
	{
		"show all",
		"disabled",
		"optional",
		"required",
		0
	};

	// we have to open the cvars like this so they are created as CVAR_ARCHIVE
	trap_Cvar_Get( "ui_filter_full", "0", CVAR_ARCHIVE );
	trap_Cvar_Get( "ui_filter_empty", "0", CVAR_ARCHIVE );
	trap_Cvar_Get( "ui_filter_password", "0", CVAR_ARCHIVE );
	trap_Cvar_Get( "ui_filter_skill", "1", CVAR_ARCHIVE );
	trap_Cvar_Get( "ui_filter_gametype", "0", CVAR_ARCHIVE );
	trap_Cvar_Get( "ui_filter_ping", "0", CVAR_ARCHIVE );
#ifdef BATTLEYE
	trap_Cvar_Get( "ui_filter_battleye", "0", CVAR_ARCHIVE );
#endif

	if( uis.vidWidth < 800 )
		scrollwindow_width = uis.vidWidth * 0.85;
	else if( uis.vidWidth < 1024 )
		scrollwindow_width = uis.vidWidth * 0.75;
	else
		scrollwindow_width = uis.vidWidth * 0.65;

	s_joinserver_menu.x = uis.vidWidth * 0.5; //fixme
	s_joinserver_menu.nitems = 0;

	yoffset = UI_SCALED_HEIGHT(72); // left space for the logo

	menuitem_filters_background = UI_InitMenuItem( "m_connect_filters", va("%sfilters",S_COLOR_WHITE) , -(scrollwindow_width/2), y+yoffset, MTYPE_SEPARATOR, ALIGN_LEFT_TOP, uis.fontSystemSmall, NULL );
	Menu_AddItem( &s_joinserver_menu, menuitem_filters_background );
	// create an associated picture to the items to act as window background
	menuitem = menuitem_filters_background;
	menuitem->pict.shader = uis.whiteShader;
	menuitem->pict.shaderHigh = NULL;
	Vector4Copy( colorMdGrey, menuitem->pict.color );
	menuitem->pict.color[3] = 0.65f;
	menuitem->pict.yoffset = 0;
	menuitem->pict.xoffset = 0;
	menuitem->pict.width = scrollwindow_width;
	menuitem->pict.height = yoffset + menuitem->pict.yoffset; // will be set later

	yoffset += 0.5 * trap_SCR_strHeight( menuitem_filters_background->font );

	menuitem_fullfilter = UI_InitMenuItem( "m_connect_filterfull", "full", UI_SCALED_WIDTH(-80), y+yoffset, MTYPE_SPINCONTROL, ALIGN_RIGHT_TOP, uis.fontSystemSmall, M_Connect_UpdateFiltersSettings );
	Menu_AddItem( &s_joinserver_menu, menuitem_fullfilter );
	UI_SetupSpinControl( menuitem_fullfilter, filter_names, trap_Cvar_VariableValue("ui_filter_full") );
	//yoffset += trap_SCR_strHeight( menuitem_fullfilter->font );

	menuitem_gametypefilter = UI_InitMenuItem( "m_connect_filtergametype", "gametype", UI_SCALED_WIDTH(120), y+yoffset, MTYPE_SPINCONTROL, ALIGN_RIGHT_TOP, uis.fontSystemSmall, M_Connect_UpdateFiltersSettings );
	Menu_AddItem( &s_joinserver_menu, menuitem_gametypefilter );
	UI_SetupSpinControl( menuitem_gametypefilter, gametype_filternames, trap_Cvar_VariableValue("ui_filter_gametype") );
	yoffset += trap_SCR_strHeight( menuitem_gametypefilter->font );

	menuitem_emptyfilter = UI_InitMenuItem( "m_connect_filterempty", "empty", UI_SCALED_WIDTH(-80), y+yoffset, MTYPE_SPINCONTROL, ALIGN_RIGHT_TOP, uis.fontSystemSmall, M_Connect_UpdateFiltersSettings );
	Menu_AddItem( &s_joinserver_menu, menuitem_emptyfilter );
	UI_SetupSpinControl( menuitem_emptyfilter, filter_names, trap_Cvar_VariableValue("ui_filter_empty") );
	//yoffset += trap_SCR_strHeight( menuitem_emptyfilter->font );

	menuitem_skillfilter = UI_InitMenuItem( "m_connect_filterskill", "skill", UI_SCALED_WIDTH(120), y+yoffset, MTYPE_SPINCONTROL, ALIGN_RIGHT_TOP, uis.fontSystemSmall, M_Connect_UpdateFiltersSettings );
	Menu_AddItem( &s_joinserver_menu, menuitem_skillfilter );
	UI_SetupSpinControl( menuitem_skillfilter, skill_filternames, trap_Cvar_VariableValue("ui_filter_skill") );
	yoffset += trap_SCR_strHeight( menuitem_skillfilter->font );

	menuitem_passwordfilter = UI_InitMenuItem( "m_connect_filterpassword", "password", UI_SCALED_WIDTH(-80), y+yoffset, MTYPE_SPINCONTROL, ALIGN_RIGHT_TOP, uis.fontSystemSmall, M_Connect_UpdateFiltersSettings );
	Menu_AddItem( &s_joinserver_menu, menuitem_passwordfilter );
	UI_SetupSpinControl( menuitem_passwordfilter, filter_names, trap_Cvar_VariableValue("ui_filter_password") );
	//yoffset += trap_SCR_strHeight( menuitem_passwordfilter->font );

	menuitem_maxpingfilter = UI_InitMenuItem( "m_connect_filterping", "maxping", UI_SCALED_WIDTH(120), y+yoffset, MTYPE_FIELD, ALIGN_RIGHT_TOP, uis.fontSystemSmall, M_Connect_UpdateFiltersSettings );
	Menu_AddItem( &s_joinserver_menu, menuitem_maxpingfilter );
	UI_SetupField( menuitem_maxpingfilter, trap_Cvar_VariableString("ui_filter_ping"), 4, -1 );
	yoffset += trap_SCR_strHeight( menuitem_maxpingfilter->font );

#ifdef BATTLEYE
	menuitem_battleyefilter = UI_InitMenuItem( "m_connect_filterbattleye", "BattlEye", UI_SCALED_WIDTH(-80), y+yoffset, MTYPE_SPINCONTROL, ALIGN_RIGHT_TOP, uis.fontSystemSmall, M_Connect_UpdateFiltersSettings );
	Menu_AddItem( &s_joinserver_menu, menuitem_battleyefilter );
	UI_SetupSpinControl( menuitem_battleyefilter, battleye_filternames, trap_Cvar_VariableValue("ui_filter_battleye") );
	yoffset += trap_SCR_strHeight( menuitem_battleyefilter->font );
#endif

	// here ends the filters background, set it's image height now
	menuitem_filters_background->pict.height = yoffset - menuitem_filters_background->pict.height + ( 0.5 * trap_SCR_strHeight( menuitem_skillfilter->font ) );

	yoffset += trap_SCR_strHeight( menuitem_skillfilter->font );

#ifdef BATTLEYE
#ifdef WIN32
	menuitem = UI_InitMenuItem( "m_connect_toggle_battleye", "BattlEye Client", UI_SCALED_WIDTH(-80), y+yoffset, MTYPE_SPINCONTROL, MTYPE_SPINCONTROL, uis.fontSystemSmall, ToggleCLBattleyeFunc );
	Menu_AddItem( &s_joinserver_menu, menuitem );
	UI_SetupSpinControl( menuitem, offon_names, trap_Cvar_VariableValue("cl_battleye") );
	//yoffset += trap_SCR_strHeight( menuitem->font );
#endif
#endif

	menuitem = UI_InitMenuItem( "m_connect_search_type", "search", UI_SCALED_WIDTH(120), y+yoffset, MTYPE_SPINCONTROL, MTYPE_SPINCONTROL, uis.fontSystemSmall, NULL );
	Menu_AddItem( &s_joinserver_menu, menuitem );
	UI_SetupSpinControl( menuitem, search_names, 0 );

	yoffset += trap_SCR_strHeight( menuitem->font );
	yoffset += trap_SCR_strHeight( menuitem->font );

	// scrollbar

	scrollwindow_height = uis.vidHeight - ( yoffset + ( 4 * trap_SCR_strHeight( uis.fontSystemBig ) ) );
	MAX_MENU_SERVERS = ( scrollwindow_height / trap_SCR_strHeight( uis.fontSystemSmall ) );
	if( MAX_MENU_SERVERS < 5 ) MAX_MENU_SERVERS = 5;

	menuitem = UI_InitMenuItem( "m_connect_scrollbar", NULL, (scrollwindow_width*0.5)+8, y+yoffset, MTYPE_SCROLLBAR, ALIGN_LEFT_TOP, uis.fontSystemSmall, M_Connect_UpdateScrollbar );
	UI_SetupScrollbar( menuitem, MAX_MENU_SERVERS-1, 0, 0, 0 );
	Menu_AddItem( &s_joinserver_menu, menuitem );

	for( i = 0; i < MAX_MENU_SERVERS; i++ )
	{
		menuitem = UI_InitMenuItem( va("m_connect_button_%i", i), NO_SERVER_STRING, -(scrollwindow_width*0.5), y+yoffset, MTYPE_ACTION, ALIGN_LEFT_TOP, uis.fontSystemSmall, M_Connect_Joinserver );
		menuitem->statusbar = "press ENTER to connect";
		menuitem->ownerdraw = M_UpdateSeverButton;
		menuitem->localdata[0] = i; // line in the window
		menuitem->localdata[1] = i; // line in list
		menuitem->width = scrollwindow_width; // adjust strings to this width
		Menu_AddItem( &s_joinserver_menu, menuitem );

		// create an associated picture to the items to act as window background
		menuitem->pict.shader = uis.whiteShader;
		menuitem->pict.shaderHigh = uis.whiteShader;
		Vector4Copy( colorWhite, menuitem->pict.colorHigh );
		Vector4Copy( ( i & 1 )?colorDkGrey:colorMdGrey, menuitem->pict.color );
		menuitem->pict.color[3] = menuitem->pict.colorHigh[3] = 0.65f;
		menuitem->pict.yoffset = 0;
		menuitem->pict.xoffset = 0;
		menuitem->pict.width = scrollwindow_width;
		menuitem->pict.height = trap_SCR_strHeight( menuitem->font );

		yoffset += trap_SCR_strHeight( menuitem->font );
	}

	yoffset += trap_SCR_strHeight( menuitem->font );

	menuitem = UI_InitMenuItem( "m_connect_search", "search!", 16, y+yoffset, MTYPE_ACTION, ALIGN_LEFT_TOP, uis.fontSystemBig, SearchGamesFunc );
	Menu_AddItem( &s_joinserver_menu, menuitem );
	Q_snprintfz ( sbar, sizeof(sbar), "Master server at %s", trap_Cvar_VariableString("masterservers") );
	menuitem->statusbar = sbar;

	// back
	menuitem = UI_InitMenuItem( "m_connect_back", "back", -16, y+yoffset, MTYPE_ACTION, ALIGN_RIGHT_TOP, uis.fontSystemBig, M_genericBackFunc );
	Menu_AddItem( &s_joinserver_menu, menuitem );
	yoffset += trap_SCR_strHeight( menuitem->font );

	//Menu_Center( &s_joinserver_menu );
	Menu_Init( &s_joinserver_menu );
}


void JoinServer_MenuDraw( void )
{
	PingServers();
	Menu_Draw( &s_joinserver_menu );
}

const char *JoinServer_MenuKey( int key )
{
	menucommon_t *item;

	item = Menu_ItemAtCursor( &s_joinserver_menu );

	if ( key == K_ESCAPE || ( (key == K_MOUSE2) && (item->type != MTYPE_SPINCONTROL) &&
		(item->type != MTYPE_SLIDER)) )
	{
	}

	return Default_MenuKey( &s_joinserver_menu, key );
}

const char *JoinServer_MenuCharEvent( int key )
{
	return Default_MenuCharEvent( &s_joinserver_menu, key );
}

void M_Menu_JoinServer_f( void )
{
	JoinServer_MenuInit();
	M_PushMenu( &s_joinserver_menu, JoinServer_MenuDraw, JoinServer_MenuKey, JoinServer_MenuCharEvent );
}
