#include "menus.h"
#include "graphics.h"
#include "help.h"
#include "environment.h"
#include "error.h"
#include "file interface.h"
#include "resource utilities.h"
#include "print meat.h"
#include "text twiddling.h"
#include "util.h"
#include "graphics dispatch.h"
#include "window layer.h"
#include "program globals.h"
#include "kant.h"
#include "kantinuous.h"
#include "kant load-save.h"
#include "kant parser dispatch.h"
#include "kant build files.h"
#include "kant build meat.h"
#include "kant build gui.h"

static	MenuHandle		gAppleMenu=0L;
static	MenuHandle		gFileMenu=0L;
static	MenuHandle		gEditMenu=0L;
static	MenuHandle		gOptionsMenu=0L;
static	MenuHandle		gModulesMenu=0L;
static	MenuHandle		gInsertMenu=0L;
MenuHandle		gBuildMenu=0L;
static	MenuHandle		gSpeedMenu=0L;

#define NUM_MODULES		(CountMItems(gModulesMenu)-modules_first+1)

enum
{
	speedMenu = 200,
	
	appleMenu = 400, fileMenu, editMenu, optionsMenu, modulesMenu, insertMenu, buildMenu,
	
	aboutItem = 1, aboutMSGItem, otherMSGItem, helpPointerItem,
	
	newItem = 1, openItem, file_unused1, closeItem, saveItem, saveAsItem, file_unused2,
		pageSetupItem, printItem, file_unused3, quitItem,
	
	undoItem = 1, edit_unused0, cutItem, copyItem, pasteItem, clearItem, edit_unused1, selectAllItem,
	
	resolveItem = 1, alwaysResolveItem, option_unused1, speedMenuPtr, option_unused3,
		kantinuousItem,
	
	modules_default = 1, modules_other, modules_unused, modules_first,
	
	sectionItem = 1, paragraphItem, sentenceItem, mainClauseItem, introClauseItem,
		throwawayClauseItem, proofItem, throwawaySentenceItem,
	
	build_new = 1, build_open, build_unused1, build_new_ref, build_new_instant,
		build_edit_ref, build_delete_ref, build_unused2, build_show_instant,
		build_show_all_instant, build_hide_all_instant,
	
	delay0Item = 1, delay6Item, delay12Item, delay30Item, delay60Item
};

/*-----------------------------------------------------------------------------------*/
/* internal stuff for menus.c                                                        */

static	void HandleAppleMenu(short menuItem);
static	void HandleFileMenu(short menuItem);
static	void HandleEditMenu(short menuItem, Boolean alreadyPassedThrough);
static	void HandleOptionsMenu(short menuItem);
static	void HandleModulesMenu(short menuItem);
static	void HandleInsertMenu(short menuItem);
static	void HandleBuildMenu(short menuItem);
static	void HandleSpeedMenu(short menuItem);
static	void EDItem(MenuHandle theMenu, short theItem, Boolean theCondition);

Boolean InitTheMenus(void)
{
	Handle			MBARHandle;
	FSSpec			theFS;
	
	if ((MBARHandle=GetNewMBar(400))==0L)		/* sez which menus are in menu bar. */
		return FALSE;
	SetMenuBar(MBARHandle);						/* set this to be THE menu bar to use. */
	
	if ((gAppleMenu=GetMHandle(appleMenu))==0L)	/* GetNewMBar also got menu handles of */
		return FALSE;
	if ((gFileMenu=GetMHandle(fileMenu))==0L)	/* every menu it includes, so just */
		return FALSE;
	if ((gEditMenu=GetMHandle(editMenu))==0L)	/* grab these handles and assign them */
		return FALSE;
	if ((gOptionsMenu=GetMHandle(optionsMenu))==0L)
		return FALSE;
	if ((gModulesMenu=GetMHandle(modulesMenu))==0L)
		return FALSE;
	if ((gInsertMenu=GetMHandle(insertMenu))==0L)
		return FALSE;
	if ((gBuildMenu=GetMHandle(buildMenu))==0L)
		return FALSE;
	if ((gSpeedMenu=GetMenu(speedMenu))==0L)
		return FALSE;
	
	InsertMenu(gSpeedMenu, -1);
	
	AddResMenu(gAppleMenu, 'DRVR');				/* adds control panels to apple menu */
	
	if (BuildModulesList(gModulesMenu)!=noErr)
	{
		HandleError(kCantFindModulesFolder, FALSE, FALSE);
		theFS.name[0]=0x00;
		UseTheModule(&theFS, TRUE);	/* revert to built-in */
	}
	
	if (!gUseDefault)
		RebuildReferencesList();
	
	AdjustMenus();								/* dim/enable/check/mark menus/items */
	DrawMenuBar();								/* draws the actual menu bar */
	
	return TRUE;
}

void ShutDownTheMenus(void)
{
	gAppleMenu=(MenuHandle)SafeReleaseResource((Handle)gAppleMenu);
	gFileMenu=(MenuHandle)SafeReleaseResource((Handle)gFileMenu);
	gEditMenu=(MenuHandle)SafeReleaseResource((Handle)gEditMenu);
	gOptionsMenu=(MenuHandle)SafeReleaseResource((Handle)gOptionsMenu);
	gModulesMenu=(MenuHandle)SafeReleaseResource((Handle)gModulesMenu);
	gInsertMenu=(MenuHandle)SafeReleaseResource((Handle)gInsertMenu);
	gBuildMenu=(MenuHandle)SafeReleaseResource((Handle)gBuildMenu);
	gSpeedMenu=(MenuHandle)SafeReleaseResource((Handle)gSpeedMenu);
}

void AdjustMenus(void)
{
	short			kind;
	WindowPtr		front, frontDoc, mainWindow, buildWindow;
	Boolean			isRef;
	Str255			theStr;
	Boolean			gotone;
	short			i;
	unsigned char	*otherStr="\pOther...";
	
	front=FrontWindow();
	kind=front ? ((WindowPeek)front)->windowKind : 0;
	frontDoc=GetFrontDocumentWindow();
	mainWindow=GetIndWindowPtr(kMainWindow);
	buildWindow=GetIndWindowPtr(kBuildWindow);
	isRef=RefHighlightedQQ(buildWindow);
	
	CheckItem(gOptionsMenu, alwaysResolveItem, gAlwaysResolve ? TRUE : FALSE);
	CheckItem(gSpeedMenu, delay0Item, (gSpeedDelay==0));
	CheckItem(gSpeedMenu, delay6Item, (gSpeedDelay==6));
	CheckItem(gSpeedMenu, delay12Item, (gSpeedDelay==12));
	CheckItem(gSpeedMenu, delay30Item, (gSpeedDelay==30));
	CheckItem(gSpeedMenu, delay60Item, (gSpeedDelay==60));
	
	SetItem(gBuildMenu, build_edit_ref, isRef ? "\pEdit reference" : "\pEdit instantiation");
	SetItem(gBuildMenu, build_delete_ref, isRef ? "\pDelete reference" : "\pDelete instantiation");
	SetItem(gBuildMenu, build_show_instant, RefClosedQQ(buildWindow) ? "\pShow instantiations" :
		"\pHide instantiations");
	
	SetItemMark(gModulesMenu, modules_default, gUseDefault ? '×' : noMark);
	SetItemMark(gModulesMenu, modules_other, noMark);
		
	gotone=gUseDefault;
	for (i=0; i<NUM_MODULES; i++)
	{
		if (!gotone)
		{
			GetItem(gModulesMenu, i+modules_first, theStr);
			if (Mymemcompare((Ptr)theStr, (Ptr)gModuleFS.name, theStr[0]+1))
				gotone=TRUE;
			SetItemMark(gModulesMenu, i+modules_first, gotone ? '×' : noMark);
		}
		else SetItemMark(gModulesMenu, i+modules_first, noMark);
	}
	
	if (!gotone)
	{
		SetItemMark(gModulesMenu, modules_other, '×');
		Mymemcpy((Ptr)theStr, (Ptr)otherStr, otherStr[0]+1);
		AppendStr255(theStr, "\p (");
		AppendStr255(theStr, gModuleFS.name);
		AppendStr255(theStr, "\p)");
		SetItem(gModulesMenu, modules_other, theStr);
	}
	else SetItem(gModulesMenu, modules_other, "\pOther...");
	
	EDItem(gAppleMenu, 0, !gInProgress);
	EDItem(gAppleMenu, aboutItem, !gInProgress);
	EDItem(gAppleMenu, aboutMSGItem, !gInProgress);
	EDItem(gAppleMenu, otherMSGItem, !gInProgress);
	EDItem(gAppleMenu, helpPointerItem, !gInProgress);
	EDItem(gFileMenu, 0, !gInProgress);
	EDItem(gEditMenu, 0, !gInProgress);
	EDItem(gOptionsMenu, 0, !gInProgress);
	EDItem(gModulesMenu, 0, !gInProgress);
	EDItem(gInsertMenu, 0, !gInProgress);
	EDItem(gBuildMenu, 0, !gInProgress);
	
	if (gInProgress)
		return;
	
	EDItem(gFileMenu, newItem, (mainWindow==0L));
	EDItem(gFileMenu, openItem, (mainWindow==0L));
	EDItem(gFileMenu, closeItem, front!=0L);
	EDItem(gFileMenu, saveItem, (mainWindow!=0L) && (WindowHasLayer(mainWindow)) &&
		(WindowIsModifiedQQ(mainWindow)));
	EDItem(gFileMenu, saveAsItem, mainWindow!=0L);
	EDItem(gFileMenu, pageSetupItem, TRUE);
	EDItem(gFileMenu, printItem, (frontDoc!=0L) && (WindowHasLayer(frontDoc)) &&
		(GetWindowTE(frontDoc)!=0L));
	EDItem(gFileMenu, quitItem, TRUE);
	
	EDItem(gEditMenu, undoItem, kind<0);
	EDItem(gEditMenu, cutItem, (frontDoc!=0L) && (frontDoc==mainWindow) && (AnyHighlightedQQ(frontDoc)));
	EDItem(gEditMenu, copyItem, (frontDoc!=0L) && (frontDoc==mainWindow) && (AnyHighlightedQQ(frontDoc)));
	EDItem(gEditMenu, pasteItem, (frontDoc!=0L) && (frontDoc==mainWindow) && (AnyTextInScrapQQ()));
	EDItem(gEditMenu, clearItem, (frontDoc!=0L) && (frontDoc==mainWindow) && (AnyHighlightedQQ(frontDoc)));
	EDItem(gEditMenu, selectAllItem, (frontDoc!=0L) && (frontDoc==mainWindow) && (AnyTextQQ(frontDoc)));
	
	EDItem(gOptionsMenu, resolveItem, (mainWindow!=0L));
	EDItem(gOptionsMenu, speedMenuPtr, TRUE);
	EDItem(gOptionsMenu, kantinuousItem, gUseDefault);
	
	EDItem(gBuildMenu, build_new, buildWindow==0L);
	EDItem(gBuildMenu, build_open, buildWindow==0L);
	EDItem(gBuildMenu, build_new_ref, buildWindow!=0L);
	EDItem(gBuildMenu, build_new_instant, (buildWindow!=0L) && (AnyHighlightedQQ(buildWindow)));
	EDItem(gBuildMenu, build_edit_ref, (buildWindow!=0L) && (AnyHighlightedQQ(buildWindow)));
	EDItem(gBuildMenu, build_delete_ref, (buildWindow!=0L) && (AnyHighlightedQQ(buildWindow)));
	EDItem(gBuildMenu, build_show_instant, (buildWindow!=0L) && (AnyHighlightedQQ(buildWindow)) && isRef);
	EDItem(gBuildMenu, build_show_all_instant, (buildWindow!=0L) && (AnyTextQQ(buildWindow)));
	EDItem(gBuildMenu, build_hide_all_instant, (buildWindow!=0L) && (AnyTextQQ(buildWindow)));
}

void HandleMenu(long mSelect)
{
	short			menuID = HiWord(mSelect);
	short			menuItem = LoWord(mSelect);
	
	switch (menuID)
	{
		case appleMenu:
			HandleAppleMenu(menuItem);
			break;
		case fileMenu:
			HandleFileMenu(menuItem);
			break;	
		case editMenu:
			HandleEditMenu(menuItem, FALSE);
			break;
		case optionsMenu:
			HandleOptionsMenu(menuItem);
			break;
		case modulesMenu:
			HandleModulesMenu(menuItem);
			break;
		case insertMenu:
			HandleInsertMenu(menuItem);
			break;
		case buildMenu:
			HandleBuildMenu(menuItem);
			break;
		case speedMenu:
			HandleSpeedMenu(menuItem);
			break;
	}
}

void DoTheCloseThing(WindowPeek theWindow)
/* a standard close procedure, called when "close" is chosen from File menu and when
   a window is closed through its close box */
{
	short			kind;
	
	if (theWindow==0L)
		return;
	
	kind = theWindow ? theWindow->windowKind : 0;
	if (kind<0)		/* DA window or other system window */
		CloseDeskAcc(kind);
	else
	{
		if (WindowHasLayer((WindowPtr)theWindow))
			CloseTheWindow((WindowPtr)theWindow);
		else
			DisposeWindow((WindowPtr)theWindow);
		
		AdjustMenus();
	}
}

static	void HandleAppleMenu(short menuItem)
{
	GrafPtr		savePort;
	Str255		name;
	
	switch (menuItem)
	{
		case aboutItem:
			if (!IndWindowExistsQQ(kAboutWindow))
				OpenTheIndWindow(kAboutWindow);
			else
				MySelectWindow(GetIndWindowPtr(kAboutWindow));
			break;
		case aboutMSGItem:
			if (!IndWindowExistsQQ(kAboutMSGWindow))
				OpenTheIndWindow(kAboutMSGWindow);
			else
				MySelectWindow(GetIndWindowPtr(kAboutMSGWindow));
			break;
		case otherMSGItem:
			if (!IndWindowExistsQQ(kOtherMSGWindow))
				OpenTheIndWindow(kOtherMSGWindow);
			else
				MySelectWindow(GetIndWindowPtr(kOtherMSGWindow));
			break;
		case helpPointerItem:
			if (!IndWindowExistsQQ(kHelpWindow))
				OpenTheIndWindow(kHelpWindow);
			else
				MySelectWindow(GetIndWindowPtr(kHelpWindow));
			break;
		default:
			if (menuItem > helpPointerItem+1)
			{
				GetPort(&savePort);
				GetItem(gAppleMenu, menuItem, name);
				OpenDeskAcc(name);
				SetPort(savePort);
			}
			break;
	}
}

static	void HandleFileMenu(short menuItem)
{
	WindowPtr			theWindow;
	TEHandle			hTE;
	
	switch (menuItem)
	{
		case newItem:
			OpenTheIndWindow(kMainWindow);
			break;
		case openItem:
			LoadSaveDispatch(TRUE, FALSE);
			break;
		case closeItem:
			if ((theWindow=GetFrontDocumentWindow())!=0L)
				DoTheCloseThing((WindowPeek)theWindow);
			else
				DoTheCloseThing((WindowPeek)FrontWindow());
			break;
		case saveItem:
			LoadSaveDispatch(FALSE, TRUE);
			break;
		case saveAsItem:
			LoadSaveDispatch(FALSE, FALSE);
			break;
		case pageSetupItem:
			RemoveHilitePatch();
			DoThePageSetup();
			InstallHilitePatch();
			break;
		case printItem:
			theWindow=GetFrontDocumentWindow();
			if (WindowHasLayer(theWindow))
			{
				hTE=GetWindowTE(theWindow);
				if (hTE!=0L)
				{
					RemoveHilitePatch();
					PrintText(hTE);
					InstallHilitePatch();
				}
			}
			else SysBeep(7);
			break;
		case quitItem:
			gDone=ShutDownTheProgram();
			break;
	}
}

static	void HandleEditMenu(short menuItem, Boolean alreadyPassedThrough)
{
	short			index;
	WindowPtr		frontDoc;
	enum DispatchError	resultCode;
	
	if (!alreadyPassedThrough)
		index=gFrontWindowIndex;
	else
	{
		frontDoc=GetFrontDocumentWindow();
		index=(frontDoc!=0L) ? GetWindowIndex(frontDoc) : -1;
	}
	
	if (index>=0)
	{
		switch (menuItem)
		{
			case undoItem:		resultCode=UndoDispatch(index);			break;
			case cutItem:		resultCode=CutDispatch(index);			break;
			case copyItem:		resultCode=CopyDispatch(index);			break;
			case pasteItem:		resultCode=PasteDispatch(index);		break;
			case clearItem:		resultCode=ClearDispatch(index);		break;
			case selectAllItem:	resultCode=SelectAllDispatch(index);	break;
			default:			resultCode=kSuccess;					break;
		}
		
		if ((resultCode==kPassThrough) && (!alreadyPassedThrough))
			HandleEditMenu(menuItem, TRUE);
	}
	else SystemEdit(menuItem-1);
}

static	void HandleOptionsMenu(short menuItem)
{
	WindowPtr		mainWindow;
	
	switch (menuItem)
	{
		case resolveItem:
			mainWindow=GetIndWindowPtr(kMainWindow);
			
			if (GetFrontDocumentWindow()!=mainWindow)
				MySelectWindow(mainWindow);
			
			HandleError(ParserDispatch(GetIndWindowPtr(kMainWindow)), FALSE, TRUE);
			break;
		case alwaysResolveItem:
			gAlwaysResolve=!gAlwaysResolve;
			AdjustMenus();
			break;
		case kantinuousItem:
			Kantinuous();
			break;
	}
}

static	void HandleModulesMenu(short menuItem)
{
	FSSpec			theFS;
	Str255			theName;
	OSErr			oe;
	
	switch (menuItem)
	{
		case modules_default:
			if (!gUseDefault)
			{
				theFS.name[0]=0x00;
				UseTheModule(&theFS, TRUE);
			}
			break;
		case modules_other:
			if (GetSourceFile(&theFS, BUILD_TYPE))
			{
				UseTheModule(&theFS, FALSE);
			}
			break;
		case modules_unused:
			break;
		default:
			if (NUM_MODULES>0)
			{
				GetItem(gModulesMenu, menuItem, theName);
				if ((oe=GetNamedModuleFS(&theFS, theName))==noErr)
				{
					UseTheModule(&theFS, FALSE);
				}
				else
				{
					HandleError(kModuleNotFound, FALSE, FALSE);
				}
			}
			break;
	}
}

static	void HandleInsertMenu(short menuItem)
{
	WindowPtr		mainWindow;
	Str255			theName, fullName;
	
	mainWindow=GetIndWindowPtr(kMainWindow);
	if (mainWindow==0L)
	{
		OpenTheIndWindow(kMainWindow);
		mainWindow=GetIndWindowPtr(kMainWindow);
	}
	
	if (GetFrontDocumentWindow()!=mainWindow)
		MySelectWindow(mainWindow);
	
	if (gUseDefault)
	{
		switch (menuItem)
		{
			case sectionItem:
				InsertAfterEnd(mainWindow, "\p&section");
				break;
			case paragraphItem:
				InsertAfterEnd(mainWindow, "\p&paragraph");
				break;
			case sentenceItem:
				InsertAfterEnd(mainWindow, "\p&sentence");
				break;
			case mainClauseItem:
				InsertAfterEnd(mainWindow, "\p&main-clause");
				break;
			case introClauseItem:
				InsertAfterEnd(mainWindow, "\p&intro-clause");
				break;
			case throwawayClauseItem:
				InsertAfterEnd(mainWindow, "\p&throwaway-clause");
				break;
			case proofItem:
				InsertAfterEnd(mainWindow, "\p&proof");
				break;
			case throwawaySentenceItem:
				InsertAfterEnd(mainWindow, "\p&throwaway-sentence");
				break;
		}
	}
	else
	{
		GetItem(gInsertMenu, menuItem, theName);
		fullName[0]=0x01;
		fullName[1]='&';
		AppendStr255(fullName, theName);
		InsertAfterEnd(mainWindow, fullName);
	}
	
	if (gAlwaysResolve)
		HandleError(ParserDispatch(GetIndWindowPtr(kMainWindow)), FALSE, TRUE);
}

static	void HandleBuildMenu(short menuItem)
{
	FSSpec			theFS;
	WindowPtr		buildWindow;
	OSErr			oe;
	
	buildWindow=GetIndWindowPtr(kBuildWindow);
	if ((buildWindow!=0L) && (GetFrontDocumentWindow()!=buildWindow))
	{
		MySelectWindow(buildWindow);
		SetCursor(&qd.arrow);
	}
	
	switch (menuItem)
	{
		case build_new:
			oe=DoNewModule();
			if ((oe!=noErr) && (oe!=-1))	/* -1 = user cancel */
				HandleError(kCantCreateNewModule, FALSE, FALSE);
			break;
		case build_open:
			oe=OpenTheModule(&theFS, FALSE, FALSE);
			if ((oe!=noErr) && (oe!=-1))
			{
				if (oe==-2)
					HandleError(kModuleTooLarge, FALSE, FALSE);
				else
					HandleError(kCantOpenModule, FALSE, FALSE);
			}
			break;
		case build_new_ref:
			DoNewRef(buildWindow);
			break;
		case build_new_instant:
			DoNewInstantiation(buildWindow);
			break;
		case build_edit_ref:
			if (RefHighlightedQQ(buildWindow))
				DoEditRef(buildWindow);
			else
				DoEditInstantiation(buildWindow);
			break;
		case build_delete_ref:
			if (RefHighlightedQQ(buildWindow))
				DoDeleteRef(buildWindow);
			else
				DoDeleteInstantiation(buildWindow);
			break;
		case build_show_instant:
			DealWithArrowClick(buildWindow, CurrentLineNumber(GetWindowTE(buildWindow)));
			break;
		case build_show_all_instant:
			DoShowHideAll(buildWindow, TRUE);
			break;
		case build_hide_all_instant:
			DoShowHideAll(buildWindow, FALSE);
			break;
	}
}

static	void HandleSpeedMenu(short menuItem)
{
	switch (menuItem)
	{
		case delay0Item:
			gSpeedDelay=0;
			break;
		case delay6Item:
			gSpeedDelay=6;
			break;
		case delay12Item:
			gSpeedDelay=12;
			break;
		case delay30Item:
			gSpeedDelay=30;
			break;
		case delay60Item:
			gSpeedDelay=60;
			break;
	}
	
	AdjustMenus();
}

static	void EDItem(MenuHandle theMenu, short theItem, Boolean theCondition)
{
	if (theCondition)
		EnableItem(theMenu, theItem);
	else
		DisableItem(theMenu, theItem);
}

void RebuildModulesList(void)
{
	FSSpec			theFS;
	
	DeleteMenu(modulesMenu);
	gModulesMenu=(MenuHandle)SafeReleaseResource((Handle)gModulesMenu);
	gModulesMenu=GetMenu(modulesMenu);
	InsertMenu(gModulesMenu, insertMenu);
	if (BuildModulesList(gModulesMenu)!=noErr)
	{
		HandleError(kCantFindModulesFolder, FALSE, FALSE);
		theFS.name[0]=0x00;
		UseTheModule(&theFS, TRUE);	/* revert to built-in */
	}
}

void RebuildReferencesList(void)
{
	short			i;
	FSSpec			theFS;
	
	gInsertMenu=(MenuHandle)SafeReleaseResource((Handle)gInsertMenu);
	gInsertMenu=GetMenu(insertMenu);
	InsertMenu(gInsertMenu, buildMenu);
	if (!gUseDefault)
	{
		for (i=CountMItems(gInsertMenu); i>0; i--)
			DelMenuItem(gInsertMenu, i);
		if (BuildReferencesList(gInsertMenu, gModuleFS)!=noErr)
		{
			HandleError(kCantBuildReferenceList, FALSE, FALSE);
			theFS.name[0]=0x00;
			UseTheModule(&theFS, TRUE);	/* revert to built-in */
		}
	}
}
