/***************************************************************************
Basic frame, containing sub-items.
Logically the parent of menu objects. Also used for tabs.
*/

#ifndef MITEM_FRAME_QC__
#define MITEM_FRAME_QC__

class mitem_vslider : mitem
{
	virtual float(vector pos, float scan, float char, float down) item_keypress;
	virtual void(vector pos) item_draw;
	void() mitem_vslider;
	float val;
	float minv;
	float maxv;
	float stride;	//size of one 'notch'
};

class mitem_frame : mitem
{
	virtual void(mitem newfocus, float changedflag)				item_focuschange;
	virtual float(vector pos, float scan, float char, float down)	item_keypress;
	virtual void()			item_resized;
	virtual void(vector pos)	item_draw;
	virtual void()			item_remove;
	virtual void(mitem fromitem, string cmd)	item_execcommand;

	mitem 		item_children;
	mitem 		item_mactivechild;
	mitem 		item_kactivechild;
	mitem		item_exclusive;

	mitem_vslider	vslider;	//displayed if any client item's max[y] > our item_size[y]
	vector 	item_framesize;	//x=sides, y=top, z=bottom
	float frame_hasscroll;

	static mitem(string title) findchildtext;
	static mitem(string title) findchildcmd;
	static void(mitem newitem, float originflags, vector originmin, vector originmax) add;
	static void(mitem newitem, vector pos) adda;
	static void(mitem newitem, vector originmin, vector originmax) addm;
	static void(mitem newitem, float originflags, vector originmin, vector originmax) addr;
	static void(mitem newitem, float ypos) addc;

	void() mitem_frame =
	{
		item_flags |= IF_ISFRAME;
	};
};

void(mitem fromitem, string cmd) mitem_frame::item_execcommand =
{
	if (item_parent)
		item_parent.item_execcommand(fromitem, cmd);
	else
		localcmd(strcat(cmd, "\n"));
};

mitem(string title) mitem_frame::findchildtext =
{
	mitem ch;
	for (ch = item_children; ch; ch = ch.item_next)
	{
		if (ch.item_text == title)
			return ch;
	}
	return __NULL__;
};
mitem(string title) mitem_frame::findchildcmd =
{
	mitem ch;
	for (ch = item_children; ch; ch = ch.item_next)
	{
		if (ch.item_command == title)
			return ch;
	}
	return __NULL__;
};


//adds an item with the desired origin settings
void(mitem newitem, float originflags, vector originmin, vector originmax) mitem_frame::add =
{
	newitem.item_next = item_children;
	item_children = newitem;
	newitem.item_parent = this;

	//set up position and resize
	newitem.resizeflags = originflags;
	newitem.mins = originmin;
	newitem.maxs = originmax;
	mitem_parentresized(newitem, [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])]);
	newitem.item_resized();
	
	item_flags |= IF_CLIENTMOVED;	//make sure it happens.

//	if (!item_kactivechild && (newitem.item_flags & IF_SELECTABLE))
//		item_focuschange(newitem, IF_KFOCUSED);
};

//adds an item to the parent with an absolute position relative to the parent's top-left. objects are expected to already have a size specified.
void(mitem newitem, vector pos) mitem_frame::adda =
{
	local vector parentsize = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];
	newitem.item_next = item_children;
	item_children = newitem;
	newitem.item_parent = this;

	newitem.resizeflags = RS_X_MIN_PARENT_MIN | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN;
	newitem.mins = pos;
	newitem.maxs = newitem.item_size;

	mitem_parentresized(newitem, parentsize);
	newitem.item_resized();
	
	item_flags |= IF_CLIENTMOVED;	//make sure it happens.

//	if (!item_kactivechild && (newitem.item_flags & IF_SELECTABLE))
//		item_focuschange(newitem, IF_KFOCUSED);
};

//adds an item to the parent in reverse order (ie: at the tail, so the actual order in code)
void(mitem newitem, float originflags, vector originmin, vector originmax) mitem_frame::addr =
{
	local vector parentsize = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];
	if (item_children)
	{
		local mitem prev;
		for (prev = item_children; prev.item_next; prev = prev.item_next)
			;
		prev.item_next = newitem;
		newitem.item_next = __NULL__;
	}
	else
	{
		newitem.item_next = item_children;
		item_children = newitem;
	}
	newitem.item_parent = this;

	newitem.resizeflags = originflags;
	newitem.mins = originmin;
	newitem.maxs = originmax;
	mitem_parentresized(newitem, parentsize);
	newitem.item_resized();
	
	item_flags |= IF_CLIENTMOVED;	//make sure it happens.

//	if (!item_kactivechild && (newitem.item_flags & IF_SELECTABLE))
//		item_focuschange(newitem, IF_KFOCUSED);
};

//adds an item on the parent with the x coord centered, and absolute y.
//if multiple items are at the same ypos, it will recenter all with respect to the others
void(mitem newitem, float ypos) mitem_frame::addc =
{
	float w, c;
	local mitem prev;
	local vector parentsize = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];

	newitem.item_next = item_children;
	item_children = newitem;
	newitem.item_position[1] = ypos;
	newitem.item_position[0] = (parentsize[0] - newitem.item_size[0])*0.5;
	newitem.item_parent = this;


	newitem.resizeflags = RS_X_MIN_PARENT_MID | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN;
	newitem.mins[0] = 0;
	newitem.mins[1] = ypos;
	newitem.maxs = newitem.item_size;

	//count the width of the other items at this height.
	w = 0;
	c = 0;
	for (prev = item_children; prev; prev = prev.item_next)
		if (prev.resizeflags == newitem.resizeflags && prev.mins[1] == ypos && prev.maxs[1] == newitem.maxs[1])
		{
			w += prev.maxs[0];
			c += 1;
		}

	//distribute them evenly (from the right, because its add-at-head)
	w += (c-1)*16;	//this much gap space
	for (prev = item_children; prev; prev = prev.item_next)
	{
		if (prev.resizeflags == newitem.resizeflags && prev.mins[1] == ypos && prev.maxs[1] == newitem.maxs[1])
		{
			w -= prev.maxs[0];
			prev.mins[0] = (w+prev.maxs[0])/-2;
			mitem_parentresized(prev, parentsize);
			w -= 16;
		}
	}
	newitem.item_resized();
	
	item_flags |= IF_CLIENTMOVED;	//make sure it happens.

//	if (!item_kactivechild && (newitem.item_flags & IF_SELECTABLE))
//		item_focuschange(newitem, IF_KFOCUSED);
};

//Adds the item in the exact middle of the parent, in both axis
void(mitem newitem, vector min, vector max) mitem_frame::addm =
{
	this.add(newitem, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, min, max);
};

//updates this.item_activechild, and calls focus notifications to ensure things get the message. 
//flag should be IF_MFOCUSED or IF_KFOCUSED
void(mitem newfocus, float flag) mitem_frame::item_focuschange =
{
	local mitem it;

	if (newfocus == this)
	{
		//our focus didn't change, but the parent is telling us that *it* got changed while we're the focus
		if (flag & IF_MFOCUSED)
		{
			if (item_parent.item_flags & IF_MFOCUSED && item_parent.item_mactivechild == this)
				item_flags |= IF_MFOCUSED;
			else
				item_flags = item_flags - (item_flags&IF_MFOCUSED);
			//and tell the child
			it = item_mactivechild;
			if (it)
				if (it.item_focuschange)
					it.item_focuschange(it, IF_MFOCUSED);
		}
		if (flag & IF_KFOCUSED)
		{
			if (item_parent.item_flags & IF_KFOCUSED && item_parent.item_kactivechild == this)
				item_flags |= IF_KFOCUSED;
			else
				item_flags = item_flags - (item_flags&IF_KFOCUSED);
			//and tell the child
			it = item_kactivechild;
			if (it)
				if (it.item_focuschange)
					it.item_focuschange(it, IF_KFOCUSED);
		}
		return;
	}

	if ((flag & IF_MFOCUSED) && item_mactivechild != newfocus)
	{
		//make key focus follow the mouse cursor. this should probably only apply to button menus or something? :s
		if (newfocus && (item_flags & IF_FOCUSFOLLOWSMOUSE))
			flag |= IF_KFOCUSED;

		it = item_mactivechild;
		item_mactivechild = newfocus;
		if (it)
		{
			it.item_flags = it.item_flags - (it.item_flags&IF_MFOCUSED);
			if (it.item_focuschange)
				it.item_focuschange(it, IF_MFOCUSED);
		}
		it = item_mactivechild;
		if (it)
		{
			it.item_flags = it.item_flags | (item_flags & IF_MFOCUSED);
			if (it.item_focuschange)
				it.item_focuschange(it, IF_MFOCUSED);
		}
	}

	if ((flag & IF_KFOCUSED) && item_kactivechild != newfocus)
	{
		it = item_kactivechild;
		item_kactivechild = newfocus;
		if (it)
		{
			it.item_flags = it.item_flags - (it.item_flags&IF_KFOCUSED);
			if (it.item_focuschange)
				it.item_focuschange(it, IF_KFOCUSED);
		}
		it = item_kactivechild;
		if (it)
		{
			it.item_flags = it.item_flags | (item_flags & IF_KFOCUSED);
			if (it.item_focuschange)
				it.item_focuschange(it, IF_KFOCUSED);
		}
	}
};


float(vector pos, float scan, float char, float down) mitem_vslider::item_keypress =
{
	if (down != 1)
	{
		if (scan == K_MOUSE1 && ui.mgrabs == this)
			ui.mgrabs = __NULL__;
		return FALSE;
	}
	if (scan == K_MWHEELDOWN)
		val = bound(minv, val+stride, maxv);
	else if (scan == K_MWHEELUP)
		val = bound(minv, val-stride, maxv);
	else if (scan == K_MOUSE1)
		ui.mgrabs = this;
	else
		return FALSE;
	return TRUE;
};
void(vector pos) mitem_vslider::item_draw =
{
	float f;
	float w = item_size[0];
	float h = item_size[1];
	float isize = w;
	
	vector v = drawgetimagesize("scrollbars/slider.tga");

	if (v != '0 0 0')
	{
		ui.drawpic(pos, "scrollbars/arrow_up.tga", [w, w], '1 1 1', item_alpha, 0);	//top
		pos_y += w;
		h -= w*2;
		ui.drawpic(pos + [0, h], "scrollbars/arrow_down.tga", [w, w], '1 1 1', item_alpha, 0);	//bottom
		ui.drawpic(pos, "scrollbars/slidebg.tga", [w, h], '1 1 1', item_alpha, 0);	//back-middle
	
		isize = (v_y * w) / (v_x);
		if (isize > h/2)
			isize = h/2;
	}
	else
	{
//		ui.drawfill(pos, [w, w], '1 1 1', item_alpha, 0);	//top
//		pos_y += w;
//		h -= w*2;
//		ui.drawfill(pos + [0, h], [w, w], '1 1 1', item_alpha, 0);	//bottom
		ui.drawfill(pos, [w, h], '0.5 0.5 0.5', item_alpha, 0);	//back-middle
	}

	if (ui.mgrabs == this)
	{
		f = (ui.mousepos[1] - pos_y - (isize/2))/(h - isize);
		f = bound(0, f, 1);
		val = minv + (f * (maxv - minv));
	}
	else
		f = bound(0, (val - minv) / (maxv - minv), 1);

	h -= isize;
	if (v != '0 0 0')
		ui.drawpic(pos + [0, h*f], "scrollbars/slider.tga", [w, isize], '1 1 1', item_alpha, 0);	//back-middle
	else
		ui.drawfill(pos + [0, h*f], [w, isize], '1 1 1', item_alpha, 0);	//back-middle
};
void() mitem_vslider::mitem_vslider =
{
	item_size[0] = 16;
	item_size[1] = 128;
};


mitem(mitem_frame menu, mitem item) menu_previousitem =
{
	if (!item)
		item = menu.item_children;
	local mitem stop = item;
	local mitem prev;
	if (!stop)
		return stop;
	while(1)
	{
		for(prev = menu.item_children; prev.item_next; prev = prev.item_next)
		{
			if (prev.item_next == item)
				break;
		}
		if (prev == stop || (prev.item_flags & IF_SELECTABLE))
			return prev;
		item = prev;
	}
};
mitem(mitem_frame menu, mitem item) menu_nextitem =
{
	if (!item)
		item = menu.item_children;
	local mitem stop = item;
	if (!stop)
		return stop;
	while(1)
	{
		item = item.item_next;
		if (!item)
			item = menu.item_children;

		if (item == stop || (item.item_flags & IF_SELECTABLE))
			return item;
	}
};

float(vector pos, float scan, float char, float down) mitem_frame::item_keypress =
{
	mitem ch;
	float handled = FALSE;

	//compensate for the frame
	pos[0] += item_framesize[0];
	pos[1] += item_framesize[1];

	if (scan >= K_MOUSE1 && scan <= K_MOUSE5 && scan != K_MWHEELUP && scan != K_MWHEELDOWN)
	{
		if (item_exclusive)
			ch = item_exclusive;
		else 
			ch = item_mactivechild;
		if (down)	//keyboard focus follows on mouse click.
		{
			item_focuschange(ch, IF_KFOCUSED);
		}
	}
	else
	{
		ch = item_kactivechild;
		if (!ch && down)
		{
			//if there's no key child active, then go and pick one so you can just start using keyboard access etc.
			if (item_exclusive)
				ch = item_exclusive;
			else if (item_mactivechild)
				ch = item_mactivechild;
			else
			{
				mitem c;
				for (c = item_children; c; c = c.item_next)
				{
					if (c.item_flags & IF_SELECTABLE)
						ch = c;
				}
			}
			if (ch)
				item_focuschange(ch, IF_KFOCUSED);
			ch = item_kactivechild;
		}
	}

	if (vslider)
		pos[1] -= vslider.val;

	if (ch)
	{
		if (ch.item_keypress)
			handled = ch.item_keypress(pos + ch.item_position, scan, char, down);
	}
	if (vslider && !handled && (scan == K_MWHEELUP || scan == K_MWHEELDOWN))	//give mwheel to the slider if its visible.
		handled = vslider.item_keypress(pos + vslider.item_position, scan, char, down);

	return handled;
};
void() mitem_frame::item_remove =
{
	local mitem ch;
	while (this.item_children)
	{
		ch = this.item_children;
		this.item_children = ch.item_next;
		ch.item_remove();
	}
	super::item_remove();
};
void() mitem_frame::item_resized =
{
	mitem ch;
	vector framemax = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];
	for (ch = this.item_children; ch; ch = ch.item_next)
	{
		vector os = ch.item_size;
		mitem_parentresized(ch, framemax);
		if (ch.item_resized && ch.item_size != os)
			ch.item_resized();
	}
	item_flags |= IF_CLIENTMOVED;	//make sure it happens.

	super::item_resized();
};
void(vector pos) mitem_frame::item_draw =
{
	local vector omin = ui.drawrectmin, omax = ui.drawrectmax;
	local mitem ch;
	local vector clientpos;
	local vector clientsize;

	if (frame_hasscroll && (item_flags & IF_CLIENTMOVED))
	{
		//if a client object moved, make sure the scrollbar is updated
		item_flags &~= IF_CLIENTMOVED;
		clientsize= '0 0';
		for(ch = item_children; ch; ch = ch.item_next)
		{
			if (clientsize[0] < ch.item_position[0] + ch.item_size[0])
				clientsize[0] = ch.item_position[0] + ch.item_size[0];
			if (clientsize[1] < ch.item_position[1] + ch.item_size[1])
				clientsize[1] = ch.item_position[1] + ch.item_size[1];
		}
//		if (clientsize[0] > item_size[0] - item_framesize[0]*2)
//			//add hscroll
		if (clientsize[1] > item_size[1] - (item_framesize[1]+item_framesize[2]))
		{
			if (!vslider)
			{
				local mitem_vslider tmp;
				tmp = spawn(mitem_vslider, val:0, stride:8);
				vslider = tmp;
			}
			vslider.maxv = clientsize[1] - (item_size[1] - (item_framesize[1]+item_framesize[2]));
		}
		else if (vslider)
		{
			vslider.item_remove();
			vslider = (mitem_vslider)__NULL__;
		}
	}
	else if (!frame_hasscroll && this.vslider)
	{
		vslider.item_remove();
		vslider = (mitem_vslider)__NULL__;
	}


	//compensate for the frame
	pos[0] += item_framesize[0];
	pos[1] += item_framesize[1];

	clientpos = pos;
	clientsize = this.item_size;
	clientsize[0] -= item_framesize[0]*2;
	clientsize[1] -= (item_framesize[1]+item_framesize[2]);

	if (vslider)
	{
		//scroll+shrink the client area to fit the slider on it.
		clientpos[1] -= vslider.val;
		clientsize[0] -= vslider.item_size[0];
	}


	if (ui.mousepos != ui.oldmousepos)
	{
		local mitem newfocus = __NULL__;
		//if the mouse moved, select the new item
		if (item_exclusive)
			newfocus = item_exclusive;
		else
		{
			for(ch = item_children; ch; ch = ch.item_next)
			{
				if (ch.item_flags & IF_SELECTABLE)
				if (mouseinbox(clientpos + ch.item_position, ch.item_size))
				{
					newfocus = ch;
				}
			}
		}
		if (vslider)
			if (mouseinbox(pos + [clientsize[0], 0], vslider.item_size))
				newfocus = vslider;
		this.item_focuschange(newfocus, IF_MFOCUSED);
	}

	//clip the draw rect to our area, so children don't draw outside it. don't draw if its inverted.
	if (pos_x > ui.drawrectmin[0])
		ui.drawrectmin[0] = pos_x;
	if (pos_y > ui.drawrectmin[1])
		ui.drawrectmin[1] = pos_y;
	if (pos_x+clientsize_x < ui.drawrectmax[0])
		ui.drawrectmax[0] = pos_x+clientsize_x;
	if (pos_y+clientsize_y < ui.drawrectmax[1])
		ui.drawrectmax[1] = pos_y+clientsize[1];
	if (ui.drawrectmax[0] > ui.drawrectmin[0] && ui.drawrectmax[1] > ui.drawrectmin[1])
	{
		ui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);
		if (item_exclusive)
			item_exclusive.item_draw(clientpos + ch.item_position);
		else
		{
			for(ch = item_children; ch; ch = ch.item_next)
			{
				if not (ch.item_flags & IF_INVISIBLE)
					ch.item_draw(clientpos + ch.item_position);
			}
		}
		ui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);

		if (vslider)
		{
			vslider.item_size[1] = clientsize[1];
			vslider.item_draw(pos + [clientsize[0], 0]);
		}
	}
	ui.drawrectmin = omin;
	ui.drawrectmax = omax;
};
#define menuitemframe_spawn(sz) spawn(mitem_frame, item_size:sz)

#endif //MITEM_FRAME_QC__
