/*
 *     XaoS, a fast portable realtime fractal zoomer 
 *                  Copyright (C) 1996 by
 *
 *      Jan Hubicka          (hubicka@paru.cas.cz)
 *      Thomas Marsh         (tmarsh@austin.ibm.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#undef _EFENCE_
#ifdef _plan9_
#include <u.h>
#include <libc.h>
#include <ctype.h>
#else
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#endif
#include <config.h>
#include <fconfig.h>
#ifndef _plan9_
#include <assert.h>
#endif
#include <filter.h>
#include <ui_helper.h>
#include <ui.h>
#include <timers.h>
#include <xmenu.h>
#include <grlib.h>
#include "uiint.h"


#define NMENUS 20
struct ui_menuitems
{
  int x, y, width, height;
  CONST menuitem *item;
  int separator;
};
static struct ui_menu
{
  int x, y, width, height;
  CONST char *name;
  CONST char *fullname;
  int namewidth;
  int selected;
  int n;
  int flags;
  struct ui_menuitems *items;
  struct uih_window *window;
  tl_timer *timer;
}
 *ui_menus[NMENUS];
int ui_nmenus;

#define MENU_HORIZONTAL 1
#define MENU_PRESSED 2
#define MENU_AUTOHIDE 4

#define SUBMENUWIDTH xtextwidth(uih->font, ">")
#define MENUPAUSE xtextwidth(uih->font, "X")
#define MENUWIDTH(a) (xtextwidth(uih->font, "w")+MENUPAUSE+xtextwidth(uih->font, a)+SUBMENUWIDTH)
#define HMENUWIDTH(a) (xtextwidth(uih->font, a)+xtextwidth(uih->font, "  "))

#define SEPARATORSIZE 6
static struct ui_menuitems *
ui_getmenuitems (CONST char *name, int *width1, int *height1, int *n1,
		 int horizontal)
{
  CONST menuitem *item;
  int nseparators = 0;
  int i;
  int width = 0;
  int n;
  struct ui_menuitems *items;
  for (n = 0; (item = menu_item (name, n)) != NULL; n++)
    if (item->type == MENU_SEPARATOR)
      nseparators++;
  n -= nseparators;
  *n1 = n;
  *height1 = n * xtextheight (uih->font) + nseparators * SEPARATORSIZE;
  items = (struct ui_menuitems *) malloc (n * sizeof (struct ui_menuitems));
  nseparators = 0;
  for (i = 0; i < n; i++)
    {
      int w;
      int sbehind = -1;
      nseparators--;
      do
	{
	  nseparators++;
	  sbehind++;
	  item = menu_item (name, i + nseparators);
	}
      while (item->type == MENU_SEPARATOR);
      items[i].item = item;
      items[i].separator = sbehind;
      if (horizontal)
	{
	  w = HMENUWIDTH (items[i].item->name);
	  if (items[i].item->key)
	    {
	      char c[10];
	      sprintf (c, "(%s)", items[i].item->key);
	      w += xtextwidth (uih->font, c);
	    }
	}
      else
	{
	  w = MENUWIDTH (items[i].item->name);
	  if (items[i].item->key)
	    {
	      char c[10];
	      sprintf (c, " %s ", items[i].item->key);
	      w += xtextwidth (uih->font, c);
	    }
	}
      items[i].width = w;
      items[i].height = xtextheight (uih->font) + 1;
      if (w > width)
	width = w;
    }
  *width1 = width;
  return (items);
}
static void
ui_menusize (uih_context * c, int *x, int *y, int *w, int *h, void *data)
{
  struct ui_menu *m = (struct ui_menu *) data;
  *x = m->x;
  *y = m->y;
  *w = m->width;
  *h = m->height;
}
static void
ui_drawmenu (uih_context * c, void *data)
{
  struct ui_menu *m = (struct ui_menu *) data;
  int i;
  int width1 = xtextwidth (c->font, "w");
  char s[2];
  s[1] = 0;
  if (!(m->flags & MENU_HORIZONTAL))
    xprint (c->image, c->font, m->x + (m->width - m->namewidth) / 2,
	    m->y + BORDERWIDTH, m->fullname, c->encoding, SELCOLOR (c), BGCOLOR (c), 0);
  for (i = 0; i < m->n; i++)
    {
      int color = (i == m->selected ? SELCOLOR (c) : FGCOLOR (c));
      int pressed = 0;
      if ((uih->palette->type & BITMAPS) && i == m->selected)
	{
	  pressed = TEXT_PRESSED;
	  color = BGCOLOR (c);
	  xrectangle (uih->image, m->items[i].x, m->items[i].y,
		      m->items[i].width, m->items[i].height, FGCOLOR (c));
	}
      if (!(m->flags & MENU_HORIZONTAL))
	{
	  if (m->items[i].separator)
	    {
	      xhline (c->image, m->x + 5,
		      m->items[i].y - 2 - SEPARATORSIZE / 2, m->width - 10,
		      BGCOLOR (c));
	      xhline (c->image, m->x + 5,
		      m->items[i].y - 1 - SEPARATORSIZE / 2, m->width - 10,
		      LIGHTGRAYCOLOR (c));
	    }
	  if (i < 10)
	    s[0] = '0' + (i == 9 ? 0 : i + 1);
	  else
	    s[0] = 'A' + (i - 10);
	  xprint (c->image, c->font, m->items[i].x, m->items[i].y, s, c->encoding, color,
		  BGCOLOR (c), pressed);
	  if (menu_enabled (m->items[i].item, uih))
	    {
	      xprint (c->image, c->font, m->items[i].x + width1,
		      m->items[i].y, "X", c->encoding, color, BGCOLOR (c), pressed);
	    }
	  xprint (c->image, c->font, m->items[i].x + width1 + MENUPAUSE,
		  m->items[i].y, m->items[i].item->name, c->encoding, color, BGCOLOR (c),
		  pressed);
	  if (m->items[i].item->key)
	    {
	      char ch[20];
	      sprintf (ch, " %s ", m->items[i].item->key);
	      xprint (c->image, c->font,
		      m->items[i].x + m->items[i].width - SUBMENUWIDTH -
		      xtextwidth (uih->font, ch), m->items[i].y, ch, c->encoding,
		      LIGHTGRAYCOLOR (c), BGCOLOR (c), pressed);
	    }
	  if (m->items[i].item->type == MENU_SUBMENU)
	    xprint (c->image, c->font,
		    m->items[i].x + m->items[i].width - SUBMENUWIDTH,
		    m->items[i].y, ">", c->encoding, color, BGCOLOR (c), pressed);
	}
      else
	{
	  xprint (c->image, c->font, m->items[i].x, m->items[i].y,
		  m->items[i].item->name, c->encoding, color, BGCOLOR (c), pressed);
	  if (m->items[i].item->key)
	    {
	      char ch[20];
	      sprintf (ch, "%s", m->items[i].item->key);
	      xprint (c->image, c->font,
		      m->items[i].x + xtextwidth (uih->font,
						  m->items[i].item->name) + 2,
		      m->items[i].y, ch, c->encoding, LIGHTGRAYCOLOR (c), BGCOLOR (c),
		      pressed);
	    }
	}
    }
}
static struct ui_menu *
ui_buildmenu (CONST char *name, int x, int y, int flags)
{
  int shift = 0;
  int width, height;
  int textheight = xtextheight (uih->font);
  struct ui_menu *menu;
  int i;
  menu = (struct ui_menu *) malloc (sizeof (*menu));
  menu->timer = tl_create_timer ();
  tl_reset_timer (menu->timer);
  menu->flags = flags;
  menu->items =
    ui_getmenuitems (name, &width, &height, &menu->n,
		     flags & MENU_HORIZONTAL);
  menu->selected = -1;
  menu->fullname = menu_fullname (name);
  menu->name = name;
  menu->namewidth = xtextwidth (uih->font, menu->fullname);
  if (!(menu->flags & MENU_HORIZONTAL))
    {
      if (menu->namewidth > width)
	width = menu->namewidth;
      width += 2 * BORDERWIDTH;
      height += 2 * BORDERHEIGHT + xtextheight (uih->font);
      if (x + width > uih->image->width)
	x = uih->image->width - width;
      if (y + height > uih->image->height)
	y = uih->image->height - height;
      if (x < 0)
	x = 0;
      if (y < 0)
	y = 0;
      shift = 0;
      for (i = 0; i < menu->n; i++)
	{
	  shift += menu->items[i].separator * SEPARATORSIZE;
	  menu->items[i].x = x + BORDERWIDTH;
	  menu->items[i].y = y + BORDERWIDTH + textheight * (i + 1) + shift;
	  menu->items[i].width = width - 2 * BORDERWIDTH;
	  menu->items[i].height = textheight;
	}
    }
  else
    {
      int line = 0;
      int xpos = BORDERWIDTH;
      x = 0, width = uih->image->width;
      for (i = 0; i < menu->n; i++)
	{
	  if (xpos + 2 * BORDERWIDTH + menu->items[i].width >
	      uih->image->width)
	    xpos = BORDERWIDTH, line++;
	  menu->items[i].x = xpos;
	  menu->items[i].y = y + BORDERWIDTH + line * textheight;
	  xpos += menu->items[i].width;
	  menu->items[i].height = textheight;
	}
      height = (line + 1) * textheight + 2 * BORDERWIDTH;
    }
  menu->selected = -1;
  menu->window =
    uih_registerw (uih, ui_menusize, ui_drawmenu, menu, DRAWBORDER);
  uih->display = 1;
  menu->x = x;
  menu->y = y;
  menu->width = width;
  menu->height = height;
  return (menu);
}
static void
ui_closemenu (struct ui_menu *menu)
{
  free (menu->items);
  tl_free_timer (menu->timer);
  uih_removew (uih, menu->window);
  uih->display = 1;
  free (menu);
}
static void
ui_openmenu (CONST char *name, int x, int y, int flags)
{
  if (ui_nogui)
    {
      printf ("menu \"%s\"\n", name);
      return;
    }
  if (driver->gui_driver && driver->gui_driver->menu)
    {
      driver->gui_driver->menu (uih, name);
      return;
    }
  if (ui_nmenus > NMENUS)
    return;
  ui_menus[ui_nmenus] = ui_buildmenu (name, x, y, flags);
  ui_nmenus++;
  ui_updatestarts ();
}
static void
ui_closetopmenu (void)
{
  if (!ui_nmenus)
    return;
  ui_nmenus--;
  ui_closemenu (ui_menus[ui_nmenus]);
  ui_updatestarts ();
}

void
ui_closemenus (void)
{
  while (ui_nmenus)
    ui_closetopmenu ();
}

void
ui_menu (CONST char *m)
{
  int mousex, mousey, buttons;
  driver->getmouse (&mousex, &mousey, &buttons);
  ui_openmenu (m, mousex, mousey, 0);
}
static void
ui_menupress (int number)
{
  CONST menuitem *item;
  if (number >= ui_menus[ui_nmenus - 1]->n)
    return;
  ui_menus[ui_nmenus - 1]->selected = number;
  item = ui_menus[ui_nmenus - 1]->items[number].item;
  if (item != NULL)
    {
      uih->display = 1;
      if (item->type == MENU_SUBMENU)
	{
	  int flags = 0;
	  int mousex, mousey, buttons;
	  driver->getmouse (&mousex, &mousey, &buttons);
	  if (buttons & BUTTON1)
	    flags |= MENU_PRESSED;
	  if ((ui_menus[ui_nmenus - 1]->flags & MENU_HORIZONTAL))
	    ui_openmenu (item->shortname,
			 ui_menus[ui_nmenus - 1]->items[number].x,
			 ui_menus[ui_nmenus - 1]->items[number].y +
			 ui_menus[ui_nmenus - 1]->items[number].height,
			 flags);
	  else
	    ui_openmenu (item->shortname,
			 ui_menus[ui_nmenus - 1]->items[number].x +
			 ui_menus[ui_nmenus - 1]->items[number].width,
			 ui_menus[ui_nmenus - 1]->items[number].y, flags);
	}
      else
	ui_menuactivate (item, NULL);
    }
}
int
ui_menumouse (int x, int y, int mousebuttons, int flags)
{
  if (ui_nmenus)
    {
      struct ui_menu *m = ui_menus[ui_nmenus - 1];
      int place = -1;
      int inmenu = 0;
      if (x >= m->x && y >= m->y && x <= m->x + m->width
	  && y <= m->y + m->height)
	{
	  int i;
	  for (i = 0; i < m->n; i++)
	    {
	      if (x >= m->items[i].x && y >= m->items[i].y
		  && x <= m->items[i].x + m->items[i].width
		  && y <= m->items[i].y + m->items[i].height)
		{
		  place = i;
		  break;
		}
	    }
	  inmenu = 1;
	}
      else
	{
	  if (ui_nmenus > 1)
	    {
	      struct ui_menu *m2 = ui_menus[ui_nmenus - 2];
	      int i;
	      i = m2->selected;
	      if (x >= m2->items[i].x && y >= m2->items[i].y
		  && x <= m2->items[i].x + m2->items[i].width
		  && y <= m2->items[i].y + m2->items[i].height)
		inmenu = 1;
	    }
	}
      if ((m->flags & MENU_AUTOHIDE) && !inmenu)
	{
	  ui_closetopmenu ();
	  return (ui_menumouse (x, y, mousebuttons, flags));
	}
      if (flags & MOUSE_MOVE && m->selected != place)
	m->selected = place, uih->display = 1;
      if (m->flags & MENU_PRESSED)
	{
	  if (inmenu && place >= 0 && (m->flags & MENU_HORIZONTAL)
	      && (flags & MOUSE_DRAG)
	      && m->items[place].item->type == MENU_SUBMENU)
	    {
	      ui_menupress (place);
	      return 1;
	    }
	  else if (inmenu && place >= 0 && (flags & MOUSE_DRAG)
		   && m->items[place].item->type == MENU_SUBMENU
		   && x >
		   m->items[place].x + m->items[place].width -
		   2 * SUBMENUWIDTH)
	    {
	      ui_menupress (place);
	      return 1;
	    }
	  if (flags & MOUSE_RELEASE || !(flags & MOUSE_DRAG))
	    {
	      if (tl_lookup_timer (m->timer) < 300000)
		{
		  m->flags &= ~MENU_PRESSED;
		  return 1;
		}
	      if (!inmenu || place < 0)
		{
		  ui_closetopmenu ();
		  return (ui_menumouse (x, y, mousebuttons, flags));
		}
	      ui_menupress (place);
	      return 1;
	    }
	  if (!inmenu)		/*Trace all menus back and look, if user selected some */
	    {
	      int nmenu;
	      for (nmenu = ui_nmenus - 2; nmenu > -1; nmenu--)
		{
		  struct ui_menu *m2 = ui_menus[nmenu];
		  if (x > m2->x && y > m2->y && x < m2->x + m2->width
		      && y < m2->y + m2->height)
		    {
		      ui_closetopmenu ();
		      m2->flags |= MENU_PRESSED;
		      return (ui_menumouse (x, y, mousebuttons, flags));
		    }
		}
	    }
	}
      else if (flags & MOUSE_PRESS)
	{
	  if (!inmenu || place < 0)
	    {
	      ui_closetopmenu ();
	      return (ui_menumouse (x, y, mousebuttons, flags));
	    }
	  ui_menupress (place);
	}
      return (1);
    }
  else
    {
      if (!ui_nogui &&
	  (!driver->gui_driver || !driver->gui_driver->setrootmenu) &&
	  (flags & MOUSE_MOVE) && y < xtextheight (uih->font) + 1
	  && !(mousebuttons))
	ui_openmenu (uih->menuroot, 0, 0, MENU_HORIZONTAL | MENU_AUTOHIDE);
    }
  return (0);
}

int
ui_menukey (int key)
{
  int k;
  if (!ui_nmenus)
    {
      if (key == '\n' || key == 13)
	{
	  ui_closemenus ();
	  ui_openmenu (uih->menuroot, 0, 0, MENU_HORIZONTAL);
	  return 1;
	}
      return 0;
    }
  else
    {
      struct ui_menu *menu = ui_menus[ui_nmenus - 1];
      switch (key)
	{
	case '\n':
	case 13:
	  if (menu->selected >= 0)
	    ui_menupress (menu->selected);
	  return 1;
	case 'h':
	  {
	    CONST menuitem *item = menu->items[menu->selected].item;
	    ui_closemenus ();
	    if (menu->selected >= 0)
	      {
		ui_help (item->shortname);
	      }
	    else
	      ui_help (menu->name);
	    return 1;
	  }
	case UIKEY_LEFT:
	  if (menu->flags & MENU_HORIZONTAL)
	    {
	      if (menu->selected == -1)
		menu->selected = 0;
	      else
		menu->selected--;
	      if (menu->selected < 0)
		menu->selected = menu->n - 1;
	      uih->display = 1;
	    }
	  else if (ui_nmenus == 2 && ui_menus[0]->flags & MENU_HORIZONTAL)
	    {
	      ui_closetopmenu ();
	      ui_menus[0]->selected--;
	      if (ui_menus[0]->selected < 0)
		ui_menus[0]->selected = ui_menus[0]->n - 1;
	      ui_menupress (ui_menus[0]->selected);
	    }
	  else
	    ui_closetopmenu ();
	  return 1;
	case UIKEY_RIGHT:
	  if (menu->flags & MENU_HORIZONTAL)
	    {
	      if (menu->selected == -1)
		menu->selected = 0;
	      else
		menu->selected++;
	      menu->selected %= menu->n;
	      uih->display = 1;
	    }
	  else if (menu->selected >= 0
		   && menu->items[menu->selected].item->type == MENU_SUBMENU)
	    {
	      ui_menupress (menu->selected);
	    }
	  else if (ui_nmenus == 2 && ui_menus[0]->flags & MENU_HORIZONTAL)
	    {
	      ui_closetopmenu ();
	      ui_menus[0]->selected++;
	      ui_menus[0]->selected %= ui_menus[0]->n;
	      ui_menupress (ui_menus[0]->selected);
	    }
	  return 1;
	case UIKEY_DOWN:
	  if (menu->flags & MENU_HORIZONTAL)
	    {
	      if (menu->selected >= 0)
		ui_menupress (ui_menus[0]->selected);
	    }
	  else
	    {
	      if (menu->selected == -1)
		menu->selected = 0;
	      else
		menu->selected++;
	      menu->selected %= menu->n;
	      uih->display = 1;
	    }
	  return 1;
	case UIKEY_ESC:
	  ui_closetopmenu ();
	  return 1;
	case UIKEY_UP:
	  if (menu->flags & MENU_HORIZONTAL)
	    {
	      ui_closetopmenu ();
	    }
	  else
	    {
	      if (menu->selected == -1)
		menu->selected = 0;
	      else
		menu->selected--;
	      if (menu->selected < 0)
		menu->selected = menu->n - 1;
	      uih->display = 1;
	    }
	  return 1;
	}
      if (tolower (key) >= 'a'
	  && tolower (key) - 'a' < ui_menus[ui_nmenus - 1]->n - 10)
	{
	  ui_menupress (tolower (key) - 'a' + 10);
	  return 1;
	}
      if (key >= '0' && key <= '9')
	{
	  k = key - '1';
	  if (k == -1)
	    k = 9;
	  ui_menupress (k);
	  return 1;
	}
      return 0;
    }
}
int
ui_menuwidth (void)
{
  if (ui_nmenus && (ui_menus[0]->flags & MENU_HORIZONTAL))
    return (ui_menus[0]->height);
  return (0);
}
