#help_index "Menus"
#help_file "::/Doc/Menus"

CTask *MenuTask()
{
	CTask *res = sys_focus_task;

	while (res && !res->cur_menu)
		res = res->parent_task;

	return res;
}

CMenuEntry *sys_cur_submenu_entry = NULL;

public CMenuEntry *MenuSubEntryFind(CMenuEntry *haystack_first, U8 *needle_entry_name)
{//You probably don't need this. Use dir / and $LK,"MenuEntryFind",A="MN:MenuEntryFind"$().
	while (haystack_first)
	{
		if (!StrCompare(haystack_first->name, needle_entry_name))
			return haystack_first;
		haystack_first = haystack_first->next;
	}

	return NULL;
}

public CMenuEntry *MenuEntryFind(CMenu *haystack_menu, U8 *needle_full_name)
{//Find pulldown entry. Fs->cur_menu is probably the menu you want.
//Just 2 levels -- across top and down are valid, currently.
	U8			*st, *st2;
	CMenuEntry	*tmpse;

	if (!haystack_menu || !needle_full_name)
		return NULL;
	st = StrNew(needle_full_name);
	st2 = StrNew(needle_full_name);
	tmpse = (&haystack_menu->sub)(U8 *) - offset(CMenuEntry.sub);
	while (*st && tmpse)
	{
		StrFirstRemove(st, "/", st2);
		tmpse=MenuSubEntryFind(tmpse->sub, st2);
	}
	Free(st);
	Free(st2);

	return tmpse;
}

CMenuEntry *MenuNewSub(CCompCtrl *cc, CTask *task)
{
	CMenuEntry *tmpme = NULL, *tmpse;

	if (cc->token==TK_IDENT)
	{
		tmpme = CAlloc(sizeof(CMenuEntry),task);
		if (StrLen(cc->cur_str) > 31)
			cc->cur_str[31] = 0;
		StrCopy(tmpme->name, cc->cur_str);
		if (Lex(cc) == '(')
		{
			tmpme->message_code = MESSAGE_KEY_DOWN_UP;
			if (Lex(cc) != ',' && cc->token != ')')
				tmpme->message_code = LexExpressionI64(cc);
			if (cc->token == ',')
				Lex(cc);
			if (cc->token != ',' && cc->token != ')')
				tmpme->arg1 = LexExpressionI64(cc);
			if (cc->token == ',')
				Lex(cc);
			if (cc->token != ',' && cc->token != ')')
				tmpme->arg2 = LexExpressionI64(cc);
			if (cc->token != ')')
				LexExcept(cc, "Missing ')' at ");
			if (Lex(cc) != ';')
				LexExcept(cc, "Missing ';' at");
			Lex(cc); //Skip ;
		}
		else if (cc->token == '{')
		{
			Lex(cc); //Skip {
			tmpme->dir = TRUE;
			tmpse = &tmpme->sub;
			while (tmpse && cc->token != '}')
				tmpse = tmpse->next = MenuNewSub(cc, task);
			if (cc->token != '}')
				LexExcept(cc, "Missing '}' at ");
			else
				Lex(cc); //Skip }
		}
		else
			LexExcept(cc, "Expecting '{' at ");
	}
	return tmpme;
}

public CMenu *MenuNew(U8 *st, I64 flags = 0, CTask *task = NULL)
{//Parse a menu. You probably don't need this.
	CMenu		*m;
	CMenuEntry	*tmpse;
	CCompCtrl	*cc = CompCtrlNew(st, CCF_DONT_FREE_BUF);

	if (!task)
		task = Fs;
	Lex(cc);
	m = CAlloc(sizeof(CMenu), task);
	m->task  = task;
	m->flags = flags;
	m->attr  = BLUE << 4 + YELLOW;
	tmpse = &m->sub;
	while (tmpse)
		tmpse = tmpse->next = MenuNewSub(cc, task);
	CompCtrlDel(cc);

	return m;
}

public CMenu *MenuFile(U8 *filename, I64 flags = 0, CTask *task = NULL)
{//Parse a pulldown menu file. You probably don't need this.
	CMenu	*m;
	U8		*st = MStrPrint("#include \"%s\"", filename);

	m = MenuNew(st, flags, task);
	Free(st);

	return m;
}

U0 MenuDelSub(CMenuEntry *tmpme)
{
	CMenuEntry *tmpse, *tmpse1;

	if (tmpme)
	{
		tmpse = tmpme->sub;
		while (tmpse)
		{
			tmpse1 = tmpse->next;
			MenuDelSub(tmpse);
			tmpse = tmpse1;
		}
		Free(tmpme);
	}
}

public U0 MenuDel(CMenu *m)
{//Delete a manu. You probably don't need this.
	CMenuEntry *tmpme, *tmpme1;

	if (!m)
		return;
	tmpme = m->sub;
	while (tmpme)
	{
		tmpme1 = tmpme->next;
		MenuDelSub(tmpme);
		tmpme = tmpme1;
	}
	Free(m);
}

I64 MenuEntryWidth(CMenuEntry *tmpme)
{
	I64			 res = StrLen(tmpme->name);
	CMenuEntry	*tmpse = tmpme->sub;

	while (tmpse)
	{
		res = MaxI64(res, StrLen(tmpse->name));
		tmpse = tmpse->next;
	}

	return res + 1;
}

public CMenu *MenuPush(U8 *st)
{//Save old pulldown menu and replace with new from str.
	CMenu *m = MenuNew(st);

	m->next = Fs->cur_menu;
	Fs->cur_menu = m;

	return m;
}

public CMenu *MenuFilePush(U8 *filename)
{//Save old pulldown menu and replace with new from file.
	CMenu *m = MenuFile(filename);

	m->next = Fs->cur_menu;
	Fs->cur_menu = m;

	return m;
}

public U0 MenuPop()
{//Restore old pulldown menu. Delete just-deactivated menu.
	CMenu *m = Fs->cur_menu;

	if (!m)
		return;
	Fs->cur_menu = m->next;
	MenuDel(m);
}

U0 DrawMenu(CDC *dc)
{
	CMenu		*m;
	CMenuEntry	*tmpme, *tmpse, *cur_submenu = NULL;
	U8			*st = NULL;
	CTask		*task = MenuTask;
	I64			 i, w, x0, y0, x1 = mouse.pos.x, y1 = mouse.pos.y;

	if (!TaskValidate(task) || !(m = task->cur_menu))
	{
		sys_cur_submenu_entry = NULL;
		return;
	}
	dc->color = m->attr >> 4;
	GrRect(dc, 0, 0, GR_WIDTH, FONT_HEIGHT);
	x0 = 0;
	tmpme = m->sub;
	while (tmpme)
	{
		w = MenuEntryWidth(tmpme) * FONT_WIDTH;
		if (x0 <= x1 < x0 + w) {
			if (0 <= y1 < FONT_HEIGHT)
			{
				dc->color = m->attr & 15;
				GrRect(dc, x0, 0, w, FONT_HEIGHT);
				dc->color = m->attr >> 4;
			}
			else
				dc->color = m->attr & 15;
			GrPrint(dc, x0, 0, "%s", tmpme->name);
			y0 = FONT_HEIGHT;
			tmpse = tmpme->sub;
			while (tmpse)
			{
				if (tmpse->checked)
					i = m->attr ^ 0xFF;
				else
					i = m->attr;
				if (y0 <= y1 < y0 + FONT_HEIGHT)
				{
					if (tmpse->message_code == MESSAGE_KEY_DOWN ||
						tmpse->message_code == MESSAGE_KEY_DOWN_UP)
					{
						if (!tmpse->arg2)
							tmpse->arg2 = Char2ScanCode(tmpse->arg1);
						st = ScanCode2KeyName(tmpse->arg2);
					}
					sys_cur_submenu_entry = cur_submenu = tmpse;
					dc->color = i & 15;
					GrRect(dc, x0, y0, w, FONT_HEIGHT);
					dc->color = i >> 4;
					GrPrint(dc, x0, y0, "%s", tmpse->name);
					if (st)
					{
						dc->color = i >> 4;
						GrRect(dc, x0 + w, y0 - FONT_HEIGHT,
								(StrLen(st) + 1) * FONT_WIDTH, FONT_HEIGHT * 3);
						dc->color = i & 15;
						GrPrint(dc, x0 + w, y0, "%s", st);
						Free(st);
					}
				}
				else
				{
					dc->color = i >> 4;
					GrRect(dc, x0, y0, w, FONT_HEIGHT);
					dc->color = i & 15;
					GrPrint(dc, x0, y0, "%s", tmpse->name);
				}
				y0 += FONT_HEIGHT;
				tmpse = tmpse->next;
			}
		}
		else
		{
			dc->color = m->attr & 15;
			GrPrint(dc, x0, 0, "%s", tmpme->name);
		}
		x0 += w;
		tmpme = tmpme->next;
	}
	sys_cur_submenu_entry = cur_submenu;
}
