/* 

	By Don Gaspar
	
	MOOSE, Inc. (Macintosh Object-Oriented Software Engineering)
	10866 Northridge Square
	Cupertino, CA 95014
	(408)252-7576
	
	Tuesday, April 10, 1990				Added MultiFinder support.
															by Don T. Gaspar
	
	Wednesday, April 11, 1990		Added scrollbars and DoGrowWindow
															by Don T. Gaspar
	
	Wednesday, April 25, 1990		Started adding the buffer, and invoking the basics
															to add saving selections/sessions. This is not
															completed yet.
															by Don T. Gaspar
												
*/


	/* These are the standard Mac includes for all managers */
 
#include <values.h>
#include <types.h>
#include <Resources.h>
#include <QuickDraw.h>
#include <fonts.h>
#include <events.h>
#include <windows.h>
#include <menus.h>
#include <textedit.h>
#include <dialogs.h>
#include <Controls.h>
#include <desk.h>
#include <toolutils.h>
#include <memory.h>
#include <Lists.h>
#include <SegLoad.h>
#include <Files.h>
#include <Packages.h>
#include <OSEvents.h>
#include <OSUtils.h>
#include <DiskInit.h>
#include <Traps.h>
#include <String.h>
#include <Strings.h>

#include <CRMIntf.h>														// Communications Resource Manager stuff
#include <CMIntf.h>														// Connection Manager stuff
#include <FTIntf.h>															// File Transfer Manager stuff
#include <TMIntf.h>														// Terminal Manager stuff
#include <CTBUtils.h>													// Communications Toolbox Utility stuff

#include <Errors.h>
#include <Limits.h>
#include <Traps.h>

#include "CommTypes.h"												// our communications types, etc.
#include "CheapComm.h"													// our constants, forward declarations, etc.

/* 	Here's our global variables, dude!	*/
Boolean				gHasWaitNextEvent;							// does user machine have WaitNextEvent????
Boolean				gInBackground;									// are we in the background????
Boolean				gStopped;												// are we stopped????
	
TermHandle		gTerm;													// handle to terminal record
ConnHandle		gConn;													// handle to connection record
FTHandle			gFT;														// handle to file transfer record

Ptr						gBuffer;													// global connection buffer

long					gFTSearchRefNum;
Boolean				gStartFT;												// are we doing a file transfer????
Boolean				gWasFT;												// was a file transfer in progress????

short					gDummy;

Handle				gCache;													// buffer for the last terminal line recdeived/sent
TEHandle				gTE;														// buffer for terminal emulator

ControlHandle	gScrollHHandle, gScrollVHandle;	// our scroll bars in the terminal window

#pragma segment Initialize
/* Checks to see if a given trap is implemented */
Boolean	TrapAvailable(tNumber,tType)
	short			tNumber;
	TrapType	tType;
{
	short	unImplemented;
	
	if (tType == OSTrap)
		unImplemented = _UnimplementedOSTrap;
	else
		unImplemented = _UnimplementedToolTrap;
		
	return(NGetTrapAddress(tNumber,tType) != GetTrapAddress(unImplemented));
}

#pragma segment Main
/*	Sends the data out via the choosen connection */
pascal long	TermSendProc(thePtr, theSize, refCon, flags)
	Ptr			thePtr;
	long		theSize;
	long		refCon;
	short		flags;
{
	CMErr	theErr;
	long		termSendProc = 0L;
	
	if (gConn != nil) {
		theErr = CMWrite(gConn,thePtr,&theSize,cmData,false,nil,0,flags);
		if (theErr == noErr)
			termSendProc = theSize;
	}
	return(termSendProc);
}

#pragma segment Main
/*  Gets the data from the connection tool and sends it to the terminal tool */
pascal void TermRecvProc()
{
	CMErr				theErr;
	CMStatFlags	status;
	CMBufferSizes	sizes;
	short					flags;
	
	if (gConn != nil && gTerm != nil) {
		theErr = CMStatus(gConn, sizes, &status);
		
		if (theErr == noErr) {
			if ((status & (cmStatusOpen+cmStatusDataAvail)) != 0 && sizes[cmDataIn] != 0) {
				if (sizes[cmDataIn] > kBufferSize)
					sizes[cmDataIn] = kBufferSize;
				
				theErr = CMRead(gConn, gBuffer, &sizes[cmDataIn], cmData, false, nil, 0, &flags);
				
				if (theErr  == noErr)	// give it to the terinal emulation buffer
					sizes[cmDataIn] = TMStream(gTerm, gBuffer, sizes[cmDataIn], flags);
			}
		}
		else
			;					// Connection Manager will handle this for us
	}
}

#pragma segment Main
/* Gets the connection environments for the FT or Term tool */
pascal OSErr	ToolGetConnEnvirons(refCon, theEnvirons)
	long						refCon;
	ConnEnvironRec	*theEnvirons;
{
	OSErr		toolGetConnEnvirons = envNotPresent;
	
	if(gConn != nil)
		toolGetConnEnvirons = CMGetConnEnvirons(gConn,theEnvirons);
		
	return(toolGetConnEnvirons);
}

#pragma segment Main
/* Sends the data during a file transfer */
pascal long	FTSendProc(thePtr, theSize, refCon, channel, flags)
	Ptr					thePtr;
	long				theSize;
	long				refCon;
	CMChannel	channel;
	short				flags;
{
	CMErr	theErr;
	long		ftSendProc = 0L;
	
	if (gConn != nil) {
		theErr = CMWrite(gConn, thePtr,& theSize, channel, false, nil, 0, flags);
		
		if(theErr == noErr)
			ftSendProc = theSize;
	}
	return(ftSendProc);
}

#pragma segment Main
/* Gets the data during a data transfer */
pascal long	FTReceiveProc(thePtr, theSize, refCon, channel, flags)
	Ptr					thePtr;
	long				theSize;
	long				refCon;
	CMChannel	channel;
	short				*flags;
{
	CMErr		theErr;
	long			ftReceiveProc = 0L;
	
	if (gConn != nil) {
		theErr = CMRead(gConn, thePtr, &theSize, channel, false, nil, 0, flags);
		if (theErr == noErr)
			ftReceiveProc = theSize;
	}
	return(ftReceiveProc);
}

#pragma segment Main
/* Sets the file transfer flag if an autoreceive string was found */
pascal void AutoRecCallBack(theConn, data, refNum)
	ConnHandle		theConn;
	Ptr						data;
	long					refNum;
{
	if (gFTSearchRefNum == refNum)
		gStartFT = true;
}

#pragma segment Main
/* Checks to see if the file transfer has an autoreceive string, and then adds a
	search to find it																												*/
void AddFTSearch()
{
	Str255	tempStr;
	
	if (gFT != nil && gConn != nil) {
		//tempStr = (*gFT)->autoRec;
		
		if ((*gFT)->autoRec[0] != 0) {
			gFTSearchRefNum = CMAddSearch(gConn,(*gFT)->autoRec, cmSearchSevenBit,
				(ProcPtr)AutoRecCallBack);
			if (gFTSearchRefNum == -1) {
				AlertUser("Couldn't add stream search\0", false);
				gFTSearchRefNum = 0;
			}
		}
	}
}

#pragma segment Main
/* Initiates a file transfer send from the menu command */
void DoSend()
{
	SFReply		theReply;
	Point				where;
	short				numTypes;
	SFTypeList	typeList;
	FTErr				anyErr;
	
	if(gFT != nil) {
		SetPt(&where,100,100);
		
		if(((**gFT).attributes & ftTextOnly) != 0) {
			typeList[0] = 'TEXT';
			numTypes = 1;
		}
		else
			numTypes = -1;
		
		sfgetfile(&where, "File to send", nil, numTypes, typeList, nil, &theReply);
		
		if(theReply.good) {
			anyErr = FTStart(gFT, ftTransmitting, &theReply);
			if(anyErr != noErr)
				;									// file transfer tool will alert user
		}
	}
}

#pragma segment Main
/* Initiates a file transfer receive from the menu */
void DoReceive()
{
	SFReply		theReply;
	OSErr				anyErr;
	
	if (gFT != nil) {
		theReply.vRefNum = 0;
		//theReply.fName = '';
		gStartFT = false;
		
		if (gConn != nil ) {
			if ((**gFT).autoRec != "\0" && gFTSearchRefNum != 0) {
				CMRemoveSearch(gConn, gFTSearchRefNum);
				gFTSearchRefNum = 0;
			}
		}
		
		anyErr = FTStart(gFT, ftReceiving, &theReply);
		
		if(anyErr != noErr)
			;									// file transfer tool will alert the user
	}
}

#pragma segment Main
/* checks to see if a window belongs to a DA */
Boolean	IsDAWindow(window)
	WindowPtr	window;
{
	if (window == nil)
		return(false);
	else 
		return (((WindowPeek) window)->windowKind < 0);
}

#pragma segment Main
/* Adjust the scroll bars if the window need it */
void AdjustScrollBars(OldRect,whichWindow)/* Resized this window */
	Rect	*OldRect;   				/* Rect area */ 
	WindowPtr	whichWindow;   		/* Window that was resized */ 
{
	WindowPtr	SavePort;  			/* Place to save the last port */
	Rect	temp2Rect,tempRect;  				/* temp rectangle */
	short	Index; 					/* temp integer */

	GetPort(&SavePort); 			/* Save the current port */
	SetPort(whichWindow);  			/* Set the port to my window */
	
	if (gScrollHHandle != nil)   	/* Only do if the control is valid */
	{
		HLock((Handle)gScrollHHandle);/* Lock the handle while we use it */
		tempRect.left = (*gScrollHHandle)->contrlRect.left;/* the area to update */
		tempRect.top = (*gScrollHHandle)->contrlRect.top - 4;/* Widen the area to update */
		tempRect.right = (*gScrollHHandle)->contrlRect.right + 16;/* Widen the area to update */
		tempRect.bottom = (*gScrollHHandle)->contrlRect.bottom;/* the area to update */
		InvalRect(&tempRect);   	/* Flag old position for update routine */
		tempRect.top = (*gScrollHHandle)->contrlRect.top ;/*  */
		tempRect.right = (*gScrollHHandle)->contrlRect.right ;/*  */
		temp2Rect.left = whichWindow->portRect.left;/* Get window rectangle */
		temp2Rect.top =  whichWindow->portRect.top ;/*  */
		temp2Rect.right =  whichWindow->portRect.right ;/*  */
		temp2Rect.bottom =  whichWindow->portRect.bottom;/*  */
		Index = temp2Rect.right - temp2Rect.left - 13;/* Get the scroll area width */
		tempRect.left = -1; 		/* Pin at left edge */
		HideControl(gScrollHHandle);/* Hide it during size and move */
		SizeControl(gScrollHHandle, Index,16);/* Make it 16 pixels high, std width */
		MoveControl(gScrollHHandle, tempRect.left,temp2Rect.bottom - temp2Rect.top-15);/* Size it correctly */
		ShowControl(gScrollHHandle);/* Safe to show it now */
		HUnlock((Handle)gScrollHHandle);/* Let it float again */
	}   							/* End for scroll handle not nil)*/
	
	if (gScrollVHandle != nil)   	/* Only do if the control is valid */
	{
		HLock((Handle)gScrollVHandle);/* Lock the handle while we use it */
		tempRect.left = (*gScrollVHandle)->contrlRect.left-4;/* the area to update */
		tempRect.top = (*gScrollVHandle)->contrlRect.top;/* Widen the area to update */
		tempRect.right = (*gScrollVHandle)->contrlRect.right;/* Widen the area to update */
		tempRect.bottom = (*gScrollVHandle)->contrlRect.bottom+16;/* the area to update */
		InvalRect(&tempRect);   	/* Flag old position for update routine */
		tempRect.left = (*gScrollVHandle)->contrlRect.left;/* Get control rectangle */
		tempRect.bottom = (*gScrollVHandle)->contrlRect.bottom;/*  */
		temp2Rect.left = whichWindow->portRect.left;/* Get window rectangle */
		temp2Rect.top =  whichWindow->portRect.top ;/*  */
		temp2Rect.right =  whichWindow->portRect.right ;/*  */
		temp2Rect.bottom =  whichWindow->portRect.bottom;/*  */
		Index = temp2Rect.bottom - temp2Rect.top - 13;/* Get the scroll area height */
		tempRect.top = -1;  		/* Pin at top edge */
		HideControl(gScrollVHandle);/* Hide it during size and move */
		SizeControl(gScrollVHandle, 16,Index);/* Make it 16 pixels wide, std height */
		MoveControl(gScrollVHandle, temp2Rect.right - temp2Rect.left-15,tempRect.top);/* Size it correctly */
		ShowControl(gScrollVHandle);/* Safe to show it now */
		HUnlock((Handle)gScrollVHandle);/* Let it float again */
	}   							/* End for scroll handle not nil) */
	
	SetPort(SavePort);  			/* Restore the old port */
}   												/* End of function */


#pragma segment Main
/* Checks to see if the window belongs to our application */
Boolean IsAppWindow(window)
	WindowPtr	window;
{
	short		windowKind;
	long		theRefCon;

	if ( window == nil )
		return false;
	else {	/* application windows have windowKinds >= userKind (8) or dialogKind (2) */
	theRefCon = GetWRefCon(window);
		windowKind = ((WindowPeek) window)->windowKind;
		return ((windowKind >= userKind) || (windowKind == dialogKind)
		 & (gTerm != (TermHandle)theRefCon) & (gConn != (ConnHandle)theRefCon)
		 & (gFT != (FTHandle)theRefCon));
	}
} /*IsAppWindow*/

#pragma segment Main
/* alerts the user of any errors that have occurred */
void AlertUser(msg, fatal)
char			*msg;
Boolean		fatal;
{
	short	itemHit;
	
	SetCursor(&qd.arrow);
	ParamText(c2pstr(msg),"","","");
	itemHit = Alert(rUserAlert, nil);
	
	if (fatal)
		Terminate();
}

#pragma segment Main
/* Initiates a connection */
void OpenConnection()
{
	CMErr				theErr;
	CMBufferSizes	sizes;
	CMStatFlags	status;
	
	if(gConn != nil) {
		theErr = CMStatus(gConn, sizes, &status);
		
		if(theErr == noErr) 
			if((status & (cmStatusOpen + cmStatusOpening)) == 0)
				theErr =CMOpen(gConn, false, nil, -1);
			
			if (theErr != noErr)
				;								// connection tool will tell the uer if there's an errror
	}
}

#pragma segment Main
/* Nukes the connection */
void CloseConnection()
{
	CMErr				theErr;
	CMBufferSizes	sizes;
	CMStatFlags	status;
	
	if (gConn != nil) {
		theErr = CMStatus(gConn, sizes, &status);
		if(theErr == noErr)
			if ((status & (cmStatusOpen+cmStatusOpening)) != 0)
				theErr = CMClose(gConn, false, nil ,0, true);
		
		if (theErr != noErr)
			;								// connection tool will handle the error
	}
}

#pragma segment Main
/* Closes our window */
Boolean DoCloseWindow(window)
	WindowPtr	window;
{
	Boolean		doCloseWindow = true;
	
	if(IsDAWindow(window))
		CloseDeskAcc(((WindowPeek) window)->windowKind);
	else if (IsAppWindow(window)) {
		CloseConnection();
		
		if(gTerm != nil) {
			HUnlock((Handle)gTerm);
			TMDispose(gTerm);
		}
		
		if(gFT != nil) {
			HUnlock((Handle)gFT);
			FTDispose(gFT);
		}	
		
		if(gConn != nil) {
			HUnlock((Handle)gConn);
			CMDispose(gConn);
		}
		
		if (gBuffer != nil)
			DisposPtr(gBuffer);
			
		if(gCache != nil)
			DisposHandle(gCache);
		
		if(gTE != nil)
			TEDispose(gTE);
			
		DisposeWindow(window);
			
	}
	return(doCloseWindow);
}

#pragma segment Main
/* tries to get the default tool proc ID, otherwise gets the first one it can find */
short	FindToolID(toolClass)
	OSType	toolClass;
{
	Str255	toolName;
	OSErr		anyErr;
	short		procID = -1;
	
	if (toolClass == classTM) {
		StuffHex(&toolName,kDefaultTermTool);
		procID = TMGetProcID(toolName);
		
		if(procID == -1) {
			anyErr = CRMGetIndToolName(toolClass,1, toolName);
			if (anyErr == noErr)
				procID = TMGetProcID(toolName);
		}
	}
	else if (toolClass == classCM) {
		StuffHex(&toolName,kDefaultConnTool);
		procID = CMGetProcID(toolName);
		
		if(procID == -1) {
			anyErr = CRMGetIndToolName(toolClass,1, toolName);
			if (anyErr == noErr)
				procID = CMGetProcID(toolName);
		}
	}
	
	else if (toolClass == classFT) {
		StuffHex(&toolName,kDefaultFTTool);
		procID = FTGetProcID(toolName);
		
		if(procID == -1) {
			anyErr = CRMGetIndToolName(toolClass,1, toolName);
			if (anyErr == noErr)
				procID = FTGetProcID(toolName);
		}
	}
	return(procID);
}

#pragma segment Main
/* this is the click loop for the terminal emulation to track */
pascal Boolean clikLoop(refcon)
	long	refcon;
{
	return(true);
}

#pragma segment Initialize
/* this function just sets up the CommToolbox for what we need;
	it should resemble most of the other Macinotosh toolbox calls	*/
void InitCommTB()
{
	(void)InitCTBUtilities();		//	Comm Toolbox Utilities
	(void)InitCRM();						//	Communications Resource Manager
	
	//	initialize the Terminal Manager
	if (InitTM() == tmNoTools)			//	Did we fail
		AlertUser("No terminal tools found, dude!\0", true);
	
	//	Initialize the Connection Manager
	if(InitCM()== cmNoTools)			//	failure????
		AlertUser("No connection tools found, dude!\0", true);
	
	//	Initialize the File Transfer Manager
	if(InitFT() == ftNoTools)				// failure????
		AlertUser("No file transfer tools found, dude!\0",false);
		
	gTerm = nil;							//	gotta initialize our globals
	gConn = nil;
	gFT = nil;
	gCache = nil;
	gFTSearchRefNum = 0;
}

#pragma segment Main
/*	this will cache all the data coming in through the serial port */
pascal long cacheProc(refCon, theTermData)
	long						refCon;
	TermDataBlock	*theTermData;
{
	long						sizeCached;
	TermEnvironRec	theEnvirons;
	
	theEnvirons.version = curTermEnvRecVers;
	theEnvirons.termType = tmTextTerminal;
	(void)TMGetTermEnvirons(gTerm, &theEnvirons);

	if (theTermData->theData == nil) 
		return(-1);
		
	if(gCache != nil) 							//	is it valid????
			DisposHandle(gCache);	//	nuke it, dude
	
	HLock((Handle)theTermData->theData);
	
	gCache = theTermData->theData;
	
	if(HandToHand(&gCache)) {
			DisposHandle(gCache);
			sizeCached = -1;
		}
		else {
			sizeCached = GetHandleSize(gCache);
		}
	
	HUnlock((Handle)theTermData->theData);
	
	if(theTermData->flags == tmTextTerminal && sizeCached >0L) {
		/*HandAndHand(gCache, (**gTE).hText);
		(**gTE).teLength += 80;
		(**gTE).nLines += 1;*/
		((Ptr)*gCache,80L,gTE);
		//(**gTE).viewRect.top -= (**gTE).lineHeight;
		//(**gTE).destRect.top -= (**gTE).lineHeight;
		//TECalText(gTE);
		//TEScroll(0,-(**gTE).lineHeight,gTE);
	}	
	return(tmNoErr);
}

#pragma segment Main
/* gets the window and create the session */
Boolean	DoNewWindow()
{
	WindowPtr		window;
	Rect					theRect;
	short					procID;
	CMBufferSizes	sizes;
	Rect					tempRect;
	short					index;
	Rect					r;
	
	window = GetNewWindow(rWindow, nil, (WindowPtr)-1);
	SetPort(window);
	
/*  Make a scroll bar, Scroll bar   */
	SetRect(&tempRect,-1,241,392,257);
	tempRect.left = window->portRect.left;
	tempRect.right = window->portRect.right;
	tempRect.top = window->portRect.top;
	tempRect.bottom = window->portRect.bottom;
	gScrollHHandle = GetNewControl(cHScrollBar,window);
	index = tempRect.right - tempRect.left - 13;
	tempRect.left = 0; 
	SizeControl(gScrollHHandle, index,16);
	MoveControl(gScrollHHandle, tempRect.left-1,tempRect.bottom - tempRect.top-15);
	
	/*  Make a scroll bar, Scroll bar   */
	SetRect(&tempRect,391,-1,407,242);
	tempRect.left = window->portRect.left;
	tempRect.right = window->portRect.right;
	tempRect.top = window->portRect.top;
	tempRect.bottom = window->portRect.bottom;
	gScrollVHandle = GetNewControl(cVScrollBar,window);
	index = tempRect.bottom - tempRect.top - 13;
	tempRect.top = 0;   		
	SizeControl(gScrollVHandle, 16,index);
	MoveControl(gScrollVHandle, tempRect.right - tempRect.left-15,tempRect.top-1);
	//SetCtlMax(gScrollVHandle,10000);
	/* terminal tool */
	procID = FindToolID(classTM);
	if(procID == -1)
		AlertUser("No terminal tools found, dude!\0", true);
	
	theRect = window->portRect;
	theRect.bottom -= 16;
	theRect.right -= 16;
	
	/* no cache, breakproc, or clikloop */
	gTerm = TMNew(&theRect,&theRect, tmSaveBeforeClear + tmAutoScroll,
		 procID, window, (ProcPtr)TermSendProc,(ProcPtr)cacheProc,nil,
		/*(ProcPtr)clikLoop*/nil, (ProcPtr)ToolGetConnEnvirons,0,0);
	
	SetRect(&r,theRect.left,-theRect.bottom,theRect.right,theRect.top);
	gTE = TENew(&r,&r);
	(**gTE).txSize = 9;
	(**gTE).txFont = monaco;
	(**gTE).viewRect.bottom = (((**gTE).viewRect.bottom - (**gTE).viewRect.top)/
		(**gTE).lineHeight)*(**gTE).lineHeight + (**gTE).viewRect.top;
	TEAutoView(true,gTE);
		
	/* custom configure with our personal settings -- store this as a file later */
	(void)TMSetConfig(gTerm, "Scroll Smooth\0");
	
	if(gTerm == nil)
		AlertUser("Can't create a terminal tool, dude!\0", true);
	
	HLock((Handle)gTerm);
	
	/* connection tool */
	procID = FindToolID(classCM);
	if(procID == -1)
		AlertUser("No connection tools found, dude!/0", true);
		
	sizes[cmDataIn] = kBufferSize*10;		// just the data channel; large incoming buffer
	sizes[cmDataOut] = kBufferSize;
	sizes[cmCntlIn] = 0;
	sizes[cmCntlOut] = 0;
	sizes[cmAttnIn] = 0;
	sizes[cmAttnOut] = 0;
	
	gConn = CMNew(procID, cmData, sizes, 0,0);
	(void)CMSetConfig(gConn,"Baud 9600 DataBits 7 StopBits 1 Parity Even ModemType Other PhoneNumber \0429,1800-346-0145\042\0");
	if(gConn == nil)
		AlertUser("Can't create a connection tool, dude!/0", true);
	
	HLock((Handle)gConn);
	
	/* allocate space for reads/writes using the number returned by the connection tool */
	gBuffer = NewPtrClear(sizes[cmDataIn]);
	if(MemError() != noErr)
		AlertUser("Out of memory, dude!\0", true);
	
	/* file transfer tool */
	procID = FindToolID(classFT);
	if(procID == -1)
		AlertUser("No file transfer tools found, dude!\0", false);
		
	/* no read/write proc -- tool has its own */
	gFT = FTNew(procID, 0 ,(ProcPtr)FTSendProc, (ProcPtr)FTReceiveProc, 
		nil, nil, (ProcPtr)ToolGetConnEnvirons, window, 0L,0L);
	
	if(gFT == nil)
		AlertUser("Can't create a file transfer tool, dude!\0", true);
	
	HLock((Handle)gFT);
	
	gWasFT = false;
	gStartFT = false;
	gFTSearchRefNum = 0;
	
	AddFTSearch();
	
	return(true);
}

#pragma segment Initialize
/* initialize our goodies and the toolbox */
void Initialize()
{
	Handle			menuBar;
	WindowPtr	window;
	OSErr				ignoreError;
	long				total,contig;
	Boolean			ignoreResult;
	EventRecord	event;
	short				count;
	SysEnvRec	TerraMac;
	short				err;
	
	gHasWaitNextEvent = TrapAvailable(_WaitNextEvent, ToolTrap);
	gInBackground = false;
	
	InitGraf((Ptr) &qd.thePort);
	InitFonts();
	InitWindows();
	InitMenus();
	TEInit();
	InitDialogs(nil);
	InitCursor();
	
	for(count=0;count<3;count++)
		ignoreResult = GetNextEvent(everyEvent, &event);
		
	if (!TrapAvailable(_CommToolboxTrap, OSTrap))
		AlertUser("No commtoolbox, dude!\0", true);
	
	ignoreError = SysEnvirons(kSysEnvironsVersion, &TerraMac);
	
	if (TerraMac.systemVersion < 0x600 || TerraMac.machineType < 0)
		AlertUser("You need System 6.0 or later, dude!\0", true);

	PurgeSpace(&total, &contig);
	if (total < kMinSpace) 
		AlertUser(true);
		
	if ((long) GetApplLimit() - (long) ApplicZone() < kMinHeap) 
		AlertUser("Out of memory, dude!\0", true);
		
	InitCommTB();															// initialize our comm toolbox things
		
	menuBar = GetNewMBar(rMenuBar);
	if(menuBar == nil)
		AlertUser("Can't get the menubar, dude!\0", true);
	SetMenuBar(menuBar);
	DisposHandle(menuBar);
	
	AddResMenu(GetMHandle(mApple), 'DRVR');	//	desk accessories
	DrawMenuBar();														//	show the menus
	
	if (!DoNewWindow())												// can we create our comm window????
		AlertUser("Can't create a session, dude!\0", true);
	
	gStopped = true;
}

#pragma segment Main
/* cleans up and gets the hell out of here */
void Terminate()
{
	WindowPtr	aWindow;
	Boolean			closed = true;
	
	aWindow = FrontWindow();
	
	do {
		if (aWindow != nil)
			if (IsAppWindow(aWindow))
				closed = DoCloseWindow(aWindow);
		
		if(aWindow != nil)
			aWindow = (WindowPtr)(((WindowPeek)aWindow)->nextWindow);
	} while(!(closed | aWindow ==nil));
	
	if(closed)
		ExitToShell();
}

#pragma segment Main
/* enables and disables items based on the applications current state */
void AdjustMenus()
{
	WindowPtr		window;
	MenuHandle	menu;
	CMErr				theErr;
	CMBufferSizes	sizes;
	CMStatFlags	status;
	
	window = FrontWindow();
	
	menu = GetMHandle(mFile);
	if(menu == nil)
		AlertUser("Can't get menu resource, dude!\0", true);
	
	if (gConn != nil) {
		theErr = CMStatus(gConn, sizes, &status);
		if (theErr == noErr) {
			if (!IsDAWindow(window)) {
				setitem(menu, iOpen, "Open");
				setitem(menu,iClose,"Close");
				if((status & (cmStatusOpen+cmStatusOpening)) == 0) {
						EnableItem(menu, iOpen);
						DisableItem(menu, iClose);
				}
				else {
					DisableItem(menu,iOpen);
					EnableItem(menu,iClose);
				}
				DisableItem(menu, iSendFile);
				DisableItem(menu,iReceiveFile);
				
				if (gFT != nil ) {
					if (((**gFT).attributes & ftSendDisable) == 0)
						EnableItem(menu, iSendFile);
					if (((**gFT).attributes & ftReceiveDisable) == 0)
						EnableItem(menu, iReceiveFile);
				}
			}
			else {
				setitem(menu,iOpen, "Open");
				setitem(menu, iClose, "Close");
				DisableItem(menu, iOpen);
				EnableItem(menu,iClose);
				DisableItem(menu, iSendFile);
				DisableItem(menu, iReceiveFile);
			}
		}
	}
	
	menu = GetMHandle(mEdit);
	if (menu == nil) 
		AlertUser("Can't get the menu resource, dude!\0", true);
		
	if (!IsDAWindow(window)) {
		EnableItem(menu, iUndo);
		EnableItem(menu, iCut);
		EnableItem(menu,iCopy);
		EnableItem(menu,iPaste);
		EnableItem(menu,iClear);
		EnableItem(menu,iSelectAll);
		EnableItem(menu,iFind);
		EnableItem(menu,iFindAgain);
		EnableItem(menu,iRecallLast);
		EnableItem(menu,iShowClip);
	}
	else {
		DisableItem(menu,iUndo);
		DisableItem(menu, iCut);
		DisableItem(menu, iCopy);
		DisableItem(menu, iPaste);
		DisableItem(menu, iClear);
		DisableItem(menu,iSelectAll);
		DisableItem(menu,iFind);
		DisableItem(menu,iFindAgain);
		DisableItem(menu,iRecallLast);
		DisableItem(menu,iShowClip);
	}
	
	menu = GetMHandle(mSettings);
	if (menu == nil) 
		AlertUser("Can't get the menu resource, dude!\0", true);
		
	if (!IsDAWindow(window)) {
		EnableItem(menu, iConnection);
		EnableItem(menu, iFileTransfer);
		EnableItem(menu, iTerminal);
	}
	else {
		DisableItem(menu, iConnection);
		DisableItem(menu, iFileTransfer);
		DisableItem(menu, iTerminal);
	}
			
	if (gConn != nil) {
		theErr = CMStatus(gConn, sizes, &status);
		
	}
	
	menu = GetMHandle(mPhone);
	if (menu == nil) 
		AlertUser("Can't get the menu resource, dude!\0", true);
		
	if (gConn != nil) {
		theErr = CMStatus(gConn, sizes, &status);
		if (theErr == noErr) {
				
			if (!IsDAWindow(window)) {
				if(status & (cmStatusOpen+cmStatusOpening)) {
					DisableItem(menu, iOpenConn);
					EnableItem(menu, iHangUp);
					EnableItem(menu, iBreak);
					EnableItem(menu, iSendXON);
					EnableItem(menu, iSendXOFF);
				}
				else {
					EnableItem(menu, iOpenConn);
					DisableItem(menu, iHangUp);
					DisableItem(menu, iBreak);
					DisableItem(menu, iSendXON);
					DisableItem(menu, iSendXOFF);
				}
			}
			else {
				DisableItem(menu, iOpenConn);
				DisableItem(menu, iHangUp);
				DisableItem(menu, iBreak);
				DisableItem(menu, iSendXON);
				DisableItem(menu, iSendXOFF);
			}
		}
	}
}

/* tries to give the menu to the tool */ 
#pragma segment Main
Boolean DoToolMenu(menuID, menuItem)
	short	menuID;
	short	menuItem;
{
	
	if (gTerm != nil)
		return(TMMenu(gTerm, menuID, menuItem) );
	
	if (gConn != nil)
		return(CMMenu(gConn, menuID, menuItem));
	
	if(gFT != nil)
		return(FTMenu(gFT, menuID, menuItem));
		
	return(false);
}

#pragma segment Main
/* execute our application commands */
void DoMenuCommand(menuResult)
	long	menuResult;
{
	short			menuID;
	short			menuItem;
	short			itemHit;
	Str255		DAName;
	short			DARefNum;
	Boolean		handledByDA;
	Boolean		ignore;
	Point			where;
	short			result;
	Handle		tempHand;
	
	menuID = HiWrd(menuResult);
	menuItem = LoWrd(menuResult);
	
	if(!DoToolMenu(menuID,menuItem))
		switch (menuID) {
			case	mApple:
				switch(menuItem) {
					case iAbout:
						itemHit = Alert(rAboutAlert,nil);
						break;
					default:
						GetItem(GetMHandle(mApple), menuItem, DAName);
						DARefNum = OpenDeskAcc(DAName);
				}
				break;
			case mFile:
				switch(menuItem) {
					case iOpen:
						if (!IsDAWindow(FrontWindow()))
							OpenConnection();
						break;
					case iClose:
						if(IsDAWindow(FrontWindow()))
							ignore = DoCloseWindow(FrontWindow());
						else
							CloseConnection();
						break;
					case iSendFile:
						if(!IsDAWindow(FrontWindow()))
							DoSend();
						break;
					case iReceiveFile:
						if(!IsDAWindow(FrontWindow()))
							DoReceive();
						break;
					case iRevert:
						break;
					case iOpenCapture:
						break;
					case iAppendCapture:
						break;
					case iPageSetup:
						break;
					case iPrint:
						break;
					case iQuit:
						Terminate();
				}
				break;
			case mEdit:
				case iUndo:
				case iCut:
				case iCopy:
				case iPaste:
				case iClear:
				case iSelectAll:
					handledByDA = SystemEdit(menuItem-1);
				break;
				case iFind:
					break;
				case iFindAgain:
					break;
				case iRecallLast:
					break;
				case iShowClip:
					break;
			case mSettings:
				switch (menuItem) {
					case iConnection:
						if (gConn != nil ) {
							HUnlock((Handle)gConn);
							SetPt(&where,10,40);
							result = CMChoose(&gConn,where, nil);
							switch (result) {	
								case chooseDisaster:
								case chooseFailed:
									AlertUser("Connection choose failed, dude!\0",(result = chooseDisaster));
									break;
								case chooseOKMajor:
									AddFTSearch();
							}
							HLock((Handle)gConn);
						}
						break;
					case iFileTransfer:
						if(gFT != nil ) {
							HUnlock((Handle)gFT);
							SetPt(&where,10,40);
							result = FTChoose(&gFT,where, nil);
							
							switch (result) {
								case chooseDisaster:
								case chooseFailed:
									AlertUser("File transfer choose failed, dude!\0", (result = chooseDisaster));
									break;
								case chooseOKMinor:
								case chooseOKMajor:
									if(gFTSearchRefNum != 0 && gConn != nil)
										CMRemoveSearch(gConn,gFTSearchRefNum);
									gFTSearchRefNum = 0;
									
									AddFTSearch();
							}
							HLock((Handle)gFT);
						}
						break;
					case iTerminal:
						if(gTerm != nil) {
							HUnlock((Handle)gTerm);
							
							SetPt(&where,10,40);
							result = TMChoose(&gTerm,where, nil);
							
							if(result < 0)
								AlertUser("Terminal choose failed, dude!\0", (result = chooseDisaster));
							
							HLock((Handle)gTerm);
						}
						break;
				}
				case mPhone:
					switch(menuItem) {
						case iOpenConn:
							if (!IsDAWindow(FrontWindow()))
								OpenConnection();
							break;
						case iHangUp:
							if(IsDAWindow(FrontWindow()))
								ignore = DoCloseWindow(FrontWindow());
							else
								CloseConnection();
							break;
						case iBreak:
							CMBreak(gConn,20L,true,nil);				// break signal
							AlertUser("Break completed, dude!\0", false);
							break;
						case iSendXON:
							break;
						case iSendXOFF:
							break;
					}
					break;
					
		}
		HiliteMenu(0);
}

#pragma segment Main
/* Grows the window in response to the user resizing it */
void DoGrowWindow(theEvent, whichWindow)
	EventRecord		*theEvent;
	WindowPtr		whichWindow;
{
	Point		myPt;
	Rect		oldRect, tempRect;
	long		mResult;
	
	SetPort(whichWindow);/* Point all activity to this window */
 
	myPt = theEvent->where;/* Get mouse position */
	GlobalToLocal(&myPt);/* Make it relative */

	oldRect.left = whichWindow->portRect.left;/* Save the rect before resizing */
	oldRect.right = whichWindow->portRect.right;/*   */
	oldRect.top = whichWindow->portRect.top;/*   */
	oldRect.bottom = whichWindow->portRect.bottom;/*   */

	SetRect(&tempRect,475,100,(qd.screenBits.bounds.right - qd.screenBits.bounds.left), (qd.screenBits.bounds.bottom - qd.screenBits.bounds.top) - 20);/* l,t,r,b */
	mResult = GrowWindow(whichWindow, theEvent->where, &tempRect);/* Grow it , globally relative*/
	SizeWindow(whichWindow, LoWrd(mResult), HiWrd(mResult), true);/* Resize to result */

	AdjustScrollBars(&oldRect, whichWindow);
	SetPort(whichWindow);/* Point all activity to this window */

	SetRect(&tempRect, 0, myPt.v - 15, myPt.h + 15, myPt.v + 15); /* Position for horz scrollbar area */
	EraseRect(&tempRect);/* Erase old area */
	InvalRect(&tempRect);/* Flag us to update it */
	SetRect(&tempRect, myPt.h - 15, 0, myPt.h + 15, myPt.v + 15);  /* Position for vert scrollbar area */
	EraseRect(&tempRect);/* Erase old area */
	InvalRect(&tempRect);/* Flag us to update it */
	DrawGrowIcon(whichWindow);/* Draw the grow Icon again */
	
	if (gTerm != nil) {																					//adjust the terminal emulation area
		tempRect = whichWindow->portRect;
		tempRect.bottom -= 16;
		tempRect.right -= 16;
		//TMResize(gTerm,&tempRect);
	}
}

#pragma segment Main
/* Updates the window, dude! */
void DoUpdate(window)
	WindowPtr	window;
{
	RgnHandle	savedClip;
	GrafPtr			savedPort;
	
	if (IsAppWindow(window)) {
		GetPort(&savedPort);
		SetPort(window);
		
		/* clip to the window content */
		savedClip = NewRgn();
		GetClip(savedClip);
		ClipRect(&window->portRect);
		DrawControls(window);
		DrawGrowIcon(window);
		
		BeginUpdate(window);
			if(gTerm != nil )
				TMUpdate(gTerm, window->visRgn);
			if(gTE != nil)
				TEUpdate(&window->portRect,gTE);
		EndUpdate(window);
		
		SetClip(savedClip);
		DisposeRgn(savedClip);
		
		SetPort(savedPort);
	}
}

#pragma segment Main
/* Suspends/Resumes the terminal window */
void DoResume(becomingActive)
	Boolean	becomingActive;
{
	WindowPtr	theWindow;
	GrafPtr			savedPort;
	
	GetPort(&savedPort);
	
	theWindow = FrontWindow();
	
	while (theWindow!= nil) {
		if (IsAppWindow(theWindow)) {
			SetPort(theWindow);
			
			if(gTerm != nil)
				TMResume(gTerm, becomingActive);
			if(gConn != nil)
				CMResume(gConn, becomingActive);
			if(gFT != nil)
				FTResume(gFT, becomingActive);
		}
		theWindow = (WindowPtr)(((WindowPeek) theWindow)->nextWindow);
	}
	SetPort(savedPort);
}

#pragma segment Main
/* (De)Activates the window */
void DoActivate(window, becomingActive)
	WindowPtr	window;
	Boolean			becomingActive;

{	
	if (IsAppWindow(window)) {				// does the window belong to us????
		SetPort(window);								//	set current port
		
		if(gConn != nil)									//	do we have a valid connection????
			CMActivate(gConn, becomingActive);//	activate it
		
		if(gTerm != nil)									//	do we have a terminal????
			TMActivate(gTerm, becomingActive);	//	activate it
	
		if(gFT != nil)										//	do we have a vlaid file transfer????
			FTActivate(gFT, becomingActive);//	activate it
	}
}

#pragma segment Main
/* updates mouse cursor depending on its cotton-pickin' location */
void AdjustCursor(mouse)
	Point		*mouse;
{
	WindowPtr	window;
	Point				bozoPt;

	bozoPt = *mouse;
	
	window = FrontWindow();	/* we only adjust the cursor when we are in front */
	
	if ( ! gInBackground &&  IsAppWindow(window) ) {
		GlobalToLocal(&bozoPt);	//	localize the coordinates
		if (gTerm != nil) 	//	if the terminal is valid
			if(!PtInRect(bozoPt,&(**gTerm).viewRect)) 
				InitCursor();
	}
} /*AdjustCursor*/

#pragma segment Main
/* tries to pass the event to a tool if the window is a tool window;
	handles event if appropriate	*/
Boolean	DoToolEvent(event, window)
	EventRecord		*event;
	WindowPtr		window;
{
	Boolean		doToolEvent;
	
	if (window != nil	&&	!IsAppWindow(window)) {	//	is the window valid and not ours????
		doToolEvent = true;
		
		/* Evidently, copies of the commtb record are in the refCon field of
			the window for changing the settings	*/
		if(gFT != nil && gFT == (FTHandle)GetWRefCon(window))
			FTEvent(gFT,event);	// handle  file transfer manager event 
		else if(gConn != nil && gConn == (ConnHandle)GetWRefCon(window))
			CMEvent(gConn,event);	//	handle the connection manager event
		else if(gTerm != nil && gTerm == (TermHandle)GetWRefCon(window))
			TMEvent(gTerm,event);	// handle the terminal manager event
		else
			doToolEvent = false;
	}
	else
		doToolEvent = false;
		
	return(doToolEvent);
}

#pragma segment Main
/* Controls handling all the events in the event queue in our application;
	We could define scripting events if we wanted and intercept them here
	as well. This should be straight forward and pretty much self-explanatory.*/
void DoEvent(event)
	EventRecord		*event;
{
	short				part;
	short				err;
	WindowPtr	window;
	char				key;
	long				result;
	Boolean			processed;
	Point				aPoint;
	
	switch (event->what) {							//	what type of event did we have????
		case mouseDown:
			part = FindWindow(event->where,&window);	//	find where the mouse was clicked
			switch(part) {
				case inMenuBar:
					AdjustMenus();
					DoMenuCommand(MenuSelect(event->where));
					break;
				case inSysWindow:
					SystemClick(event, window);
					break;
				case inContent:					//	within the window itself
					if (!DoToolEvent(event, window)) {	//	tool event???? try to handle anyway
						if (window != FrontWindow())	//	 not in front????
							SelectWindow(window);	//	bring it to front
						else if (gTerm != nil)	//	otherwise, it may be a terminal event
							TMClick(gTerm, event);
					}
					break;
				case inDrag:	//	user wants to move the window????
					if (!DoToolEvent(event, window))
						DragWindow(window,event->where,&qd.screenBits.bounds);
					break;
				case inGrow:
					if(!DoToolEvent(event,window))	//	user wants to change the window's size????
						if(IsAppWindow(window))
							DoGrowWindow(event, window);
					break;
				case inZoomIn:
				case inZoomOut:
				case inGoAway:	//	user wants to nuke the window????
					if (DoToolEvent(event, window))
						;
					break;
			}
			break;
		case keyDown:	//	user typed on the keyboard
		case autoKey:
			window = FrontWindow();		//	need to get the front window
			if (IsAppWindow(window)) {	//	is it our window????
				key = event->message & charCodeMask;
				processed = false;
				
				if ( event->modifiers & cmdKey ) {	
					AdjustMenus();						/* enable/disable/check menu items properly */
					result = MenuKey(key);
					if(result != 0L) {		//	user selected a cmd-key combination
						processed = true;
						DoMenuCommand(result);	// handle it like a menu
					}
				}
				
				if(gTerm != nil && !processed)	//	key pressed was for the terminal
					if(!DoToolEvent(event, window)) 
						TMKey(gTerm, event);			//	let the terminal manager handle it
				}
		break;
		case activateEvt:
			window = (WindowPtr)event->message;
			
			if(!DoToolEvent(event, window))
				DoActivate(window, (event->modifiers & activeFlag) != 0);
			break;
		case updateEvt:
			window = (WindowPtr)event->message;
			
			if(!DoToolEvent(event, window))
				DoUpdate(window);
				break;
		case diskEvt:	//	disk inserted events!
			if (HiWrd(event->message) != noErr) {
				SetPt(&aPoint, kDILeft, kDITop);
				err = DIBadMount(aPoint, event->message);
			}
			break;
		case kOSEvent:
			switch (event->message >> 24) {		/* high byte of message */
				case kSuspendResumeMessage:		/* suspend/resume is also an activate/deactivate */
					if(!DoToolEvent(event, FrontWindow()))
						;	//	do nothing
					gInBackground = (event->message & kResumeMask) == 0;
					DoResume(!gInBackground);
					break;
			}
			break;
	}
}

#pragma segment Main
/* idles all the communications tools; this was taken from the Surfer pascal
	example provided from Apple -- you can get it from APDA.*/
void DoIdle()
{
	WindowPtr		theWindow;
	Boolean				doFT, doTM;
	GrafPtr				savedPort;
	
	GetPort(&savedPort);
	theWindow = FrontWindow();
	
	while (theWindow != nil) {
		if (IsAppWindow(theWindow)) {
			SetPort(theWindow);
			//TEIdle(gTE);
			
			if(gConn != nil)
				CMIdle(gConn);
				
			doFT = false;
			doTM = true;
			
			if (gFT != nil ) {
				if (((**gFT).flags  & ftIsFTMode) != 0) {
					doFT = true;
					gWasFT = true;
					
					if(((**gFT).attributes & ftSameCircuit) != 0)
						doTM = false;
				}
				else {
					if (gWasFT) {
						gWasFT = false;
						
						if(((**gFT).flags & ftSucc) == 0)
							;
						AddFTSearch();
					}
					if(gStartFT)
						DoReceive();
				}
				if (doFT)
					FTExec(gFT);
			} /* if gFT != nil  */
			
			if(gTerm != nil) {
				if (doTM) {
					TMIdle(gTerm);
					TermRecvProc();
				}	
			}/* gTerm != nil */
		}
		
		theWindow = (WindowPtr)(((WindowPeek)theWindow)->nextWindow);
	}
	SetPort(savedPort);
}

#pragma segment Main
/* Main event loop, dude */
void EventLoop()
{
	Boolean				gotEvent;
	EventRecord		event;

	do {
		/* use WNE if it is available */
		DoIdle();								//keep the term looking cool, dude!
		if ( gHasWaitNextEvent )
			gotEvent = WaitNextEvent(everyEvent, &event, 0, nil);
		else {
			SystemTask();
			gotEvent = GetNextEvent(everyEvent, &event);
		}
		if ( gotEvent ) {
			/* make sure we have the right cursor before handling the event */
			AdjustCursor(&event.where);
			DoEvent(&event);
		}
		/* change the cursor (and region) if necessary */
		AdjustCursor(&event.where);
	} while ( true );	/* loop forever; we quit via ExitToShell */
} /*EventLoop*/

#pragma segment Main
/* main */
main()
{
	UnloadSeg((Ptr)_DataInit);						// Give a hoot, don't pollute!
	
	MaxApplZone();											//	expand the heap
	MoreMasters();												//	allocate some blocks of master pointers
	MoreMasters();
	MoreMasters();
	
	Initialize();														//	set up everything -- let's get going!
	UnloadSeg((Ptr)Initialize);							// Give a hoot, don't pollute!

	EventLoop();
}