#define ALLOW_ACCESS_TO_INTERNALS_OF_MENUS
#define ALLOW_ACCESS_TO_INTERNALS_OF_WINDOWS

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

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

#define spaceInsidePullDownFrame	3
#define menuTitleXOffset			4
#define menuFont					boldFont

#define removeCompleteMenu			true
#define dontRemoveCompleteMenu		false

#define noDrawCoord					(Coord) -1

#define isSeparator(p)				((p)->itemStr[0] == MenuSeparatorChar)

#define menuCommandStrokeChar		chrCommandStroke

#define UIColorGetIndex	UIColorGetTableEntryIndex

void AttnIndicatorAllow (Boolean allowIt)
		SYS_TRAP(sysTrapAttnIndicatorAllow);

Boolean AttnIndicatorAllowed (void)
		SYS_TRAP(sysTrapAttnIndicatorAllowed);

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

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

static Coord WbMmDrawOneItem (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();

	localFntLineHeight = FntLineHeight();
	pullDownP = WbMmGetPulldownMenu (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(UIColorGetIndex(UIMenuSelectedForeground));
		WinSetForeColor(UIColorGetIndex(UIMenuSelectedForeground));
		WinSetBackColor(UIColorGetIndex(UIMenuSelectedFill));
	}
	else {
		WinSetTextColor(UIColorGetIndex(UIMenuForeground));
		WinSetForeColor(UIColorGetIndex(UIMenuForeground));
		WinSetBackColor(UIColorGetIndex(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 = menuCommandStrokeChar;
				WinDrawChars (&menuChar, 1, menuCommandX - FntCharWidth(menuCommandStrokeChar), drawY);
				WinDrawChars ((Char *) &itemP->command, 1, menuCommandX, drawY);
				}
			drawY += localFntLineHeight;
			}
		}
	
	WinPopDrawState();

	return drawY;
}

static void WbMmDrawOneTitle (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();

	if (selected) {
		WinSetTextColor(UIColorGetIndex(UIMenuSelectedForeground));
		WinSetBackColor(UIColorGetIndex(UIMenuSelectedFill));
	}
	else {
		WinSetTextColor(UIColorGetIndex(UIMenuForeground));
		WinSetBackColor(UIColorGetIndex(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 WbMmDrawMenuPulldown (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();

	thisMenuP = WbMmGetPulldownMenu(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(UIColorGetIndex(UIMenuFrame));
	WinSetBackColor(UIColorGetIndex(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 = WbMmDrawOneItem (menuP, i, drawY, menuCommandX, i == menuP->curItem);

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

	WinPopDrawState();
}

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

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


	thisMenuP = WbMmGetPulldownMenu (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));
		WbMmDrawOneTitle (menuP, menuP->curMenu, false);
		}
	else
		// only restore draw window if it wasn't the pulldown menu
		if (drawWindow != NULL)
			WinSetDrawWindow (drawWindow);
}

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

	if (!menuP->attr.visible)
		return;

	if (menuP->curMenu != noMenuSelection)
		WbMmErasePulldownMenu (menuP, removingCompleteMenu);

	drawWindow = WinSetDrawWindow (WinGetDisplayWindow ());
	WinGetWindowFrameRect (WinGetWindowHandle(menuP->barWin), &r);
	WinRestoreBits (menuP->bitsBehind, r.topLeft.x, r.topLeft.y);
	WinDeleteWindow(WinGetWindowHandle(menuP->barWin), false);
	menuP->barWin = NULL;
	menuP->attr.visible = false;
	
	
	// Don't send another winEnter event if the savedActiveWin is already
	// set active.  This can happen if the user does a penDown outside
	// the menu windows.
	if (WinGetActiveWindow() != menuP->savedActiveWin)
		{
		WinSetDrawWindow (menuP->savedActiveWin);
		WinSetActiveWindow (menuP->savedActiveWin);
		}
	else if (menuP->savedActiveWin == 0)
		{
		// If there wasn't an active window we need to reset the draw window.
		WinSetDrawWindow (WinGetDisplayWindow ());
		}
	else
		WinSetDrawWindow(drawWindow);

	// Restore the state of the insertion point.
	InsPtEnable (menuP->attr.insPtEnabled == true);

	// Restore the state of the attention indicator.
	if (menuP->attr.attnIndicatorIsAllowed)
		AttnIndicatorAllow (true);
}

static void WbMmMenuDrawMenu (MenuBarType * menuP)
{
	RectangleType	r, frame;
	Int16 i;
	UInt16 error;

	WinSetDrawWindow (WinGetDisplayWindow());
	WinPushDrawState();

	menuP->attr.visible = true;
	FntSetFont (menuFont);
	
	// Turn off the insertion point if it's on.
	if (InsPtEnabled ())
		{
		menuP->attr.insPtEnabled = true;
		InsPtEnable (false);
		}
	else
		menuP->attr.insPtEnabled = false;

	// Turn off the attention indicator if it's on.
	if (AttnIndicatorAllowed ())
		{
		menuP->attr.attnIndicatorIsAllowed = true;
		AttnIndicatorAllow (false);
		}
	else
		menuP->attr.attnIndicatorIsAllowed = false;

	// If there isn't a window for the menu then make one and
	// save the bits behind it.
	if (menuP->barWin == NULL)
		{
		// Set the bounds of the menu bar.
		r.topLeft.x = 1;
		r.topLeft.y = 1;
		WinGetDisplayExtent(&r.extent.x, &r.extent.y);
		r.extent.x -= 3;		// leave room for the two pixel shadow
		r.extent.y = FntLineHeight () + 1;

		menuP->barWin = WinCreateWindow (&r, menuFrame, false, false, &error);
		// Save the bits behind the popup window.
		WinGetWindowFrameRect (WinGetWindowHandle(menuP->barWin), &frame);
		frame.extent.y++;					// expand bounds so there is space between menu bar and background
		menuP->bitsBehind = WinSaveBits (&frame, &error);
		}

	// Erase the window's backgroud and draw the windows frame.
	WinSetForeColor(UIColorGetIndex(UIMenuFrame));
	WinSetBackColor(UIColorGetIndex(UIMenuFill));
	
	WinEraseRectangle (&frame, 0);

	WinSetDrawWindow (WinGetWindowHandle(menuP->barWin));
	WinDrawWindowFrame ();


	// Draw all the menu titles
	for (i=0; i < menuP->numMenus; i++)
		WbMmDrawOneTitle (menuP, i, i == menuP->curMenu);

	WinEnableWindow (WinGetWindowHandle(menuP->barWin));
	menuP->savedActiveWin = WinGetActiveWindow ();
	WinSetActiveWindow (WinGetWindowHandle(menuP->barWin));

	WbMmDrawMenuPulldown (menuP);

	WinPopDrawState();
}

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

static void WbMmSaveBitsBehind (void)
{
	UInt32 value;
	FtrGet (appFileCreator, ftr_WbMmMenuP, &value);
	WbMmMenuType *WbMmMenuP = (WbMmMenuType*)value;

	WinPushDrawState ();
	WinHandle PrevDrawWinH = WinSetDrawWindow (WinGetDisplayWindow ());

	FntSetFont (menuFont);
	UInt16 MenuBarHeight = FntLineHeight () + 4;

	Coord DisplayExtentX;
	Coord DisplayExtentY;
	WinGetDisplayExtent (&DisplayExtentX, &DisplayExtentY);

	UInt16 error;
	RectangleType source;
	source.topLeft.x = 0;
	source.topLeft.y = 0;
	source.extent.x = DisplayExtentX;
	source.extent.y = DisplayExtentY;
	WbMmMenuP->EntireBitsBehind = WinSaveBits (&source, &error);

	source.topLeft.x = 0;
	source.topLeft.y = 0;
	source.extent.x = DisplayExtentX;
	source.extent.y = MenuBarHeight;
	WinHandle MenuBarAreaBitsBehind = WinSaveBits (&source, &error);

	WinEraseRectangle (&source, 0);

	source.topLeft.x = 1;
	source.topLeft.y = 1;
	source.extent.x = DisplayExtentX - 3;
	source.extent.y = MenuBarHeight - 3;
	WinDrawRectangleFrame (menuFrame, &source);

	source.topLeft.x = 0;
	source.topLeft.y = MenuBarHeight - 1;
	source.extent.x = DisplayExtentX;
	source.extent.y = DisplayExtentY - MenuBarHeight + 1;
	WbMmMenuP->PullDownAreaBitsBehind = WinSaveBits (&source, &error);

	source.topLeft.x = 0;
	source.topLeft.y = 0;
	WinRestoreBits (MenuBarAreaBitsBehind, source.topLeft.x, source.topLeft.y);
//	WinDeleteWindow (MenuBarAreaBitsBehind, true);

	WinSetDrawWindow (PrevDrawWinH);
	WinPopDrawState ();

//	UInt16 error;
	EventType event;
	event.eType = keyDownEvent;
	event.data.keyDown.chr = vchrMenu;
	event.data.keyDown.keyCode = 0;
	event.data.keyDown.modifiers = 0;
	MenuHandleEvent (0, &event, &error);

	WinPushDrawState ();
	/*WinHandle*/ PrevDrawWinH = WinSetDrawWindow (WinGetDisplayWindow ());
	source.topLeft.x = 0;
	source.topLeft.y = MenuBarHeight - 1;
	WinDrawBitmap (WbMmMenuP->PullDownAreaBitsBehind->bitmapP, source.topLeft.x, source.topLeft.y);

	WinSetDrawWindow (PrevDrawWinH);
	WinPopDrawState ();
}

static void WbMmEraseMenu (void)
{// delete menu
	UInt16 error;
	EventType event;
	event.eType = keyDownEvent;
	event.data.keyDown.chr = vchrMenu;
	event.data.keyDown.keyCode = 0;
	event.data.keyDown.modifiers = 0;
	MenuHandleEvent (0, &event, &error);

	UInt32 value;
	FtrGet (appFileCreator, ftr_WbMmMenuP, &value);
	WbMmMenuType *WbMmMenuP = (WbMmMenuType*)value;
	WinDeleteWindow (WbMmMenuP->EntireBitsBehind, true);
	WinDeleteWindow (WbMmMenuP->PullDownAreaBitsBehind, true);
}

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

Err WbMmMenuHandleEvent (SysNotifyParamType *notifyParamsP)
{
	EventPtr DequedEvent;
	DequedEvent = (EventType*)notifyParamsP->notifyDetailsP;

	UInt32 value;
	FtrGet (appFileCreator, ftr_WbMmMenuP, &value);
	WbMmMenuType *WbMmMenuP = (WbMmMenuType*)value;

	MenuBarType *menuP;
	menuP = MenuGetActiveMenu();

	if (!menuP)
	{// erase menu
		WbMmMenuP->curMenu = 0;
		WbMmMenuP->curItem = -1;
		WbMmMenuP->EntireBitsBehind = NULL;
		WbMmMenuP->PullDownAreaBitsBehind = NULL;
	}

	if (Swap16 (DequedEvent->eType) == penDownEvent)
	{
	}

	if (Swap16 (DequedEvent->eType) == keyDownEvent)		//keyDownEvent 0x0004
	{
		if ((Swap16(DequedEvent->data.keyDown.chr) == commandChr) ||
				(Swap16(DequedEvent->data.keyDown.chr) == menuChr))
		{
			MenuEraseStatus (0); // if ToolBar is Up, Erase this.

			if (Swap16(DequedEvent->data.keyDown.chr) == menuChr)
			{
				menuP = MenuGetActiveMenu();

				if (!menuP)
				{
					// menuP was not Loded.
					// do noting.
					return errNone;
				}
				else
				{
					if (menuP->attr.visible == false)
					{	// draw menu
						// save bits not MenuBar area.
						WbMmSaveBitsBehind ();

						DequedEvent->eType = (eventsEnum)nilEvent;
					}
					else
					{// erase menu
						WbMmEraseMenu ();

						DequedEvent->eType = (eventsEnum)nilEvent;
					}
				}
			}
			else if (Swap16(DequedEvent->data.keyDown.chr) == commandChr)
			{
				menuP = MenuGetActiveMenu();
				if (menuP && menuP->attr.visible)
				{// erase menu
					WbMmEraseMenu ();
				}
			}
		}
	}

	return errNone;
}