//very very basic server browser.
//this was written as an example for the api, rather than a truely usable server browser.

//WARNING: make sure you only create one server browser widget otherwise they'll fight each other

#ifndef MOD_GAMEDIR
//look up what its meant to be.
//#define MOD_GAMEDIR cvar_string("game")
#endif

#include "../menusys/mitem_menu.qc"		//subwindow

var float autocvar_sb_shownumplayers = 1;
var float autocvar_sb_showmaxplayers = 0;
var float autocvar_sb_showfraglimit = 0;
var float autocvar_sb_showtimelimit = 0;
var float autocvar_sb_showping = 1;
var float autocvar_sb_showgamedir = 0;
var float autocvar_sb_showaddress = 0;
var float autocvar_sb_showmap = 1;
var float autocvar_sb_showname = 1;

//#define COLUMN(width, sortname, title, draw)
#define COLUMN_NUMPLAYERS		COLUMN(2*8,	numplayers,	"Pl", 			ui.drawstring(pos, sprintf("%-2g", gethostcachenumber(field_numplayers, sv)), '8 8', col, 1, 0);)
#define COLUMN_MAXPLAYERS		COLUMN(2*8,	maxplayers,	"MP", 		ui.drawstring(pos, sprintf("%-2g", gethostcachenumber(field_maxplayers, sv)), '8 8', col, 1, 0);)
#define COLUMN_PING			COLUMN(4*8,	ping,			"Ping", 		ui.drawstring(pos, sprintf("%-4g", gethostcachenumber(field_ping, sv)), '8 8', col, 1, 0);)
#define COLUMN_FRAGLIMIT		COLUMN(4*8,	fraglimit,		"FL",			ui.drawstring(pos, sprintf("%-3g", gethostcachenumber(field_fraglimit, sv)), '8 8', col, 1, 0);)
#define COLUMN_TIMELIMIT		COLUMN(4*8,	timelimit,		"TL",			ui.drawstring(pos, sprintf("%-3g", gethostcachenumber(field_timelimit, sv)), '8 8', col, 1, 0);)
#define COLUMN_GAMEDIR		COLUMN(8*8,	gamedir,		"Gamedir",	ui.drawstring(pos, sprintf("%-.8s", gethostcachestring(field_gamedir, sv)), '8 8', col, 1, 0);)
#define COLUMN_ADDRESS		COLUMN(16*8,	address,		"Address",		ui.drawstring(pos, sprintf("%-.16s", gethostcachestring(field_address, sv)), '8 8', col, 1, 0);)
#define COLUMN_MAP			COLUMN(8*8,	map,			"Map",		ui.drawstring(pos, sprintf("%-.8s", gethostcachestring(field_map, sv)), '8 8', col, 1, 0);)
#define COLUMN_HOSTNAME		COLUMN(64*8,	name,		"Name",		ui.drawstring(pos, sprintf("%s", gethostcachestring(field_name, sv)), '8 8', col, 1, 0);)
//FIXME: add a little * icon before the hostname for favourites or something

#define COLUMNS 				COLUMN_NUMPLAYERS	COLUMN_MAXPLAYERS	COLUMN_PING	COLUMN_FRAGLIMIT	COLUMN_TIMELIMIT	COLUMN_GAMEDIR	COLUMN_ADDRESS	COLUMN_MAP	COLUMN_HOSTNAME

class mitem_servers : mitem
{
	float server_selected;
	mitem_vslider slider;
	float dbltime;
	float dobound;

	void() mitem_servers =
	{
		dbltime = cltime - 10;
		this.item_flags |= IF_SELECTABLE;
		server_selected = -1;

		//clear the filter
		resethostcachemasks();
#ifdef MOD_GAMEDIR
		if (MOD_GAMEDIR != "")
		{
			//constrain the list to only servers with the right gamedir.
			sethostcachemaskstring(0, gethostcacheindexforkey("gamedir"), MOD_GAMEDIR, SLIST_TEST_EQUAL);
		}
#endif
		//sort by ping by default
		sethostcachesort(gethostcacheindexforkey("ping"), FALSE);
		//(re)query the servers.
		refreshhostcache();

		resorthostcache();
	};

	virtual void(vector pos) item_draw =
	{
		local float sv, maxsv = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
		local float maxy = self.item_size_y;
		float left = pos_x;
	
		local vector omin = ui.drawrectmin, omax = ui.drawrectmax;
		local vector cmin = pos + '0 8', cmax = pos + item_size;
	
		cmin_x = max(omin_x, cmin_x);
		cmin_y = max(omin_y, cmin_y);
		cmax_x = min(omax_x, cmax_x);
		cmax_y = min(omax_y, cmax_y);

		slider.maxv = maxsv - (floor(maxy/8) - 1);

		//constrain the view the lazy way.
		if (dobound)
		{
			dobound = FALSE;
			if (slider.val < 0)
				slider.val = 0;
			while (slider.val + floor(maxy/8) - 1 <= server_selected)
				slider.val+=1;
			while (server_selected < slider.val && slider.val > 0)
				slider.val-=1;
		}

		float field_name = gethostcacheindexforkey("name");
		float field_ping = gethostcacheindexforkey("ping");
		float field_numplayers = gethostcacheindexforkey("numhumans");
		if (field_numplayers < 0)
			field_numplayers = gethostcacheindexforkey("numplayers");
		float field_maxplayers = gethostcacheindexforkey("maxplayers");
		float field_gamedir = gethostcacheindexforkey("gamedir");
		if (field_gamedir < 0)
			field_gamedir = gethostcacheindexforkey("mod");
		float field_address = gethostcacheindexforkey("cname");
		float field_map = gethostcacheindexforkey("map");
		float field_timelimit = gethostcacheindexforkey("timelimit");
		float field_fraglimit = gethostcacheindexforkey("fraglimit");

		maxy = ceil(maxy/8) - 1;
		if (maxsv  > slider.val + maxy)
			maxsv = slider.val + maxy;
	
		float sort = gethostcachevalue(SLIST_SORTFIELD);
		string colkey;
#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {if (ui.mousepos[0] > pos_x && ui.mousepos[1] < pos_y+8) colkey = #sortname; pos_x += width+8;}
		COLUMNS
#undef COLUMN

		vector col;
		pos_x = left;
#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {col = '1 1 1'; if (sort == field_##sortname) col_z = 0; if (colkey == #sortname) col_x = 0; ui.drawstring(pos, title, '8 8', col, 1, 0); pos_x += width+8;}
		COLUMNS
#undef COLUMN

		//make sure things get cut off if we have too many rows (or fractional start)
		ui.setcliparea(cmin[0], cmin[1], cmax[0] - cmin[0], cmax[1] - cmin[1]);

		pos_y += 8 * (1-(slider.val-floor(slider.val)));
		for (float y=pos_y,	sv = max(0, floor(slider.val)); sv < maxsv; sv+=1)
		{
			col = (sv&1)?'0.1 0.1 0.1':'0.15 0.1 0.05';
			drawfill([left, y], [item_size_x,8], col, 0.8, 0); 
			y += 8;
		}
		for (	sv = max(0, floor(slider.val)); sv < maxsv; sv+=1)
		{
			col = ((server_selected==sv)?'1 1 0':'1 1 1');
	
			//if isproxy
			//if islocal
			//if isfavorite
	
			pos_x = left;
#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {draw   pos_x += width+8; }
			COLUMNS
#undef COLUMN
			pos_y += 8;
		}
	
		ui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);
	};
	virtual float(vector pos, float scan, float char, float down) item_keypress =
	{
		float displaysize;
		string addr;
		/*just sink all inputs*/
		if (!down)
			return FALSE;
		if (scan != K_MOUSE1)
			dbltime = cltime - 10;
		if (scan == K_MOUSE1)
		{
			float news = ui.mousepos[1] - (pos_y+8);
			news = floor(news / 8);
			if (news == -1)
			{
				string colkey = "";
#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {if (ui.mousepos[0] > pos_x) colkey = #sortname; pos_x += width+8;}
				COLUMNS
#undef COLUMN
				if (colkey != "")
				{
					float fld = gethostcacheindexforkey(colkey);
					if (colkey == "numplayers")
					{
						//favour descending order
						sethostcachesort(fld, (gethostcachevalue(SLIST_SORTFIELD) != fld) || !gethostcachevalue(SLIST_SORTDESCENDING));
					}
					else
					{
						//favour ascending order
						sethostcachesort(fld, (gethostcachevalue(SLIST_SORTFIELD) == fld) && !gethostcachevalue(SLIST_SORTDESCENDING));
					}
					resorthostcache();	//tell the engine that its okay to resort everything.
					dobound = TRUE;
				}
			}
			if (news < 0 || news >= gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT))
				return FALSE;
	
			news += floor(slider.val);

			if (server_selected == news && dbltime > cltime)
			{
				//connect on double clicks. because we can.
				addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
				if (addr)
					localcmd(sprintf("m_pop;connect \"%s\"\n", addr));
			}
			else
				server_selected = news;
			dobound = TRUE;
	
			dbltime = cltime + 0.5;
		}
		else if (scan == K_ENTER)
		{	//connect normally
			addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
			if (addr)
				localcmd(sprintf("m_pop;connect \"%s\"\n", addr));
		}
		else if (scan == 's')
		{	//s = join as a spectator
			addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
			if (addr)
				localcmd(sprintf("m_pop;observe \"%s\"\n", addr));
		}
		else if (scan == 'j')
		{	//s = join as a spectator
			addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
			if (addr)
				localcmd(sprintf("m_pop;join \"%s\"\n", addr));
		}
		else if (scan == K_UPARROW || scan == K_MWHEELUP)
		{
			this.server_selected -= 1;
			if (this.server_selected < 0)
			{
				this.server_selected = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
				if (this.server_selected)
					this.server_selected -= 1;
			}
			dobound = TRUE;
		}
		else if (scan == K_DOWNARROW || scan == K_MWHEELDOWN)
		{
			this.server_selected += 1;
			if (this.server_selected >= gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT))
				this.server_selected = 0;
			dobound = TRUE;
		}
		else if (scan == K_PGUP)
		{
			displaysize = (item_size[1]-8)/(8);	//this many rows
			displaysize = floor(displaysize*0.5);
			if (displaysize < 1)
				displaysize = 1;
			this.server_selected -= displaysize ;
			if (this.server_selected < 0)
				this.server_selected = 0;
			dobound = TRUE;
		}
		else if (scan == K_PGDN)
		{
			displaysize = (item_size[1]-8)/(8);	//this many rows
			displaysize = floor(displaysize*0.5);
			if (displaysize < 1)
				displaysize = 1;
			this.server_selected += displaysize;
			if (this.server_selected >= gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT))
				this.server_selected = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT)-1;
			dobound = TRUE;
		}
		else if (scan == K_HOME)
		{
			this.server_selected = 0;
			dobound = TRUE;
		}
		else if (scan == K_END)
		{
			this.server_selected = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT)-1;
			if (this.server_selected < 0)
				this.server_selected = 0;
			dobound = TRUE;
		}
		else if (char == 'r' || char == 'R' || scan == K_F5)
		{
			refreshhostcache();
			resorthostcache();
		}
		else
			return FALSE;
		return TRUE;
	};
};

class mitem_servers_players : mitem
{
	mitem_servers listing;

	void() mitem_servers_players =
	{
//		this.item_flags |= IF_SELECTABLE;
	};
	
	virtual void(vector pos) item_draw =
	{
		float player;
		float y;
		float m;
		vector opos = pos;
		if (listing.server_selected < 0)
			return;
		for (player = 0, y = 0; player < 256; player++)
		{
			string playerinfo = gethostcachestring(gethostcacheindexforkey(sprintf("player%g", player)), listing.server_selected);
			if (!playerinfo)
				break;
			tokenize(playerinfo);
			float userid	= stof(argv(0));
			float frags		= stof(argv(1));
			float ontime	= stof(argv(2));
			float ping		= stof(argv(3));
			string name	= argv(4);
			string skin		= argv(5);
			vector top		= stov(argv(6));
			vector bot		= stov(argv(7));

			drawfill(pos,		'16 4',	top, 1, 0);
			drawfill(pos+'0 4 0',	'16 4',	bot, 1, 0);
			drawstring(pos,	sprintf("%g", frags), '8 8', '1 1 1', 1, 0);
			drawstring(pos+'20 0',	name, '8 8', '1 1 1', 1, 0);
			pos_y += 8;
			if (++y == 8)
			{
				y-= 8;
				pos_y = opos_y;
				pos_x += 16*8;
			}
		}
	
		if (y)
		{
			pos_y = opos_y;
			pos_x += 16*6;
			y = 0;
		}
//		drawtextfield(opos, item_size, 3, gethostcachestring(gethostcacheindexforkey("serverinfo"), listing.server_selected));
		m = tokenizebyseparator(gethostcachestring(gethostcacheindexforkey("serverinfo"), listing.server_selected), "\\");
		for(player = 1; player <= m; player += 2)
		{
			drawtextfield(pos,		'64 8', 6, argv(player));
			drawtextfield(pos+'68 0',	[32*8-40, 8], 3, argv(player+1));
	
			pos_y += 8;
			if (++y == 8)
			{
				y-= 8;
				pos_y -= 8*8;
				pos_x += 32*8;
			}
		}
	};
};


nonstatic void(mitem_desktop desktop) M_Servers =
{
	mitem_menu o;

	if (assumefalsecheckcommand("menu_servers") && argv(1) != "force")
	{
		localcmd("menu_servers\n");
		return;
	}
	
#ifdef CSQC
	if not (checkextension("FTE_CSQC_SERVERBROWSER"))
	{
		print(_("Sorry, your client does not support FTE_CSQC_SERVERBROWSER\n"));
		return;
	}
#endif

	o = (mitem_menu)desktop.findchildtext(_("Servers List"));
	if (o)
		o.totop();
	else
	{
#if 1
		mitem_exmenu m;
		m = spawn(mitem_exmenu, item_text:_("Options"), item_flags:IF_SELECTABLE, item_command:"m_main");
		desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
		desktop.item_focuschange(m, IF_KFOCUSED);
		m.totop();
#else
		mitem_menu m;
		m = menu_spawn(desktop, _("Servers List"), '320 200');
		m.item_command = "m_main";
		m.item_flags |= IF_RESIZABLE;
		desktop.item_focuschange(m, IF_KFOCUSED);
		m.totop();
#endif

		mitem_vslider sl = spawn(mitem_vslider, stride:4, item_flags:IF_SELECTABLE);
		mitem_servers ls = spawn(mitem_servers, slider:sl);
	
		m.add(ls, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '-16 -68');
		m.add(spawn(mitem_servers_players, listing:ls),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, [8*20, -8*8], [0, -8*0]);
		m.add(sl,	RS_X_MIN_PARENT_MAX|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, [-16, 8], [0, -68]);

		m.add(menuitemcheck_spawn(_("Ping"),			"sb_showping", 			[8*8, 8]),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*8], [8*20, -8*7]);
		m.add(menuitemcheck_spawn(_("Address"),		"sb_showaddress", 		[8*8, 8]),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*7], [8*20, -8*6]);
		m.add(menuitemcheck_spawn(_("Map"),			"sb_showmap", 			[8*8, 8]),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*6], [8*20, -8*5]);
		m.add(menuitemcheck_spawn(_("Gamedir"),		"sb_showgamedir", 		[8*8, 8]),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*5], [8*20, -8*4]);
		m.add(menuitemcheck_spawn(_("NumPlayers"),		"sb_shownumplayers",	[8*8, 8]),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*4], [8*20, -8*3]);
		m.add(menuitemcheck_spawn(_("MaxPlayers"),		"sb_showmaxplayers",	[8*8, 8]),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*3], [8*20, -8*2]);
		m.add(menuitemcheck_spawn(_("Fraglimit"),		"sb_showfraglimit", 		[8*8, 8]),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*2], [8*20, -8*1]);
		m.add(menuitemcheck_spawn(_("Timelimit"),		"sb_showtimelimit", 		[8*8, 8]),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*1], [8*20, -8*0]);
	}
};