/*
 * menu.c: stuff for the menu's..
 *
 * Written By Troy Rollo <troy@cbme.unsw.oz.au>
 *
 * Copyright(c) 1991/1992.
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
 */

#if 0
static	char	rcsid[] = "@(#)$Id: menu.c,v 1.8 1995/01/06 15:16:52 mrg stable $";
#endif

#include "irc.h"

#include "menu.h"
#include "list.h"
#include "ircaux.h"
#include "term.h"
#include "window.h"
#include "screen.h"
#include "input.h"
#include "vars.h"
#include "output.h"
#include "edit.h"

Menu	*MenuList = (Menu *) 0;

struct	OptionTableTag
{
	char	*name;
	void	(*func) _((char *));
};

typedef	struct	OptionTableTag OptionTable;

static	OptionTable	OptionsList[] =
{
	{ "PREVIOUS",	menu_previous },
	{ "MENU",	menu_submenu },
	{ "EXIT",	menu_exit },
	{ "CHANNELS",	menu_channels },
	{ "COMMAND",	menu_command },
	{ (char *) 0,	(void (*) _((char *))) 0}
};

MenuOption *create_new_option _((void))
{
	MenuOption *NewOption;

	NewOption = (MenuOption *) new_malloc(sizeof(MenuOption));
	NewOption->Name = (char *) 0;
	NewOption->Arguments = (char *) 0;
	NewOption->Func = (void (*) ()) 0;
	return NewOption;
}

Menu	*create_new_menu _((void))
{
	Menu	*NewMenu;

	NewMenu = (Menu *) new_malloc(sizeof(Menu));
	NewMenu->Name = (char *) 0;
	NewMenu->TotalOptions = 0;
	NewMenu->Options = (MenuOption **) 0;
	return NewMenu;
}


#ifdef __STDC__
void install_menu (Menu *NewMenu, int TotalMade)
#else
void install_menu(NewMenu, TotalMade)
	Menu	*NewMenu;
	int	TotalMade;
#endif
{
	MenuOption **NewOptions;
	int	i;

	if (!NewMenu)
		return;
	if (TotalMade != NewMenu->TotalOptions)
	{
		NewOptions = (MenuOption **) new_malloc(TotalMade * sizeof(MenuOption *));
		for (i = 0; i < TotalMade; i++)
			NewOptions[i] = NewMenu->Options[i];
		new_free((char **)&NewMenu->Options);
		NewMenu->Options = NewOptions;
		NewMenu->TotalOptions = TotalMade;
	}
	add_to_list((List **)&MenuList, (List *)NewMenu);
	say("Menu \"%s\" added", NewMenu->Name);
}

#ifdef __STDC__
void load_menu (char *FileName)
#else
void load_menu(FileName)
	char	*FileName;
#endif
{
	FILE	*fp;
	Menu	*NewMenu = NULL;
	char	*line, *command;
	int	linenum = 0;
	int	CurTotal = 0;
	char	*name, *func;
	int	FuncNum;
	MenuOption **NewOptions;
	int	i;
	char buffer[BIG_BUFFER_SIZE + 1];

	/* XXXX -- need to call expand_twiddle here */

	if ((fp = fopen(FileName, "r")) == (FILE *) 0)
	{
		say("Unable to open %s", FileName);
		return;
	}
	while (fgets(buffer, BIG_BUFFER_SIZE, fp))
	{
		buffer[strlen(buffer)-1] = '\0';
		linenum++;
		line = buffer;
		while (my_isspace(*line))
			line++;
		if (*line == '#' || !(command = next_arg(line, &line)))
			continue;
		if (!my_stricmp(command, "MENU"))
		{
			if (!line || !*line)
			{
				put_it("Missing argument in line %d of %s",
						linenum, FileName);
				continue;
			}
			install_menu(NewMenu, CurTotal);
			NewMenu = create_new_menu();
			malloc_strcpy(&NewMenu->Name, line);
		}
		else if (!my_stricmp(command, "OPTION"))
		{
			if (!(name = new_next_arg(line, &line)) ||
			    !(func = next_arg(line, &line)))
			{
				say("Missing argument in line %d of %s",
						linenum, FileName);
				continue;
			}
			for (i=0; OptionsList[i].name; i++)
				if (!my_stricmp(func, OptionsList[i].name))
					break;
			if (!OptionsList[i].name)
			{
				say("Unknown menu function \"%s\" in line %d of %s",
					func, linenum, FileName);
				continue;
			}
			FuncNum = i;
			if (++CurTotal > NewMenu->TotalOptions)
			{
				NewOptions = (MenuOption **)
					new_malloc(sizeof(MenuOption *) *
						(CurTotal+4));
				for (i = 0; i < NewMenu->TotalOptions; i++)
					NewOptions[i] = NewMenu->Options[i];
				new_free((char **)&NewMenu->Options);
				NewMenu->Options = NewOptions;
				NewMenu->TotalOptions = CurTotal + 5;
			}
			NewMenu->Options[CurTotal-1] = create_new_option();
			malloc_strcpy(&NewMenu->Options[CurTotal-1]->Name,
				name);
			malloc_strcpy(&NewMenu->Options[CurTotal-1]->Arguments,
				line);
			NewMenu->Options[CurTotal-1]->Func =
					OptionsList[FuncNum].func;
		}
		else
			say("Unkown menu command in line %d of %s",
				linenum, FileName);
	}
	install_menu(NewMenu, CurTotal);
	fclose(fp);
}

#ifdef __STDC__
int ShowMenuByWindow (Window *window, int flags)
#else
int ShowMenuByWindow (window, flags)
	Window	*window;
	int	flags;
#endif
{
	int	i;
	int	largest;
	int	NumPerLine;
	int	len;
	WindowMenu *menu_info;
	Menu	*ThisMenu;
	int	CursorLoc;

	menu_info = &window->menu;
	ThisMenu = menu_info->menu;
	CursorLoc = menu_info->cursor;
	largest = 0;
	for (i = 0; i < ThisMenu->TotalOptions; i++)
		if ((len = strlen(ThisMenu->Options[i]->Name))>largest)
			largest = len;
	NumPerLine = (CO - CO % (largest + 3)) / (largest + 3);
	menu_info->items_per_line = NumPerLine;
	menu_info->lines = 0;
	for (i = 0; i < ThisMenu->TotalOptions; i++)
	{
		if ((flags & SMF_ERASE) && !(i % NumPerLine) &&
		    !(flags & SMF_CALCONLY))
		{
			term_move_cursor(0, window->top + menu_info->lines);
			term_clear_to_eol();
		}
		if ((i == CursorLoc || !(flags&SMF_CURSONLY)) &&
		    !(flags & SMF_CALCONLY))
		{
			if (i == CursorLoc && !(flags & SMF_NOCURSOR) &&
					current_screen->inside_menu == 1)
				term_standout_on();
			else
				term_bold_on();
			term_move_cursor((i % NumPerLine) * (largest + 3),
				window->top+menu_info->lines);
			term_puts(ThisMenu->Options[i]->Name, strlen(ThisMenu->Options[i]->Name));
			if (i == CursorLoc && !(flags & SMF_NOCURSOR))
				term_standout_off();
			else
				term_bold_off();
		}
		if (!((i + 1) % NumPerLine))
			menu_info->lines++;
	}
	if (i % NumPerLine)
		menu_info->lines++;

	if (!(flags & SMF_CALCONLY))
	{
#ifdef MENU_BOTTOM
		int width = CO;
		int bl = strlen(MENU_BOTTOM);

		term_move_cursor(0, window->top + menu_info->lines);
		term_clear_to_eol();
		term_move_cursor(0, window->top + menu_info->lines);
		term_standout_on();

		/* repeat the menu seperator the entire
		 * width of the screen.
		 */
		while (width > bl)
		{
			term_puts(MENU_BOTTOM, bl);
			width -= bl;
		}
		term_puts(MENU_BOTTOM, width);
		
		term_standout_off();
#endif
	}
#ifdef MENU_BOTTOM
	menu_info->lines++;
#endif

	window->display_size = window->bottom - window->top - menu_info->lines;
	if (window->cursor < 0)
		window->cursor = 0;
	fflush(stdout);
	update_input(UPDATE_JUST_CURSOR);
	return ThisMenu->TotalOptions;
}

#ifdef __STDC__
void ListMenu (char *Name)
#else
void ListMenu(Name)
	char	*Name;
#endif
{
	int	i;
	int	j;
	Menu *ThisMenu;

	if (!Name || !*Name)
		return;

	ThisMenu = (Menu *)find_in_list((List **)&MenuList, Name, 0);
	if (!ThisMenu)
	{
		say("Like, that menu doesnt exist or something.");
		return;
	}

	for (i = 0; i < ThisMenu->TotalOptions; i++)
	{
		for (j = 0; ThisMenu->Options[i]->Func != OptionsList[j].func;
				j++);
			say("  %-10s\t%-10s\t%s", ThisMenu->Options[i]->Name,
				OptionsList[j].name,
				ThisMenu->Options[i]->Arguments?
				ThisMenu->Options[i]->Arguments: 
				empty_string);
	}
}

#ifdef __STDC__
int ShowMenu(char *Name)
#else
int ShowMenu(Name)
	char	*Name;
#endif
{
	Menu	*ThisMenu;
	Window	*window;
	WindowMenu *menu_info;

	window = curr_scr_win;
	menu_info = &window->menu;
	ThisMenu = (Menu *) find_in_list((List **)&MenuList, Name, 0);
	if (!ThisMenu)
	{
		say("No such menu \"%s\"", Name);
		return -1;
	}
	menu_info->cursor = 0;
	menu_info->menu = ThisMenu;
	return ShowMenuByWindow(window, SMF_CALCONLY);
}

#ifdef __STDC__
void set_menu (char *Value)
#else
void set_menu(Value)
	char	*Value;
#endif
{
	Window	*window;
	WindowMenu *menu_info;
	ShrinkInfo SizeInfo;

	window = curr_scr_win;
	menu_info = &window->menu;
	if (!Value)
	{
		window->display_size = window->bottom - window->top;
		menu_info->menu = (Menu *) 0;
		menu_info->lines = 0;
		SizeInfo = resize_display(window);
		redraw_resized(window, SizeInfo, 0);
		update_input(UPDATE_JUST_CURSOR);
		current_screen->inside_menu = -1;
	}
	else
	{
		if (ShowMenu(Value) == -1)
		{
			set_string_var(MENU_VAR, NULL);
			return;
		}
		SizeInfo = resize_display(window);
		redraw_resized(window, SizeInfo, 0);
		ShowMenuByWindow(window, SMF_ERASE);
	}
}

#ifdef __STDC__
extern void enter_menu (char unused, char *not_used)
#else
extern void enter_menu (unused, not_used)
char unused, *not_used;
#endif
{
	if (!curr_scr_win->menu.menu)
		return;
	current_screen->inside_menu = 1;
	ShowMenuByWindow(curr_scr_win, SMF_CURSONLY);
}


#ifdef __STDC__
void menu_previous (char *args)
#else
void menu_previous(args)
	char	*args;
#endif
{
}

#ifdef __STDC__
void menu_submenu (char *args)
#else
void menu_submenu(args)
	char	*args;
#endif
{
}

#ifdef __STDC__
void menu_exit (char *args)
#else
void menu_exit(args)
	char	*args;
#endif
{
	current_screen->inside_menu = -1;
}

#ifdef __STDC__
void menu_channels (char *args)
#else
void menu_channels(args)
	char	*args;
#endif
{
}

#ifdef __STDC__
void menu_key (char key)
#else
void menu_key(key)
	char	key;
#endif
{
	Window *window;
	WindowMenu *menu_info;
	Menu	*ThisMenu;
	static int state = 0;

	window = curr_scr_win;
	menu_info = &window->menu;
	ThisMenu = menu_info->menu;
	ShowMenuByWindow(window, SMF_CURSONLY | SMF_NOCURSOR);

	/* 
	 * This little hack here allows us to parse the cursor
	 * keys while in menu mode.  This is a *hack*, so dont
	 * get too involved with it. =)
	 */
	if ((state == 0) && (key == 27))
	{
		state++;
		return;
	}
	else if ((state == 1) && ((key == '[') || (key == 'O')))
	{
		state++;
		return;
	}
	else if (state == 2)
	{
		switch (key)
		{
			case 'D' : key = 'b'; break; /* left */
			case 'C' : key = 'f'; break; /* right */
			case 'A' : key = 'u'; break; /* up */
			case 'B' : key = 'd'; break; /* down */
		}
	}

	state = 0;
	switch(key)
	{
		case 'U':
		case 'u':
		case 'P':
		case 'p':
		case 'k':
		case 'K':
		case 'U' - '@':
		case 'P' - '@':
			menu_info->cursor-=menu_info->items_per_line;
			break;
		case 'n':
		case 'd':
		case 'j':
		case 'N':
		case 'D':
		case 'J':
		case 'D' - '@':
		case 'N' - '@':
			menu_info->cursor+=menu_info->items_per_line;
			break;
		case 'b':
		case 'h':
		case 'B':
		case 'H':
		case 'B' - '@':
			menu_info->cursor--;
			break;
		case 'f':
		case 'l':
		case 'F':
		case 'L':
		case 'F' - '@':
			menu_info->cursor++;
			break;
		case '\033':
			break;
		case '\r':
		case '\n':
			/* There is no reason <return> shouldnt be a selector */
		case ' ':
		case '.':
			current_screen->inside_menu = 0;
			ThisMenu->Options[menu_info->cursor]->Func(
				ThisMenu->Options[menu_info->cursor]->Arguments);
			if (current_screen->inside_menu != -1)
				current_screen->inside_menu = 1;
			else
				current_screen->inside_menu = 0;
			return; /* The menu may not be here any more */
		case 'Q':
		case 'q':
			set_string_var(MENU_VAR, NULL);
			set_menu(NULL);	/* do it brute force */
			return;
	}
	if (menu_info->cursor>=menu_info->menu->TotalOptions)
		menu_info->cursor = menu_info->menu->TotalOptions - 1;
	if (menu_info->cursor < 0)
		menu_info->cursor = 0;
	if (current_screen->inside_menu)
		ShowMenuByWindow(window, SMF_CURSONLY);
}

#ifdef __STDC__
void menu_command (char *args)
#else
void
menu_command(args)
	char	*args;
#endif
{
	parse_line((char *) 0, args, empty_string, 0, 0);
}
