/*
 * 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
 */
#include <stdio.h>
#include <ctype.h>
#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"

Menu	*MenuList = null(Menu *);

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

typedef	struct	OptionTableTag OptionTable;

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

MenuOption *create_new_option()
{
	MenuOption *NewOption;

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

Menu	*create_new_menu()
{
	Menu	*NewMenu;

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


void	install_menu(NewMenu, TotalMade)
Menu	*NewMenu;
int	TotalMade;
{
	MenuOption **NewOptions;
	int	i;

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

void	load_menu(FileName)
char	*FileName;
{
	FILE	*fp;
	Menu	*NewMenu = NULL;
	char	*line, *command;
	int	linenum = 0;
	int	CurTotal = 0;
	char	*name, *func;
	int	FuncNum;
	MenuOption **NewOptions;
	int	i;

	if ((fp = fopen(FileName, "r")) == null(FILE *))
	{
		say("Unable to open %s", FileName);
		return;
	}
	while (fgets(buffer, BIG_BUFFER_SIZE, fp))
	{
		buffer[strlen(buffer)-1] = '\0';
		linenum++;
		line = buffer;
		while (isspace(*line))
			line++;
		if (*line == '#' || !(command = next_arg(line, &line)))
			continue;
		if (!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 (!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 (!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(&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);
}

int	ShowMenuByWindow(window, flags)
Window	*window;
int	flags;
{
	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);
			fwrite(ThisMenu->Options[i]->Name,
				strlen(ThisMenu->Options[i]->Name), 1, stdout);
			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++;
	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;
}

void	ListMenu(ThisMenu)
Menu	*ThisMenu;
{
	int	i;
	int	j;

	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:"");
	}
}

int	ShowMenu(Name)
char	*Name;
{
	Menu	*ThisMenu;
	Window	*window;
	WindowMenu *menu_info;

	window = curr_scr_win;
	menu_info = &window->menu;
	ThisMenu = (Menu *) find_in_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);
}

void	set_menu(Value)
char	*Value;
{
	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 = null(Menu *);
		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);
/*
		redraw_window(window);
 */
	}
}

void	enter_menu()
{
	if (!curr_scr_win->menu.menu)
		return;
	current_screen->inside_menu = 1;
	ShowMenuByWindow(curr_scr_win, SMF_CURSONLY);
}


void	menu_previous(args)
char	*args;
{
}

void	menu_submenu(args)
char	*args;
{
}

void	menu_exit(args)
char	*args;
{
	current_screen->inside_menu = -1;
}

void	menu_channels(args)
char	*args;
{
}

void	menu_key(key)
char	key;
{
	Window *window;
	WindowMenu *menu_info;
	Menu	*ThisMenu;

	window = curr_scr_win;
	menu_info = &window->menu;
	ThisMenu = menu_info->menu;
	ShowMenuByWindow(window, SMF_CURSONLY | SMF_NOCURSOR);
	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':
		hold_mode(null(Window *), OFF, 1);
		break;
	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 */
	}
	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);
}

void	menu_command(args)
char	*args;
{
	parse_line(null(char *), args, empty_string, 0, 0);
}
