/***************************************************************************
 menubar.c Copyright (C) 1999,2000 Christoph Reichenbach


 This program may be modified and copied freely according to the terms of
 the GNU general public license (GPL), as long as the above copyright
 notice and the licensing information contained herein are preserved.

 Please refer to www.gnu.org for licensing details.

 This work is provided AS IS, without warranty of any kind, expressed or
 implied, including but not limited to the warranties of merchantibility,
 noninfringement, and fitness for a specific purpose. The author will not
 be held liable for any damage caused by this work or derivatives of it.

 By using this source code, you agree to the licensing terms as stated
 above.


 Please contact the maintainer for bug reports or inquiries.

 Current Maintainer:

    Christoph Reichenbach (CJR) [jameson@linuxgames.com]

***************************************************************************/
/* Management and drawing operations for the SCI0 menu bar */
/* I currently assume that the hotkey information used in the menu bar is NOT
** used for any actual actions on behalf of the interpreter.
*/

#include <sci_memory.h>
#include <engine.h>
#include <menubar.h>

#define SIZE_INF 32767

/*
static int __active = 0;

inline void*
__my_malloc(long size, char *function, int line)
{
  void *retval = sci_malloc(size);
  __active++;
  fprintf(stderr,"[%d] line %d, %s: malloc(%d) -> %p\n", __active, line, function, size, retval);
  return retval;
}

inline void*
__my_realloc(void *origin, long size, char *function, int line)
{
  void *retval = sci_realloc(origin, size);
  fprintf(stderr,"line %d, %s: realloc(%p, %d) -> %p\n", line, function, origin, size, retval);
  return retval;
}

inline void
__my_free(void *origin, char *function, int line)
{
  free(origin);
  fprintf(stderr,"[%d] line %d, %s: free(%p)\n", __active, line, function, origin);
  __active--;
}

#define malloc(x) __my_malloc(x, __PRETTY_FUNCTION__, __LINE__)
#define realloc(x,y) __my_realloc(x,y, __PRETTY_FUNCTION__, __LINE__)
#define free(x) __my_free(x, __PRETTY_FUNCTION__, __LINE__)
*/

menubar_t *
menubar_new()
{
	menubar_t *tmp = sci_malloc(sizeof(menubar_t));
	tmp->menus_nr = 0;

	return tmp;
}

void
menubar_free(menubar_t *menubar)
{
	int i;

	for (i = 0; i < menubar->menus_nr; i++) {
		menu_t *menu = &(menubar->menus[i]);
		int j;

		for (j = 0; j < menu->items_nr; j++) {
			if (menu->items[j].keytext)
				sci_free (menu->items[j].keytext);
			if (menu->items[j].text)
				sci_free (menu->items[j].text);
		}

		sci_free(menu->items);
		sci_free(menu->title);
	}

	if (menubar->menus_nr)
		sci_free (menubar->menus);

	sci_free(menubar);
}


int
_menubar_add_menu_item(gfx_state_t *state, menu_t *menu, int type, char *left, char *right,
		       int font, int key, int modifiers, int tag, heap_ptr text_pos)
/* Returns the total text size, plus MENU_BOX_CENTER_PADDING if (right != NULL) */
{
	menu_item_t *item;
	int total_left_size = 0;
	int width, height;

	if (menu->items_nr == 0) {
		menu->items = (menu_item_t *) sci_malloc(sizeof(menu_item_t));
		menu->items_nr = 1;
	} else menu->items = (menu_item_t *) sci_realloc(menu->items, sizeof(menu_item_t) * ++(menu->items_nr));

	item = &(menu->items[menu->items_nr - 1]);

	memset(item, 0, sizeof(menu_item_t));

	if ((item->type = type) == MENU_TYPE_HBAR)
		return 0;

	/* else assume MENU_TYPE_NORMAL */
	item->text = left;
	if (right) {
		int end = strlen(right);
		item->keytext = right;
		while (end && isspace(right[end]))
			right[end--] = 0; /* Remove trailing whitespace */
		item->flags = MENU_ATTRIBUTE_FLAGS_KEY;
		item->key = key;
		item->modifiers = modifiers;
	} else {
		item->keytext=NULL;
		item->flags = 0;
	}

	if (right) {
		int width, height;
		gfxop_get_text_params(state, font, right, SIZE_INF, &width, &height, 0);
		total_left_size = MENU_BOX_CENTER_PADDING + (item->keytext_size = width);
	}

	item->enabled = 1;
	item->tag = tag;
	item->text_pos = text_pos;
	gfxop_get_text_params(state, font, left, SIZE_INF, &width, &height, 0);

	return total_left_size + width;
}

void
menubar_add_menu(gfx_state_t *state, menubar_t *menubar, char *title, char *entries, int font, byte *heapbase)
{
	int i, add_freesci = 0;
	menu_t *menu;
	char tracker;
	char *left = NULL, *right, *left_origin = NULL;
	int string_len = 0;
	int tag = 0, c_width, max_width = 0;
	char *_heapbase = (char *) heapbase;
	int height;

	if (menubar->menus_nr == 0) {
#ifdef MENU_FREESCI_BLATANT_PLUG
		add_freesci = 1;
#endif
		menubar->menus = sci_malloc(sizeof(menu_t));
		menubar->menus_nr = 1;
	} else menubar->menus = sci_realloc(menubar->menus, ++(menubar->menus_nr) * sizeof (menu_t));

	menu = &(menubar->menus[menubar->menus_nr-1]);
	memset(menu, 0, sizeof(menu_t));
	menu->items_nr = 0;
	menu->title = sci_strdup(title);

	gfxop_get_text_params(state, font, menu->title, SIZE_INF, &(menu->title_width), &height, 0);

	do {
		tracker = *entries++;

		if (!left) { /* Left string not finished? */

			if (tracker == '=') { /* Hit early-SCI tag assignment? */

				left = sci_strndup(entries - string_len - 1, string_len);
				tag = atoi(entries++);
				tracker =  *entries++;
			}
			if ((tracker == 0 && string_len > 0) || (tracker == '=') || (tracker == ':')) { /* End of entry */
				int entrytype = MENU_TYPE_NORMAL;
				char *inleft;

				if (!left)
					left = sci_strndup(entries - string_len - 1, string_len);

				inleft = left;
				while (isspace(*inleft))
					inleft++; /* Seek beginning of actual string */

				if (!strncmp(inleft, MENU_HBAR_STRING_1, strlen(MENU_HBAR_STRING_1))
				    || !strncmp(inleft, MENU_HBAR_STRING_2, strlen(MENU_HBAR_STRING_2))
				    || !strncmp(inleft, MENU_HBAR_STRING_3, strlen(MENU_HBAR_STRING_3)))
					{
						entrytype = MENU_TYPE_HBAR; /* Horizontal bar */
						free(left);
						left = NULL;
					}

				c_width = _menubar_add_menu_item(state, menu, entrytype, left, NULL, font, 0, 0, tag,
								 (heap_ptr)((entries - _heapbase) - string_len - 1));
				if (c_width > max_width)
					max_width = c_width;

				string_len = 0;
				left = NULL; /* Start over */

			} else if (tracker == '`') { /* Start of right string */

				if (!left)
					left = sci_strndup(left_origin = (entries - string_len - 1), string_len);
				string_len = 0; /* Continue with the right string */

			}
			else string_len++; /* Nothing special */

		} else { /* Left string finished => working on right string */
			if ((tracker == ':') || (tracker == 0)) { /* End of entry */
				int key, modifiers = 0;

				right = sci_strndup(entries - string_len - 1, string_len);

				if (right[0] == '#') {
					right[0] = SCI_SPECIAL_CHAR_FUNCTION; /* Function key */

					key = SCI_K_F1 + ((right[1] - '1') << 8);

					if (right[1] == '0')
						key = SCI_K_F10; /* F10 */

					if (right[2]=='=') {
						tag = atoi(right + 3);
						right[2] = 0;
					};
				}
				else if (right[0] == '@') { /* Alt key */
					right[0] = SCI_SPECIAL_CHAR_ALT; /* ALT */
					key = right[1];
					modifiers = SCI_EVM_ALT;

					if ((key >= 'a') && (key <= 'z'))
						right[1] = key - 'a' + 'A';

					if (right[2]=='=') {
						tag = atoi(right+3);
						right[2] = 0;
					};

				}
				else {

					if (right[0] == '^') {
						right[0] = SCI_SPECIAL_CHAR_CTRL; /* Control key - there must be a replacement... */
						key = right[1];
						modifiers = SCI_EVM_CTRL;

						if ((key >= 'a') && (key <= 'z'))
							right[1] = key - 'a' + 'A';

						if (right[2]=='=') {
							tag = atoi(right+3);
							right[2] = 0;
						}

					}
					else {
						key = right[0];
						if ((key >= 'a') && (key <= 'z'))
							right[0] = key - 'a' + 'A';

						if (right[1]=='=') {
							tag=atoi(right+2);
							right[1] = 0;
						}
					}

					if ((key >= 'A') && (key <= 'Z'))
						key = key - 'A' + 'a'; /* Lowercase the key */
				}


				i = strlen(right);

				while (i>0 && right[--i] == ' ')
					right[i] = 0; /* Cut off chars to the right */

				c_width = _menubar_add_menu_item(state, menu, MENU_TYPE_NORMAL, left, right, font, key,
								 modifiers, tag, (heap_ptr)(left_origin - _heapbase));
				tag = 0;
				if (c_width > max_width)
					max_width = c_width;

				string_len = 0;
				left = NULL;  /* Start over */

			} else string_len++; /* continuing entry */
		} /* right string finished */

	} while (tracker);

#ifdef MENU_FREESCI_BLATANT_PLUG
	if (add_freesci) {

		char *freesci_text = sci_strdup ("About FreeSCI");
		c_width = _menubar_add_menu_item(state, menu, MENU_TYPE_NORMAL, freesci_text, NULL, font, 0, 0, 0, 0);
		if (c_width > max_width)
			max_width = c_width;

		menu->items[menu->items_nr-1].flags = MENU_FREESCI_BLATANT_PLUG;
	}
#endif /* MENU_FREESCI_BLATANT_PLUG */
	menu->width = max_width;
}

int
menubar_match_key(menu_item_t *item, int message, int modifiers)
{
	if ((item->key == message)
	    && ((modifiers & (SCI_EVM_CTRL | SCI_EVM_ALT)) == item->modifiers))
		return 1;

	if (message == '\t'
	    && item->key == 'i'
	    && ((modifiers & (SCI_EVM_CTRL | SCI_EVM_ALT)) == 0)
	    && item->modifiers == SCI_EVM_CTRL)
		return 1; /* Match TAB to ^I */

	return 0;
}

int
menubar_set_attribute(state_t *s, int menu_nr, int item_nr, int attribute, int value)
{
	menubar_t *menubar = s->menubar;
	menu_item_t *item;

	if ((menu_nr < 0) || (item_nr < 0))
		return 1;

	if ((menu_nr >= menubar->menus_nr) || (item_nr >= menubar->menus[menu_nr].items_nr))
		return 1;

	item = menubar->menus[menu_nr].items + item_nr;

	switch (attribute) {

	case MENU_ATTRIBUTE_SAID:
		if (value) {
			item->said_pos = value;
			memcpy(item->said, s->heap + value, MENU_SAID_SPEC_SIZE); /* Copy Said spec */
			item->flags |= MENU_ATTRIBUTE_FLAGS_SAID;

		} else
			item->flags &= ~MENU_ATTRIBUTE_FLAGS_SAID;

		break;

	case MENU_ATTRIBUTE_TEXT:
		free(item->text);
		assert(value);
		item->text = sci_strdup((char *) s->heap + value);
		item->text_pos = value;
		break;

	case MENU_ATTRIBUTE_KEY:
		if (item->keytext)
			free(item->keytext);

		if (value) {

			item->key = value;
			item->modifiers = 0;
			item->keytext = sci_malloc(2);
			item->keytext[0] = value;
			item->keytext[1] = 0;
			item->flags |= MENU_ATTRIBUTE_FLAGS_KEY;
			if ((item->key >= 'A') && (item->key <= 'Z'))
				item->key = item->key - 'A' + 'a'; /* Lowercase the key */


		} else {

			item->keytext = NULL;
			item->flags &= ~MENU_ATTRIBUTE_KEY;

		}
		break;

	case MENU_ATTRIBUTE_ENABLED:
		item->enabled = value;
		break;

	case MENU_ATTRIBUTE_TAG:
		item->tag = value;
		break;

	default:
		sciprintf("Attempt to set invalid attribute of menu %d, item %d: 0x%04x\n",
			  menu_nr, item_nr, attribute);
		return 1;
	}

	return 0;
}

int
menubar_get_attribute(state_t *s, int menu_nr, int item_nr, int attribute)
{
	menubar_t *menubar = s->menubar;
	menu_item_t *item;

	if ((menu_nr < 0) || (item_nr < 0))
		return -1;

	if ((menu_nr >= menubar->menus_nr) || (item_nr >= menubar->menus[menu_nr].items_nr))
		return -1;

	item = menubar->menus[menu_nr].items + item_nr;

	switch (attribute) {
	case MENU_ATTRIBUTE_SAID:
		return item->said_pos;

	case MENU_ATTRIBUTE_TEXT:
		return item->text_pos;

	case MENU_ATTRIBUTE_KEY:
		return item->key;

	case MENU_ATTRIBUTE_ENABLED:
		return item->enabled;

	case MENU_ATTRIBUTE_TAG:
		return item->tag;

	default:
		sciprintf("Attempt to read invalid attribute from menu %d, item %d: 0x%04x\n",
			  menu_nr, item_nr, attribute);
		return -1;
	}
}

int
menubar_item_valid(state_t *s, int menu_nr, int item_nr)
{
	menubar_t *menubar = s->menubar;
	menu_item_t *item;

	if ((menu_nr < 0) || (item_nr < 0))
		return 0;

	if ((menu_nr >= menubar->menus_nr) || (item_nr >= menubar->menus[menu_nr].items_nr))
		return 0;

	item = menubar->menus[menu_nr].items + item_nr;

	if ((item->type == MENU_TYPE_NORMAL)
	    && item->enabled)
		return 1;

	return 0; /* May not be selected */
}


int
menubar_map_pointer(state_t *s, int *menu_nr, int *item_nr, gfxw_port_t *port)
{
	menubar_t *menubar = s->menubar;
	menu_t *menu;

	if (s->gfx_state->pointer_pos.y <= 10) { /* Re-evaulate menu */
		int x = MENU_LEFT_BORDER;
		int i;

		for (i = 0; i < menubar->menus_nr; i++) {
			int newx = x + MENU_BORDER_SIZE * 2 + menubar->menus[i].title_width;

			if (s->gfx_state->pointer_pos.x < x)
				return 0;

			if (s->gfx_state->pointer_pos.x < newx) {
				*menu_nr = i;
				*item_nr = -1;
			}

			x = newx;
		}

		return 0;

	} else {
		int row = (s->gfx_state->pointer_pos.y / 10) - 1;

		if ((*menu_nr < 0) || (*menu_nr >= menubar->menus_nr))
			return 1; /* No menu */
		else
			menu = menubar->menus + *menu_nr; /* Menu is valid, assume that it's popped up */

		if (menu->items_nr <= row)
			return 1;

		if ((s->gfx_state->pointer_pos.x < port->bounds.x) || (s->gfx_state->pointer_pos.x > port->bounds.x + port->bounds.xl))
			return 1;

		if (menubar_item_valid(s, *menu_nr, row))
			*item_nr = row; /* Only modify if we'll be hitting a valid element */

		return 0;
	}

}
