#include "kant build gui.h"
#include "kant build meat.h"
#include "kant build files.h"
#include "dialogs.h"
#include "error.h"
#include "util.h"
#include "main.h"
#include "menus.h"
#include "window layer.h"
#include "text twiddling.h"
#include "program globals.h"

#define kTextItem			5
#define kEditRefDialog		401
#define kNewRefDialog		402
#define kDeleteAlert		403

enum { key_LeftArrow=0x1c, key_RightArrow, key_UpArrow, key_DownArrow };

static	Boolean DoRefDialogDispatch(Boolean isEdit, Boolean isRef, Str255 oldStr, Str255 newStr);
static	pascal Boolean RefModalFilter(DialogPtr theDialog, EventRecord *theEvent, short *theItem);
static	pascal Boolean InstantModalFilter(DialogPtr theDialog, EventRecord *theEvent, short *theItem);
static	Boolean CheckDeleteDialogDispatch(Boolean isRef, Str255 theName);

void DoDeleteRef(WindowPtr theWindow)
{
	FSSpec			theFS;
	TEHandle		hTE;
	ControlHandle	vScrollBar;
	Str255			theName;
	short			selStart, selEnd;
	short			lineNum;
	OSErr			oe;
	
	if (!AnyHighlightedQQ(theWindow))
		return;
	
	theFS=GetWindowFS(theWindow);
	hTE=GetWindowTE(theWindow);
	vScrollBar=GetWindowVScrollBar(theWindow);
	GetHighlightedString(theWindow, theName);
	
	if (!CheckDeleteDialogDispatch(TRUE, theName))
		return;
	
	if ((oe=DeleteOneReference(theFS, theName))!=noErr)
	{
		HandleError(kCantDeleteReference, FALSE, FALSE);
		return;
	}
	
	lineNum=CurrentLineNumber(hTE);
	selStart=LineStart(hTE, lineNum);
	selEnd=GetNextInstantiationOffset(theWindow);
	TESetSelect(selStart, selEnd, hTE);
	TEDelete(hTE);
	AdjustForEndScroll(vScrollBar, hTE);
	HighlightLine(hTE, lineNum);
	AdjustVScrollBar(vScrollBar, hTE);
	RebuildReferencesList();
}

void DoDeleteInstantiation(WindowPtr theWindow)
{
	FSSpec			theFS;
	TEHandle		hTE;
	ControlHandle	vScrollBar;
	Str255			theName, refName;
	short			stringIndex;
	short			lineNum;
	OSErr			oe;
	
	if (!AnyHighlightedQQ(theWindow))
		return;
	
	theFS=GetWindowFS(theWindow);
	hTE=GetWindowTE(theWindow);
	vScrollBar=GetWindowVScrollBar(theWindow);
	GetHighlightedString(theWindow, theName);
	if (!CheckDeleteDialogDispatch(FALSE, theName))
		return;
	
	GetRefNameFromInstantiation(theWindow, refName, &stringIndex);
	if ((oe=DeleteOneInstantiation(theFS, refName, stringIndex))!=noErr)
	{
		HandleError(kCantDeleteInstantiation, FALSE, FALSE);
		return;
	}
	
	lineNum=CurrentLineNumber(hTE);
	TESetSelect(LineStart(hTE, lineNum), LineStart(hTE, lineNum+1), hTE);
	TEDelete(hTE);
	AdjustForEndScroll(vScrollBar, hTE);
	HighlightLine(hTE, lineNum);
	AdjustVScrollBar(vScrollBar, hTE);
}

void DoNewRef(WindowPtr theWindow)
{
	FSSpec			theFS;
	Str255			refName;
	TEHandle		hTE;
	Boolean			addWorked;
	short			lineNum;
	OSErr			oe;
	
	if (!DoRefDialogDispatch(FALSE, TRUE, "\p", refName))
		return;
	
	theFS=GetWindowFS(theWindow);
	if (ReferenceNameExistsQQ(theFS, refName))
	{
		HandleError(kDuplicateReferenceName, FALSE, FALSE);
		return;
	}
	
	if ((oe=AddOneReference(theFS, refName))!=noErr)
	{
		HandleError(kCantCreateReference, FALSE, FALSE);
		return;
	}
	
	hTE=GetWindowTE(theWindow);
	lineNum=CurrentLineNumber(hTE);
	TESetSelect((**hTE).teLength, (**hTE).teLength, hTE);
	addWorked=AddNameToTE(hTE, refName, TRUE);
	if (addWorked)
		lineNum=CurrentLineNumber(hTE);
	HighlightLine(hTE, lineNum);
	AdjustVScrollBar(GetWindowVScrollBar(theWindow), hTE);
	if (addWorked)
		RebuildReferencesList();
	else
		HandleError(kModuleTooLarge, FALSE, FALSE);
}

void DoNewInstantiation(WindowPtr theWindow)
{
	FSSpec			theFS;
	Str255			instantName, refName;
	TEHandle		hTE;
	Boolean			addWorked;
	short			dummyShort;
	short			offset;
	short			lineNum;
	OSErr			oe;
	
	if (!DoRefDialogDispatch(FALSE, FALSE, "\p", instantName))
		return;
	
	if (RefHighlightedQQ(theWindow))
		GetHighlightedString(theWindow, refName);
	else
		GetRefNameFromInstantiation(theWindow, refName, &dummyShort);
	
	theFS=GetWindowFS(theWindow);
	if ((oe=AddOneInstantiation(theFS, refName, instantName))!=noErr)
	{
		HandleError(kCantCreateInstantiation, FALSE, FALSE);
		return;
	}
	
	hTE=GetWindowTE(theWindow);
	offset=GetNextInstantiationOffset(theWindow);
	lineNum=CurrentLineNumber(hTE);
	TESetSelect(offset, offset, hTE);
	addWorked=AddNameToTE(hTE, instantName, FALSE);
	if (addWorked)
		lineNum=LineNumberFromOffset(hTE, offset);
	HighlightLine(hTE, lineNum);
	AdjustVScrollBar(GetWindowVScrollBar(theWindow), hTE);
	
	if (!addWorked)
		HandleError(kModuleTooLarge, FALSE, FALSE);
}

void DoEditRef(WindowPtr theWindow)
{
	TEHandle		hTE;
	FSSpec			theFS;
	Str255			oldStr, newStr;
	OSErr			oe;
	short			lineNum;
	Boolean			addWorked;
	
	if (!AnyHighlightedQQ(theWindow))
		return;
	
	hTE=GetWindowTE(theWindow);
	theFS=GetWindowFS(theWindow);
	GetHighlightedString(theWindow, oldStr);
	
	if (!DoRefDialogDispatch(TRUE, TRUE, oldStr, newStr))
		return;
	
	if (ReferenceNameExistsQQ(theFS, newStr))
	{
		HandleError(kDuplicateReferenceName, FALSE, FALSE);
		return;
	}
	
	if ((oe=ReplaceOneReference(theFS, oldStr, newStr))!=noErr)
	{
		HandleError(kCantReplaceReference, FALSE, FALSE);
		return;
	}
	
//	ZapDrawHook(hTE);
	lineNum=CurrentLineNumber(hTE);
	TESetSelect(LineStart(hTE, lineNum), LineStart(hTE, lineNum+1), hTE);
	TEDelete(hTE);
//	RestoreDrawHook(hTE);
	addWorked=AddNameToTE(hTE, newStr, TRUE);
	HighlightLine(hTE, lineNum);
	AdjustVScrollBar(GetWindowVScrollBar(theWindow), hTE);
	if (addWorked)
		RebuildReferencesList();
	else
		HandleError(kModuleTooLarge, FALSE, FALSE);
}

void DoEditInstantiation(WindowPtr theWindow)
{
	TEHandle		hTE;
	FSSpec			theFS;
	Str255			oldStr, newStr, refName;
	short			stringIndex;
	OSErr			oe;
	short			lineNum;
	Boolean			addWorked;
	
	if (!AnyHighlightedQQ(theWindow))
		return;
	
	hTE=GetWindowTE(theWindow);
	theFS=GetWindowFS(theWindow);
	GetHighlightedString(theWindow, oldStr);
	GetRefNameFromInstantiation(theWindow, refName, &stringIndex);
		
	if (!DoRefDialogDispatch(TRUE, FALSE, oldStr, newStr))
		return;
	
	if ((oe=ReplaceOneInstantiation(theFS, refName, stringIndex, newStr))!=noErr)
	{
		HandleError(kCantReplaceInstantiation, FALSE, FALSE);
		return;
	}
	
//	ZapDrawHook(hTE);
	lineNum=CurrentLineNumber(hTE);
	TESetSelect(LineStart(hTE, lineNum), LineStart(hTE, lineNum+1), hTE);
	TEDelete(hTE);
//	RestoreDrawHook(hTE);
	addWorked=AddNameToTE(hTE, newStr, FALSE);
	HighlightLine(hTE, lineNum);
	AdjustVScrollBar(GetWindowVScrollBar(theWindow), hTE);
	
	if (!addWorked)
		HandleError(kModuleTooLarge, FALSE, FALSE);
}

/* the rest of these are internal to kant build gui.c */

static	Boolean DoRefDialogDispatch(Boolean isEdit, Boolean isRef, Str255 oldStr, Str255 newStr)
/* returns FALSE if cancelled or unchanged */
{
	DialogPtr		theDialog;
	short			itemSelected;
	short			itemType;
	Handle			item;
	Rect			box;
	Boolean			changed;
	Str255			theStr;
	ModalFilterUPP	procFilter;
	short			id;
	UserItemUPP		userUPP=NewUserItemProc(OutlineDefaultButton);
	
	RemoveHilitePatch();
	
	procFilter=NewModalFilterProc(isRef ? RefModalFilter : InstantModalFilter);
	id=isEdit ? kEditRefDialog : kNewRefDialog;
	PositionDialog('DLOG', id);
	theDialog = GetNewDialog(id, 0L, (WindowPtr)-1L);
	GetDItem(theDialog, 3, &itemType, &item, &box);
	InsetRect(&box, -4, -4);
	SetDItem(theDialog, 3, userItem, (Handle)userUPP, &box);
	SetWTitle(theDialog, isEdit ? (isRef ? "\pEdit reference" : "\pEdit instantiation") :
								  (isRef ? "\pNew reference" : "\pNew instantiation"));
	ParamText(isRef ? "\preference" : "\pinstantiation", "\p", isRef ? "\pname" : "\ptext", "\p");
	GetDItem(theDialog, kTextItem, &itemType, &item, &box);
	SetIText(item, oldStr);
	SelIText(theDialog, kTextItem, 0, 32767);	/* highlight old name */
	ShowWindow(theDialog);
	SetPort(theDialog);
	
	itemSelected=0;
	while ((itemSelected!=1) && (itemSelected!=2))
	{
		ModalDialog(procFilter, &itemSelected);
	}
	
	changed=FALSE;
	
	if (itemSelected==1)
	{
		GetDItem(theDialog, kTextItem, &itemType, &item, &box);
		GetIText(item, theStr);
		
		if ((isRef) && (theStr[0]==0x00))
			changed=FALSE;
		else
		{
			changed=(!Mymemcompare((Ptr)oldStr, (Ptr)theStr, oldStr[0]+1));
			if (changed)
			{
				Mymemcpy(newStr, theStr, theStr[0]+1);	/* return new name */
			}
		}
	}
	
	HideWindow(theDialog);
	DisposeDialog(theDialog);
	DisposeRoutineDescriptor(procFilter);
	DisposeRoutineDescriptor(userUPP);
	
	InstallHilitePatch();
	
	return changed;
}

static	pascal Boolean RefModalFilter(DialogPtr theDialog, EventRecord *theEvent, short *theItem)
{
	unsigned char	theChar;
	
	switch (theEvent->what)	/* examine event record */
	{
		case keyDown:	/* keypress */
		case autoKey:
			theChar=theEvent->message & charCodeMask;	/* get ascii char value */
			if ((theChar==0x0d) || (theChar==0x03))		/* RETURN or ENTER */
			{
				*theItem=FakeSelect(theDialog, sfItemOpenButton);
				return TRUE;
			}
			else if ((theChar==0x1b) || (theChar=='`'))	/* escape or ` key */
			{
				*theItem=FakeSelect(theDialog, sfItemCancelButton);
				return TRUE;
			}
			else if (theEvent->modifiers & cmdKey)
			{
				switch (theChar)
				{
					case '.':
						*theItem=FakeSelect(theDialog, 2);
						return TRUE;
						break;
				}
			}
			if ((theChar>=key_LeftArrow) && (theChar<=key_DownArrow))
				return FALSE;
			if (theChar==0x08)
				return FALSE;
			if ((theChar>='a') && (theChar<='z'))
				return FALSE;
			if ((theChar>='A') && (theChar<='Z'))
				return FALSE;
			if ((theChar>='0') && (theChar<='9'))
				return FALSE;
			if ((theChar=='-') || (theChar=='_'))
				return FALSE;
			if (theChar==' ')
			{
				theEvent->message='-';
				return FALSE;
			}
			
			theEvent->what=nullEvent;
			break;
		case updateEvt:
			if ((theEvent->message)!=(unsigned long)theDialog)
			{
				DispatchEvents(*theEvent, FALSE);
				return TRUE;
			}
			break;
	}
	
	return FALSE;	/* no faking, proceed as planned */
}

static	pascal Boolean InstantModalFilter(DialogPtr theDialog, EventRecord *theEvent, short *theItem)
{
	unsigned char	theChar;
	
	switch (theEvent->what)	/* examine event record */
	{
		case keyDown:	/* keypress */
		case autoKey:
			theChar=theEvent->message & charCodeMask;	/* get ascii char value */
			if ((theChar==0x0d) || (theChar==0x03))		/* RETURN or ENTER */
			{
				*theItem=FakeSelect(theDialog, sfItemOpenButton);
				return TRUE;
			}
			else if ((theChar==0x1b) || (theChar=='`'))	/* escape or ` key */
			{
				*theItem=FakeSelect(theDialog, sfItemCancelButton);
				return TRUE;
			}
			else if (theEvent->modifiers & cmdKey)
			{
				switch (theChar)
				{
					case '.':
						*theItem=FakeSelect(theDialog, 2);
						return TRUE;
						break;
				}
			}
			break;
		case updateEvt:
			if ((theEvent->message)!=(unsigned long)theDialog)
			{
				DispatchEvents(*theEvent, FALSE);
				return TRUE;
			}
			break;
	}
	
	return FALSE;	/* no faking, proceed as planned */
}

static	Boolean CheckDeleteDialogDispatch(Boolean isRef, Str255 theName)
{
	ModalFilterUPP	procFilter = NewModalFilterProc(TwoButtonFilter);
	short			result;
	
	RemoveHilitePatch();
	
	PositionDialog('ALRT', kDeleteAlert);
	ParamText(isRef ? "\preference" : "\pinstantiation", theName, "\p", "\p");
	result=CautionAlert(kDeleteAlert, procFilter);
	DisposeRoutineDescriptor(procFilter);
	
	InstallHilitePatch();
	
	return (result==1);
}
