//maxclients is a QW thing. NQ engines use maxplayers (which requires a disconnect to apply)
#include "../menusys/mitem_grid.qc"

static string newgameinfo;

nonstatic void(mitem_desktop desktop) M_Dir =
{	//implementing this primarily so its available in QSS. :P
	string path = argv(1);
	string pack = argv(2);
	searchhandle h;
	if (path == "")
	{
		print("m_dir <FILEPATH> [PACKAGE]\n");
		return;
	}
	if (pack != "")
		h = search_begin(path, SB_FULLPACKAGEPATH|SB_FORCESEARCH|16, 0, argv(2));
	else
		h = search_begin(path, SB_FULLPACKAGEPATH|16|32, 0);
	print(sprintf("Directory listing of %S (%g files)\n", path, search_getsize(h)));
	for (float i = 0; i < search_getsize(h); i++)
	{
		float sz = search_getfilesize(h,i);
		string szfmt = "b";
		if (sz > 512*1024*1024)
			sz /= 1024*1024*1024, szfmt = "gb";
		else if (sz > 512*1024)
			sz /= 1024*1024, szfmt = "mb";
		else if (sz > 512)
			sz /= 1024, szfmt = "kb";
		
		print(sprintf("  %S (%.4g %s, %s, %S)\n",
			search_getfilename(h,i),
			sz,szfmt,
			search_getfilemtime(h,i),
			search_getpackagename(h,i)
			));
	}
	search_end(h);
};

#define FOURCC(a,b,c,d) ((int)(a)<<0i)|((int)(b)<<8i)|((int)(c)<<16i)|((int)(d)<<24i)
static string(string name) getmapdesc =
{
	if (checkbuiltin(fread) && checkbuiltin(fseek) && checkbuiltin(memalloc) && checkbuiltin(memfree) && checkbuiltin(memgetval) && checkbuiltin(itof) && checkbuiltin(ftoi))
	{	//we can do it, so do it.
		filestream f = fopen(name, FILE_READ);
		if (f < 0)
			print(sprintf("Unable to read %s\n", name));
		
		name = substring(name, 5, -5);
		if (f < 0)
			return name;

		int *header = memalloc(sizeof(int)*4);
		int bspver = 0, entofs, entlen;
		if (fread(f, (void*)header, sizeof(int)*4) == sizeof(int)*4 && ((bspver=header[0]),(
			bspver == FOURCC('I','B','S','P') ||	//IBSP (q2/q3)
			bspver == FOURCC('R','B','S','P') ||	//RBSP (jk2o etc)
			bspver == FOURCC('F','B','S','P') ||	//IBSP (qfusion/warsow)
			bspver == 29i ||	//q1
			bspver == 30i ||	//hl
			bspver == FOURCC('B','S','P','2'))))	//bsp2
		{
			if (bspver == FOURCC('I','B','S','P'))
			{	//has an actual version number! ooo...
				entofs = header[2];
				entlen = header[3];
			}
			else
			{
				entofs = header[1];
				entlen = header[2];
			}
			fseek(f, entofs);
			string s = (string)memalloc(entlen+1);
			fread(f, (void*)s, entlen);
			float argc = tokenize(s);
			if (argv(0) == "{")
			{
				for (float p = 1; p < argc; p+=2)
				{
					string t = argv(p);
					if (t == "message")
					{	//finally found the human-readable name of the map. woo.
						name = argv(p+1);
						break;
					}
					if (t == "}")	//don't read the message from some kind of trigger
						break;
					if (t == "{")	//some sort of corruption
						break;
				}
			}
			else
				name = "ERROR";
			memfree((void*)s);
		}
		else
			name = sprintf("UNSUPPORTED %i", bspver);
		memfree(header);
		fclose(f);
		return name;
	}
	return "";	//we can't do that in this engine... don't show anything, because its pointless showing the same thing twice.
};

static string(string name) packagetogamedir =
{
	float so = strstrofs(name, "/");
	if (so>=0)
		name = substring(name, 0, so);

	//don't hide id1/dm4 etc just because the user previously auto-downloaded eg qw/aerowalk
	name = strtolower(name);
	if (name == "qw" || name == "fte")
		name = "id1";

	return name;
};
class mitem_maplist : mitem_grid
{
	strbuf	names;
	strbuf	descs;
	virtual void() item_remove =
	{
		buf_del(names);
		buf_del(descs);
		super::item_remove();
	};
	static void() mitem_maplist =
	{
		searchhandle searchtable;
		searchtable = search_begin("maps/*.bsp", SB_FULLPACKAGEPATH, 0);
		float files = search_getsize(searchtable);
		if (files && checkbuiltin(search_getpackagename))
		{	//find which gamedir the first entry is in (highest-priority is first)
			string gd = packagetogamedir(search_getpackagename(searchtable,0));

			for (float count = 1; count < files; count++)
			{
				string mgd = packagetogamedir(search_getpackagename(searchtable,count));
				if (mgd != gd)
				{	//the gamedir changed... truncate the list here.
					files = count;
					break;
				}
			}
		}
		grid_numchildren = 0;
		names = buf_create();
		for (float count = 0; count < files; count++)
		{
			string n = search_getfilename(searchtable, count);
			string shortname = substring(n, 5, -5);
			if (!strncmp(shortname, "_", 1) || !strncmp(shortname, "b_", 2))
				continue;	//don't add b_* names
			bufstr_set(names, grid_numchildren, n);
			grid_numchildren++;
		}
		search_end(searchtable);
		buf_sort(names, 0, FALSE);	//make sure they're sorted now.
		descs = buf_create();

		item_resized();	//FIXME: is this needed?
		grid_kactive = grid_numchildren?0:-1;
		grid_selectionchanged(-1,grid_kactive);
	};
	virtual void(vector pos, float idx) grid_draw =
	{
		string map = bufstr_get(names, idx);
		string desc = bufstr_get(descs, idx);
		if not(desc)
		{
			desc = map;
			static float lasttime;
			if (time != lasttime)
			{	//fix up the name and cache it.
				lasttime = time;
				desc = getmapdesc(map);
				bufstr_set(descs, idx, desc);
			}
		}
		map = substring(map, 5, -5);

		vector col = (map == get("map"))?[1.16, 0.54, 0.41]:'1 1 1';
		if (item_flags & IF_MFOCUSED && idx == grid_mactive)
			col_z = 0; 
		if (item_flags & IF_KFOCUSED && idx == grid_kactive)
			col_x = 0;

		vector valuepos = [pos_x+item_size_x/2, pos_y]; 
		pos_x = valuepos_x - stringwidth(map, TRUE, '1 1 0'*this.item_scale) - 8;
		valuepos_x += 1;
		ui.drawstring(pos, map, '1 1 0' * item_scale, col, item_alpha, 0);
		ui.drawstring(valuepos, desc, '1 1 0' * item_scale, col, item_alpha, 0);
	};
	virtual float(vector pos, float scan, float chr, float down, float idx) grid_keypress =
	{
		if ((scan == K_ENTER || scan == K_MOUSE1) && down)
		{
			string map = bufstr_get(names, idx);
			map = substring(map, 5, -5);
			set("map", map);
			return TRUE;
		}
		return FALSE;
	};
//	virtual void(float olditem, float newitem) grid_selectionchanged;
};

class mitem_newgame : mitem_exmenu
{
	virtual float(string key) isvalid =
	{
		return TRUE;
	};
	virtual string(string key) get =
	{
		return infoget(newgameinfo, key);
	};
	virtual void(string key, string newval) set =
	{
		string old = newgameinfo;
		newgameinfo = strzone(infoadd(newgameinfo, key, newval));
		if (old)
			strunzone(old);
	};
};

nonstatic void(mitem_desktop desktop) M_NewGame =
{
	mitem_pic banner;
	string gametype = argv(1);
	local float pos;
	mitem_exmenu m;
	if (gametype == "sp")
	{
		//single player has no options. the start map itself gives skill+episode options.
		//use 0 for maxplayers. this allows splitscreen to allocate more.
		localcmd("\ndisconnect; deathmatch 0; coop 0; maxplayers 0; timelimit 0; fraglimit 0; teamplay 0; samelevel 0; startmap_sp\n");
		return;
	}
	if (gametype == "begin")
	{
		cvar_set("hostname", 		infoget(newgameinfo, "hostname"));
		cvar_set("deathmatch", 	infoget(newgameinfo, "deathmatch"));
		cvar_set("coop", 		infoget(newgameinfo, "coop"));
		cvar_set("teamplay", 		infoget(newgameinfo, "teamplay"));
		cvar_set("sv_public", 	infoget(newgameinfo, "sv_public"));
		cvar_set("timelimit", 	infoget(newgameinfo, "timelimit"));
		cvar_set("fraglimit", 	infoget(newgameinfo, "fraglimit"));
		string map = 			infoget(newgameinfo, "map");
		if (map == "")
		{
			if (infoget(newgameinfo, "deathmatch"))
				map = sprintf("dm%g", floor(random(1, 6)));
			else
				map = "start";
		}

		if (engine == E_QSS)
		{
			if (stof(infoget(newgameinfo, "maxclients")) != cvar("maxplayers"))
				localcmd(sprintf("\ndisconnect; maxplayers %S\n", infoget(newgameinfo, "maxclients")?:"0"));
		}
		else
			cvar_set("maxclients", 	infoget(newgameinfo, "maxclients"));

		//'map' acts slightly differently in different engines.
		//qss: kicks everyone, resets all parms
		//fte: just resets parms.
		//whereas 'changelevel' works more predictably, but does mean you might get more weapons than were really intended.
		if (engine == E_QSS && gametype == "warp")
			localcmd(sprintf("\nchangelevel %S\n", map));
		else
			localcmd(sprintf("\nmap %S\n", map));
		return;
	}

	if (newgameinfo)
		strunzone(newgameinfo);
	newgameinfo = "";
	newgameinfo = infoadd(newgameinfo, "hostname", cvar_string("hostname"));
	newgameinfo = infoadd(newgameinfo, "deathmatch", cvar_string("deathmatch"));
	newgameinfo = infoadd(newgameinfo, "teamplay", cvar_string("teamplay"));
	newgameinfo = infoadd(newgameinfo, "sv_public", cvar_string("sv_public"));
	newgameinfo = infoadd(newgameinfo, "maxclients", cvar_string((engine == E_QSS)?"maxplayers":"maxclients"));
	newgameinfo = infoadd(newgameinfo, "timelimit", cvar_string("timelimit"));
	newgameinfo = infoadd(newgameinfo, "fraglimit", cvar_string("fraglimit"));
	newgameinfo = strzone(newgameinfo);

	//create the menu
	m = spawn(mitem_newgame, item_text:_("New Game"), 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();

#if 1
	mitem_frame fr = m;
#else
	//create a frame for its scrollbar.
	float h = 100;
	mitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);
	m.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);
#endif

	switch(gametype)
	{
	case "tdm":
	case "dm":
	case "coop":
	case "sp":
	case "warp":
		break;
	default:
		//show game type selection
		pos = (16/-2)*(4);
		banner = spawn(mitem_pic, item_text:"gfx/p_option.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
		m.addm(banner, [(160-banner.item_size_x)*0.5, pos-32], [(160+banner.item_size_x)*0.5, pos-8]);
		m.addm(spawn(mitem_text, item_text:"Single Player", item_command:"m_pop;m_newgame sp", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);	pos += 16;
		m.addm(spawn(mitem_text, item_text:"Cooperative", item_command:"m_pop;m_newgame coop", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);	pos += 16;
		m.addm(spawn(mitem_text, item_text:"Deathmatch", item_command:"m_pop;m_newgame dm", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);	pos += 16;
		m.addm(spawn(mitem_text, item_text:"Team Deathmatch", item_command:"m_pop;m_newgame tdm", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);	pos += 16;

		m.addm(spawn(mitem_text, item_text:"Map Selection", item_command:"m_pop;m_newgame warp", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);	pos += 16;

		m.add(spawn (mitem_spinnymodel, item_text: "progs/soldier.mdl",firstframe:73, framecount:8, shootframe:81, shootframes:9), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);
		return;
	}
	
	pos = -100;

	banner = spawn(mitem_pic, item_text:"gfx/p_multi.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
	m.addm(banner, [(0-banner.item_size_x)*0.5, pos-32], [(0+banner.item_size_x)*0.5, pos-8]);

	if (gametype != "warp")
	{
		fr.addm(menuitemeditt_spawn(_("Hostname"), 		"hostname", '280 8'), 					[-160, pos], [160, pos+8]); pos += 8;
		fr.addm(menuitemcombo_spawn(_("Public"),			"sv_public",				'280 8', 	(engine == E_QSS)?_(	"0	\"LAN\" "
																			"1	\"Internet\" "
																		):_(	"-1	\"Reject All (Splitscreen)\" "
																			"0	\"Private (Manual IP Sharing)\" "
																			"1	\"Public (Manual Config)\" "
																			"2	\"Public (Holepunch)\" "
																			)),	[-160, pos], [160, pos+8]); pos += 8;
		fr.addm(menuitemcombo_spawn(_("Max Clients"),	"maxclients",				'280 8', _(
																			"2	\"Two\" "
																			"3	\"Three\" "
																			"4	\"Four\" "
																			"8	\"Eight\" "
																			"16	\"Sixteen\" "
																			"32	\"Thirty Two\" "
																			)),	[-160, pos], [160, pos+8]); pos += 8;
	}

	if (gametype == "dm" || gametype == "tdm")
	{
		if (m.get("deathmatch") == "0")
		{
			m.set("deathmatch", "1");
			m.set("coop", "0");
		}
		fr.addm(menuitemcombo_spawn(_("Deathmatch Mode"),	"deathmatch",					'280 8', _(
																			"1	\"Weapons Respawn\" "
																			"2	\"Weapons Stay\" "
																			"3	\"Powerups Respawn\" "
																			"4	\"Start With Weapons\" "
																			)),	[-160, pos], [160, pos+8]); pos += 8;
	}
	else
	{
		if (m.get("coop") == "0")
		{
			m.set("deathmatch", "0");
			m.set("coop", "1");
		}
	}
	if (gametype == "tdm")
	{
		if (m.get("teamplay") == "0")
			m.set("teamplay", "1");
	}
	if (gametype == "dm")
	{
		if (m.get("teamplay") != "0")
			m.set("teamplay", "0");
	}
	if (gametype == "coop")
		fr.addm(menuitemcheck_spawn(_("No Friendly Fire"),		"teamplay", 			'280 8'),	[-160, pos], [160, pos+8]), pos += 8;
//	if (gametype == "dm" || gametype == "tdm")

	if (gametype == "coop")
		m.set("map", "start");
	else
	{
		m.set("map", "");

		if (gametype != "warp")
		{
			fr.addm(menuitemcombo_spawn(_("Time Limit"),	"timelimit",					'280 8', _(
																			"0	\"No Limit\" "
																			"5	\"5 minutes\" "
																			"10	\"10 minutes\" "
																			"15	\"15 minutes\" "
																			"20	\"20 minutes\" "
																			"25	\"25 minutes\" "
																			"30	\"30 minutes\" "
																			"35	\"35 minutes\" "
																			"40	\"40 minutes\" "
																			"45	\"45 minutes\" "
																			"50	\"50 minutes\" "
																			"55	\"55 minutes\" "
																			"60	\"1 hour\" "
																			)),	[-160, pos], [160, pos+8]); pos += 8;
			fr.addm(menuitemcombo_spawn(_("Frag Limit"),	"fraglimit",					'280 8', _(
																			"0	\"No Limit\" "
																			"10	\"10 frags\" "
																			"20	\"20 frags\" "
																			"30	\"30 frags\" "
																			"40	\"40 frags\" "
																			"50	\"50 frags\" "
																			"60	\"60 frags\" "
																			"70	\"70 frags\" "
																			"80	\"80 frags\" "
																			"90	\"90 frags\" "
																			"100	\"100 frags\" "
																			)),	[-160, pos], [160, pos+8]); pos += 8;
		}

		pos += 8;
		fr.addm(spawn(mitem_maplist, item_scale:8, item_flags:IF_CENTERALIGN|IF_SELECTABLE), [-160, pos], [150, 100-16-8]), pos = 100-16;
	}

	fr.addm(spawn(mitem_text, item_text:"BEGIN!", item_command:"m_pop;m_newgame begin", item_scale:16, item_flags:IF_CENTERALIGN), [-160, pos], [160, pos+16]);pos += 16;


	//random art for style
	if (gametype == "coop")
	{
		m.add(spawn (mitem_spinnymodel, item_text: "progs/soldier.mdl", firstframe:73, framecount:8, shootframe:81, shootframes:9), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, -240/2], [0, 240/2]);
	}
	else
	{
		m.add(spawn (mitem_spinnymodel, item_text: "progs/player.mdl", firstframe:0, framecount:6, shootframe:119, shootframes:6), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);
	}

	addmenuback(m);
};