#ifdef MENU
#define cltime time
#endif

#ifdef CSQC
#define csqconly
#else
#define csqconly __strip
#endif
#ifdef MENU
#define menuonly
#else
#define menuonly __strip
#endif



//#include "mitems.qh"

__strip var float dp_workarounds;

#define IF_SELECTABLE		(1<<0)	//can accept KFOCUSED/MFOCUSED and key events etc. cannot be selected otherwise.
#define IF_INTERACT		(1<<1)	//generic interaction flag for use by the widgets themselves.
#define IF_RESIZABLE		(1<<2)	//may be resized, either by parent (it takes the full space) or by user.
#define IF_MFOCUSED		(1<<3)	//mouse is currently sitting over it
#define IF_KFOCUSED		(1<<4)	//has keyboard focus
#define IF_NOKILL		(1<<5)	//kill button is disabled. move to frame/menu?
#define IF_DRAGGABLE		(1<<6)	//can be dragged. this is stupid.
#define IF_DROPPABLE		(1<<7)	//dragged items can be dropped on this item.
#define IF_CLIENTMOVED		(1<<8)	//recalc required client dimensions (and toggle scrollbars if needed). move to frame?
#define IF_CENTERALIGN		(1<<9)	//
#define IF_RIGHTALIGN		(1<<10)	//
#define IF_NOCURSOR		(1<<11)	//when mgrabbed, the cursor will not be shown
#define IF_ISFRAME		(1<<12)	//is derived from mitem_frame (helps debugging recurion).
#define IF_FOCUSFOLLOWSMOUSE 	(1<<13)	//on frames, child keyboard focus (mostly) follows the mouse cursor. not like windows, but handy on things with lots of buttons. annoying on the desktop. move to frame?
#define IF_INVISIBLE		(1<<14)

#define RS_X_MIN_PARENT_MIN	0x0000
#define RS_X_MIN_PARENT_MID	0x0001
#define RS_X_MIN_PARENT_MAX	0x0002
#define RS_X_FRACTION			0x0004

#define RS_X_MAX_OWN_MIN		0x0000
#define RS_X_MAX_PARENT_MIN	0x0010
#define RS_X_MAX_PARENT_MID	0x0020
#define RS_X_MAX_PARENT_MAX	0x0040

#define RS_Y_MIN_PARENT_MIN	0x0000
#define RS_Y_MIN_PARENT_MID	0x0100
#define RS_Y_MIN_PARENT_MAX	0x0200
#define RS_Y_FRACTION			0x0400

#define RS_Y_MAX_OWN_MIN		0x0000
#define RS_Y_MAX_PARENT_MIN	0x1000
#define RS_Y_MAX_PARENT_MID	0x2000
#define RS_Y_MAX_PARENT_MAX	0x4000

//the 3d effect needs some sort of fake lighting values.
//these are for bumps. invert for inset things.
#define TD_TOP '0.8 0.8 0.8'
#define TD_LFT '0.6 0.6 0.6'
#define TD_RGT '0.2 0.2 0.2'
#define TD_BOT '0.0 0.0 0.0'

#ifndef MENUBACK_RGB
#define MENUBACK_RGB '0.4 0.365 0.29'
//#define MENUBACK_RGB '0.5 0.5 0.6'
#endif
#ifndef MENUBACK_ALPHA
#define MENUBACK_ALPHA 0.8
#endif

//#ifdef TARGET_FTE
//#pragma TARGET FTE
//#endif

//#define HEIRACHYDEBUG	//enable this and press f1 to print out the current menuitem tree

class mitem_frame;
.vector		mins;		//gravity mins
.vector		maxs;		//gravity mins
class mitem
{
	void() 	mitem;
	virtual void(vector pos)										item_draw;
	virtual float(vector pos, float scan, float char, float down)	item_keypress = {return FALSE;};
	virtual void(mitem newfocus, float changedflag)					item_focuschange;	//move into frame?
	virtual string(string key)										get;
	virtual void(string key, string newval)							set;
	virtual float(string key)										isvalid;
	virtual void() 													item_remove;
	virtual void() 													item_resized;
	float 			item_flags;		//contains IF_ flags
	vector 		item_position;		//relative to the parent's client area. which is admittedly not well defined...
	vector 		item_size;			//interaction bounding box.
	float 			item_scale;		//text etc scale
	vector 		item_rgb;			//colours!
	float 			item_alpha;		//transparency value
	string 		item_text;			//used as a unique identifier
	string 		item_command;		//something to do when clicked. move out?
	mitem_frame	item_parent;		//the item that contains us. make mitem_frame?
	mitem 		item_next;		//the next child within the parent

	float			resizeflags;


	static void() totop;
};

//this struct is used to access the various drawing functions. this allows the functions to be replaced for worldspace stuff
typedef struct uiinfo_s
{
	void(float min_x, float min_y, float max_x, float max_y)							setcliparea;
	float(vector min, string imagename, vector size, vector col, float alph, float drawflag)	drawpic;
	float(vector min, vector size, vector col, float alph, float drawflag)				drawfill;
	float(vector min, float charcode, vector scale, vector col, float alph, float drawflag)	drawcharacter;
	float(vector min, string text, vector scale, vector col, float alph, float drawflag)		drawstring;

	mitem kgrabs;		//kfocused or mfocused or both or none to say what sort of grabs is in effect.
	mitem mgrabs;		//says who has stolen all input events.

	float ctrlheld;		//ctrl is held.
	float shiftheld;	//shift is held.
	float mousedown;	//which mouse buttons are currently held.
	vector oldmousepos;	//old mouse position, to track whether its moved.
	vector mousepos;	//current mouse position.
	vector screensize;	//total screen size
	vector drawrectmin;	//minimum scissor region, to clamp children to within the confines of its parent.
	vector drawrectmax;	//maximum scissor region

#ifndef MENU
	//menuqc has no concept of the world and cannot display or query 3d positions nor projections. Any related UI elements are thus not available to menuqc.
	//these globals are not part of the ui struct either, because they're illegal in world UIs.
	float havemouseworld;		//whether the mouseworld stuff is valid - ie: that the cursor is in a 3d view
	vector mouseworldnear;	//position of the mouse cursor upon the near clip plane in world space
	vector mouseworldfar;		//position of the mouse cursor upon the far(ish) clip plane in world space
#endif
} uiinfo_t;
var uiinfo_t ui =
{
	drawsetcliparea,
	drawpic,
	drawfill,
	drawcharacter,
	drawstring
};



void() queryscreensize =
{
#pragma warning disable F333
#ifdef MENU
	//there is no proper way to do this.
	//fte thus has special checks for these cvars, and they should not be autocvars if you want them to work properly.
	ui.screensize[0] = cvar("vid_conwidth");
	ui.screensize[1] = cvar("vid_conheight");
	ui.screensize[2] = 0;
#endif
#ifdef CSQC
	#ifndef CSQC_SIMPLE
		//make sure the screensize is set.
		normalize('0 0 1');	//when getproperty fails to return a meaningful value, make sure that we don't read some random stale value.
		ui.screensize = (vector)getproperty(VF_SCREENVSIZE);
		if (ui.screensize[2])	//lingering return value from normalize.
		{
			ui.screensize[2] = 0;
			ui.screensize[0] = cvar("vid_conwidth");
			ui.screensize[1] = cvar("vid_conheight");
		}
	#endif
#endif
#pragma warning enable F333
};

//helper function
float(vector minp, vector sz) mouseinbox =
{
	if (ui.mousepos[0] < minp_x)
		return FALSE;
	if (ui.mousepos[1] < minp_y)
		return FALSE;
	if (ui.mousepos[0] >= minp_x + sz_x)
		return FALSE;
	if (ui.mousepos[1] >= minp_y + sz_y)
		return FALSE;

	return TRUE;
};


void(mitem ch, vector parentsize) mitem_parentresized;

#include "mitem_frame.qc"
void() mitem::item_resized =
{
	if (this.item_parent)
		this.item_parent.item_flags |= IF_CLIENTMOVED;
};


#ifdef HEIRACHYDEBUG
void mitem_printnode(float depth, mitem root, string prefix)
{
	mitem ch;
	string col = "";
	if (root.item_flags & IF_KFOCUSED && root.item_flags & IF_MFOCUSED)
		col = "^2";
	else if (root.item_flags & IF_KFOCUSED)
		col = "^5";
	else if (root.item_flags & IF_MFOCUSED)
		col = "^3";

	print(sprintf("%s%s%i:%s%s\n", col, strpad(depth, ""), root, prefix, root.item_text));

	//can only recurse into items which are actually frames.
	if (root.item_flags & IF_ISFRAME)
	{
		mitem_frame fr = (mitem_frame)root;
		depth+=1;
		for(ch = fr.item_children; ch; ch = ch.item_next)
		{
			if (ch.item_parent != root)
				print("corrupt parent\n");
			if (ch.item_next == ch)
			{
				print("infinite loop\n");
				break;
			}
			string pre = "";
			if (fr.item_mactivechild == ch)
				pre = strcat(pre, "m");
			if (fr.item_kactivechild == ch)
				pre = strcat(pre, "k");
			if (pre)
				pre = strcat(pre, " ");
			mitem_printnode(depth, ch, pre);
		}
	}
};
void mitem_printtree(mitem_frame root, string from, float line)
{
	print(sprintf("%s:%g\n", from, line));
	mitem_printnode(0, root, "");
}
#endif

string(string key) mitem::get =
{
	//walk through to the parent for menu parents to track all this.
	if (this.item_parent)
		return this.item_parent.get(key);
	else	//no parent, just assume its a cvar.
		return cvar_string(key);
};
void(string key, string newval) mitem::set =
{
	//walk through to the parent for menu parents to track all this.
	if (this.item_parent)
		this.item_parent.set(key, newval);
	else	//no parent, just assume its a cvar.
		cvar_set(key, newval);
};
float(string key) mitem::isvalid =
{
	//walk through to the parent for menu parents to track all this.
	if (this.item_parent)
		return this.item_parent.isvalid(key);
	else	//no parent, just assume its a cvar.
		return cvar_type(key);
};


/***************************************************************************
basic 'null' item, for completeness, every other item logically inherits from this.
drawing this just shows its text right-aligned at w/2. most things will want to override this.
*/
void(mitem newfocus, float flag) mitem::item_focuschange =
{
};

//z order is determined by the list order. the ones at the end (oldest) are on top.
void() mitem::totop =
{
	mitem_frame p = item_parent;
	mitem prev;
	//unlink it
	if (p.item_children == this)
		prev = p.item_children = item_next;
	else
	{
		for (prev = p.item_children; prev; prev = prev.item_next)
		{
			if (prev.item_next == this)
			{
				prev.item_next = item_next;
				break;
			}
		}
	}

	item_next = __NULL__;
	if (prev)
	{
		//add it on the end
		while(prev.item_next)
			prev = prev.item_next;
		prev.item_next = this;
	}
	else
		p.item_children = this;
};

vector(mitem it) menuitem_textcolour =
{
	local vector col;
	col = it.item_rgb;
	if (!(it.item_flags & IF_SELECTABLE) && it.item_keypress && it.item_command != "")
		col *= 0.2;
	else
	{
		if (it.item_flags & IF_MFOCUSED)
			col_z = 0;
		if (it.item_flags & IF_KFOCUSED)
			col_x = 0;
	}
	return col;
};
void(vector pos) mitem::item_draw =
{
	vector col;
	pos_x += this.item_size[0] / 2 - stringwidth(this.item_text, TRUE, '1 1 0'*this.item_scale) - 8;
	col = menuitem_textcolour(this);
	ui.drawstring(pos, this.item_text, '1 1 0' * this.item_scale, col, this.item_alpha, 0);
};
void() mitem::item_remove =
{
	local mitem ch;
	//any children got removed by the frame code.

	//make sure the item is removed from its parent.
	local mitem_frame p = this.item_parent;
	if (p)
	{
		if (p.item_exclusive == this)
			p.item_exclusive = __NULL__;

		if (p.item_children == this)
			p.item_children = this.item_next;
		else
		{
			for(ch = p.item_children; ch; ch = ch.item_next)
			{
				if (ch.item_next == this)
				{
					ch.item_next = this.item_next;
					break;
				}
			}
		}
	}

	//pick a different item on the parent.
	if (p.item_mactivechild == this)
		p.item_focuschange(__NULL__, IF_MFOCUSED);
	if (p.item_kactivechild == this)
	{
		for (ch = p.item_children; ch; ch = ch.item_next)
		{
			if (ch.item_flags & IF_SELECTABLE)
				break;
		}
		p.item_focuschange(ch, IF_KFOCUSED);
	}
	if (ui.kgrabs == this)
		ui.kgrabs = __NULL__;
	if (ui.mgrabs == this)
		ui.mgrabs = __NULL__;
	remove((entity)this);

	//force mousefocus to update
	ui.oldmousepos = '-1 -1';
};
void() mitem::mitem =
{
	if (!this.item_scale)
		this.item_scale = 1;
	if (!this.item_rgb)
		this.item_rgb = '1 1 1';
	if (!this.item_alpha)
		this.item_alpha = 1;

	//force mousefocus to update
	ui.oldmousepos = '-1 -1';
};
