#define ALLOW_ACCESS_TO_INTERNALS_OF_MENUS

#include <PalmOS.h>
#include "PalmChars.h"
#include "T-Suite.h"

#define noDrawCoord	(Coord)-1
#define isSeparator(p) ((p)->itemStr[0] == MenuSeparatorChar)
#define spaceInsidePullDownFrame 3
#define	UICState ((UIGlobalsPtr)GUIGlobalsP)->uicState
#define UIColorGetIndex(which)	(UICState.colorTableP[which].index)
#define menuTitleXOffset 4
#define menuFont boldFont

#define removeCompleteMenu 		true
#define dontRemoveCompleteMenu	false

void MenuEraseMenu (MenuBarType * menuP, Boolean removingCompleteMenu)
							SYS_TRAP(sysTrapMenuEraseMenu);

#pragma mark ----------

static MenuPullDownType * mnuGetPulldownMenu (MenuBarType * menuP)
{
	if (menuP->curMenu == noMenuSelection)
		{
		ErrNonFatalDisplay("No menu pulled down");
		return 0;
		}
	return &menuP->menus[menuP->curMenu];
}

static Coord mnuDrawOneItem (MenuBarType * menuP, Int16 which,
								  Coord drawY, Coord menuCommandX, Boolean selected)
{
	MenuPullDownType *	pullDownP;
	Coord				localFntLineHeight;
	MenuItemType 		*itemP;
	Char *				itemText;
	RectangleType		r;
	
	// bail out if we were passed noMenuSelection or separatorItemSelection
	if (which <= noMenuSelection)
		return 0;

	WinPushDrawState();

	FntSetFont (menuFont);

	localFntLineHeight = FntLineHeight();
	pullDownP = mnuGetPulldownMenu(menuP);
	itemP = pullDownP->items;
	
	// if we weren't passed in a Y offset, compute it
	if (drawY ==noDrawCoord) {
		Int16 i;
		drawY = 0;
		for (i=0; i < which; i++)
			if (!itemP[i].hidden)
				drawY += isSeparator(&itemP[i]) ? (localFntLineHeight/2) : localFntLineHeight;
	}
	
	// Find the widest menu command Char.  The command keys will be drawn
	// so that the widest Char is separated from the right edge of the 
	// menu by spaceInsidePullDownFrame.  If no command keys are assigned
	// then RezConvert will have set up the right bounds closer in and this
	// code will not find any command keys to draw.
	if (menuCommandX == 0) 
		{
	   Int16	charWidth;
		Coord menuCommandWidth = 0;
  		Int16 i;
		for (i=0; i < pullDownP->numItems; i++)
			{
			if (itemP[i].hidden) continue;
			if (itemP[i].command != 0 && itemP[i].itemStr[0] != MenuSeparatorChar) {
				charWidth = FntCharWidth(itemP[i].command);
				if (charWidth > menuCommandWidth)
					menuCommandWidth = charWidth;
			}
		}
		menuCommandX = pullDownP->bounds.extent.x - (spaceInsidePullDownFrame - 1 ) - 
			menuCommandWidth;
	}
	
	if (selected) {
		WinSetTextColor(UIColorGetTableEntryIndex(UIMenuSelectedForeground));
		WinSetForeColor(UIColorGetTableEntryIndex(UIMenuSelectedForeground));
		WinSetBackColor(UIColorGetTableEntryIndex(UIMenuSelectedFill));
	}
	else {
		WinSetTextColor(UIColorGetTableEntryIndex(UIMenuForeground));
		WinSetForeColor(UIColorGetTableEntryIndex(UIMenuForeground));
		WinSetBackColor(UIColorGetTableEntryIndex(UIMenuFill));
	}

	r.topLeft.x = 0;
	r.extent.x = pullDownP->bounds.extent.x;
	r.topLeft.y = drawY;
	r.extent.y = isSeparator(itemP) ? (localFntLineHeight/2) : localFntLineHeight;
	WinEraseRectangle (&r, 0);
	
	// Draw the item text if there is any.
	itemP += which;
	itemText = itemP->itemStr;
	ErrNonFatalDisplayIf(itemText == NULL, "NULL menu item text.");

	if (itemText)
		{
		if (itemText[0] == MenuSeparatorChar)
			{
			// Draw a dotted line for a separator.
			WinDrawGrayLine (0, drawY + localFntLineHeight / 4,
				pullDownP->bounds.extent.x, drawY + localFntLineHeight / 4);
			drawY += localFntLineHeight / 2;		// separators occupy half a line
			}
		else
			{
			// Draw the menu item text
			WinDrawChars (itemText, StrLen(itemText), spaceInsidePullDownFrame, drawY);
			
			// If there is a command display the key for it
			if (itemP->command != 0)
				{
				Char menuChar = chrCommandStroke;
				WinDrawChars (&menuChar, 1, menuCommandX - FntCharWidth(chrCommandStroke), drawY);
				WinDrawChars ((Char *) &itemP->command, 1, menuCommandX, drawY);
				}
			drawY += localFntLineHeight;
			}
		}
	
	WinPopDrawState();

	return drawY;
}

static void mnuDrawOneTitle (MenuBarType * menuP, Int16 which, Boolean selected)
{
	Char * titleText;

	if (menuP->menus[which].hidden)	return;

	ErrNonFatalDisplayIf(which == noMenuSelection, 
		"Somehow drawing when there isn't a menu to invert.");
	
	WinPushDrawState();

	FntSetFont (menuFont);

	if (selected) {
		WinSetTextColor(UIColorGetTableEntryIndex(UIMenuSelectedForeground));
		WinSetBackColor(UIColorGetTableEntryIndex(UIMenuSelectedFill));
	}
	else {
		WinSetTextColor(UIColorGetTableEntryIndex(UIMenuForeground));
		WinSetBackColor(UIColorGetTableEntryIndex(UIMenuFill));
	}

	WinEraseRectangle (&(menuP->menus[which].titleBounds), 0);

	titleText = menuP->menus[which].title;
	WinDrawChars (titleText, StrLen(titleText),
		menuP->menus[which].titleBounds.topLeft.x + menuTitleXOffset,
		menuP->menus[which].titleBounds.topLeft.y);

	WinPopDrawState();
}

static void mnuDrawMenuPulldown (MenuBarType * menuP)
{
	RectangleType frame;
	Int16 i;
	MenuItemType * items;
	UInt16 error;
	MenuPullDownType * thisMenuP;
	Coord	drawY = 0;
	Coord menuCommandX;
	Coord menuCommandWidth = 0;
	Int16 charWidth;
	
	if (!menuP->attr.visible ||
		 menuP->curMenu == noMenuSelection ||
		 menuP->menus == NULL)
		return;


	WinPushDrawState();

	FntSetFont (menuFont);

	thisMenuP = mnuGetPulldownMenu(menuP);

	// If there isn't a window for the menu then make one and
	// save the bits behind it.
	if (thisMenuP->menuWin == NULL)
		{
		thisMenuP->menuWin = WinCreateWindow (&thisMenuP->bounds,
			menuFrame, false, false, &error);
		// Save the bits behind the popup window.
		WinSetDrawWindow (WinGetDisplayWindow());
		WinGetWindowFrameRect (WinGetWindowHandle(thisMenuP->menuWin), &frame);
		RctInsetRectangle (&frame, -1);				// expand bounds so there is space between menu and background
		thisMenuP->bitsBehind = WinSaveBits (&frame, &error);
		}


	// Erase the window's backgroud and draw the windows frame.
	WinSetForeColor(UIColorGetTableEntryIndex(UIMenuFrame));
	WinSetBackColor(UIColorGetTableEntryIndex(UIMenuFill));
	WinEraseLine (frame.topLeft.x + 2, frame.topLeft.y + 2,
		frame.topLeft.x + frame.extent.x - 4, frame.topLeft.y + 2);
	frame.topLeft.y += 3;
	frame.extent.y -= 3;
	WinEraseRectangle (&frame, 0);
	WinSetDrawWindow (WinGetWindowHandle(thisMenuP->menuWin));
	WinDrawWindowFrame();



	// Draw all the menu items
	items = thisMenuP->items;
	for (i=0; i < thisMenuP->numItems; i++)
		if (items[i].command != 0 &&
			items[i].itemStr[0] != MenuSeparatorChar && !items[i].hidden)
			{
			charWidth = FntCharWidth(items[i].command);
			if (charWidth > menuCommandWidth)
				menuCommandWidth = charWidth;

			// FntCharWidth counts the last column which is blank.  Subtract it.
			// DOLATER kwk - better here would be a call to get a character's glyph width.
			if (charWidth > 0)
				charWidth--;
			}
	
	// Draw all the menu items
	menuCommandX = thisMenuP->bounds.extent.x - (spaceInsidePullDownFrame - 1 ) - 
		menuCommandWidth;

	for (i=0; i < thisMenuP->numItems; i++)
		if (!items[i].hidden)
			drawY = mnuDrawOneItem(menuP, i, drawY, menuCommandX, i == menuP->curItem);

	WinEnableWindow (WinGetWindowHandle(thisMenuP->menuWin));
	WinSetActiveWindow (WinGetWindowHandle(thisMenuP->menuWin));

	WinPopDrawState();

}

static void mnuErasePulldownMenu (MenuBarType * menuP, Boolean removingCompleteMenu)
{
	WinHandle drawWindow; 
	RectangleType r;
	MenuPullDownType * thisMenuP;

	if (!menuP->attr.visible ||
		 menuP->curMenu == noMenuSelection ||
		 menuP->menus == NULL)
		{
		return;
		}


	thisMenuP = mnuGetPulldownMenu(menuP);


	drawWindow = WinSetDrawWindow (WinGetDisplayWindow ());
	WinGetWindowFrameRect (WinGetWindowHandle(thisMenuP->menuWin), &r);
	RctInsetRectangle (&r, -1);				// expand bounds so there is space between menu and background
	WinRestoreBits (thisMenuP->bitsBehind, r.topLeft.x, r.topLeft.y);
	
	// check if old draw window was pulldown menu which we're about to delete
	if (drawWindow == thisMenuP->menuWin)
		drawWindow = NULL;
	WinDeleteWindow(WinGetWindowHandle(thisMenuP->menuWin), false);
	thisMenuP->menuWin = NULL;
	
	// When removing the complete menu bar it is desirable to 
	// not call WinSetActiveWindow so that multiple winEnter and winExit
	// events are not posted to the event queue.
	if (!removingCompleteMenu)
		{
		WinSetDrawWindow (WinGetWindowHandle(menuP->barWin));
		WinSetActiveWindow (WinGetWindowHandle(menuP->barWin));
		mnuDrawOneTitle (menuP, menuP->curMenu, false);
		}
	else
		// only restore draw window if it wasn't the pulldown menu
		if (drawWindow != NULL)
			WinSetDrawWindow (drawWindow);
}

#pragma mark ----------

Err WbMmMenuKeyDownHandle (SysNotifyParamType *notifyParamsP)
{
	EventPtr DequedEvent;
	DequedEvent = (EventType*)notifyParamsP->notifyDetailsP;
	if (Swap16 (DequedEvent->eType) == penDownEvent)		//keyDownEvent 0x0004
	{
		return errNone;
	}

	if (Swap16 (DequedEvent->eType) == keyDownEvent)		//keyDownEvent 0x0004
	{
		WinHandle curWin = WinGetActiveWindow ();
		MenuBarType *menuP;
		menuP = (MenuBarType*) *(UInt32*)0x0216;
		Int16 target;
		Boolean found = false;
		Boolean dontRemovePulldown = false;
		if ( menuP != NULL && menuP->attr.visible)
		{
			EventType newEvent;
			MemSet (&newEvent, sizeof(EventType), 0);

			switch (Swap16(DequedEvent->data.keyDown.chr))
			{
//			case vchrPageUp:
			case chrUpArrow:	//chrUpArrow 0x001E
up:
				notifyParamsP->handled = true;
				DequedEvent->eType = nilEvent;
				target = menuP->curItem;

				if (menuP->curMenu < 0)
					goto noPullDown;

				while ((Int16)target > (Int16)-1)
				{
					target--;
					if (menuP->menus[menuP->curMenu].items[target].hidden == false)
					{
						if(menuP->menus[menuP->curMenu].items[target].itemStr[0] != MenuSeparatorChar)
						{
							found = true;
							break;
						}
					}
				}

				if (!found)
					goto right;

				goto itemFound;

				break;

//			case vchrPageDown:
			case chrDownArrow:	//chrDownArrow 0x001F
down:
				notifyParamsP->handled = true;
				DequedEvent->eType = nilEvent;
				target = menuP->curItem;

				if (menuP->curMenu < 0)
					goto noPullDown;

				while ((Int16)target < (Int16)menuP->menus[menuP->curMenu].numItems - 1)
				{
					target++;
					if (menuP->menus[menuP->curMenu].items[target].hidden == false)
					{
						if(menuP->menus[menuP->curMenu].items[target].itemStr[0] != MenuSeparatorChar)
						{
							found = true;
							break;
						}
					}
				}

				goto itemFound;

				break;

noPullDown:
					found = true;
					menuP->curMenu = -1;
					target = 0;
					dontRemovePulldown = true;
					mnuDrawMenuPulldown (menuP);
					goto titleFound;

itemFound:
				if (found)
				{
					WinSetDrawWindow (menuP->menus[menuP->curMenu].menuWin);
					WinSetActiveWindow (menuP->menus[menuP->curMenu].menuWin);
					mnuDrawOneItem (menuP, menuP->curItem, noDrawCoord, 0, false);
					mnuDrawOneItem (menuP, target, noDrawCoord, 0, true);
					menuP->curItem = target;
				}
				break;

//			case vchrHard2:
			case chrLeftArrow:	//chrLeftArrow 0x001C
left:
				notifyParamsP->handled = true;
				DequedEvent->eType = nilEvent;
				target = menuP->curMenu;
				if (target < 0)
				{
					found = true;
					target = 0;
					goto titleFound;
				}
				do
				{
					target--;
					if ((Int16)target == (Int16)-1)
						target = menuP->numMenus - 1;
					if (menuP->menus[target].hidden == false)
					{
						found = true;
						break;
					}
				}while ((Int16)menuP->curMenu != target);

				goto titleFound;
				break;

//			case vchrHard3:
			case chrRightArrow:	//chrRightArrow 0x001D
right:
				notifyParamsP->handled = true;
				DequedEvent->eType = nilEvent;
				target = menuP->curMenu;
				do
				{
					target++;
					if ((Int16)target == (Int16)menuP->numMenus)
						target = 0;
					if (menuP->menus[target].hidden == false)
					{
						found = true;
						break;
					}
				}while ((Int16)menuP->curMenu != target);

				goto titleFound;
				break;

titleFound:
				if (found)
				{
					if (dontRemovePulldown != true)
						mnuErasePulldownMenu (menuP, false);
					mnuDrawOneTitle (menuP, menuP->curMenu, false);
					mnuDrawOneTitle (menuP, target, true);
					menuP->curMenu = target;
					menuP->curItem = -1;
//					if (dontRemovePulldown != true)
//						menuP->curItem = 0;
					mnuDrawMenuPulldown (menuP);
				}
				break;

//			case vchrHard1:
//			case vchrHard4:
			case chrLineFeed:	//chrLineFeed 0x000A
				notifyParamsP->handled = true;
				DequedEvent->eType = nilEvent;
				if ((menuP->curMenu != -1) && (menuP->curItem != -1))
				{
					newEvent.eType = menuEvent;
					newEvent.data.menu.itemID =
						menuP->menus[menuP->curMenu].items[menuP->curItem].id;
				}
				MenuEraseMenu (menuP, removeCompleteMenu);
				MenuEraseStatus (0);
				EvtAddEventToQueue (&newEvent);
				break;

			case chrBackspace:	//chrBackspace 0x0008
				newEvent.eType = keyDownEvent;
				newEvent.data.keyDown.chr = vchrMenu;
				newEvent.data.keyDown.keyCode = 0;
				newEvent.data.keyDown.modifiers = commandKeyMask;
				EvtAddEventToQueue (&newEvent);
				notifyParamsP->handled = true;
				DequedEvent->eType = nilEvent;
				break;
			}
		}
	}

	return errNone;
}