/*  -*- Mode:C; -*- */
/*                                                                       
 *                                                                       
 *                      RESTRICTED RIGHTS LEGEND                         
 *                                                                       
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions as set forth in subdivision (b)(3)(ii) of the Rights in
 * Technical Data and Computer Software clause at 52.227-7013.
 *        
 *                    TEXAS INSTRUMENTS INCORPORATED.
 *                            P.O. BOX 2909
 *                         AUSTIN, TEXAS 78769
 *                              MS 2151
 *
 * Copyright (C) 1988, Texas Instruments Incorporated.
 * All rights reserved.
 * 
 * From file Sample.c
 *
 * Copyright Apple Computer, Inc. 1985-1987
 * All rights reserved.
 */

/*  File: RPC-Flashcards.C
 *  Origin: TI Tech Support with use of Sample.c from MPW
 *
 *This demonstrates a simple RPC application. A server is 
 *established on the Mac for evaluating simple arithmetic expressions.
 *On the Explorer, the user is prompted for such an expression and 
 *it is passed to the mac as a string. The Mac evaluates it and 
 *displays the answer. The mac side prompts the user for an expression 
 *and it is passed back to the Explorer as the return 
 *value. That expression is evaluated by the Explorer and the result is displayed.
 */

#include	<types.h>
#include	<quickdraw.h>
#include	<toolutils.h>
#include	<fonts.h>
#include	<events.h>
#include	<windows.h>
#include	<dialogs.h>
#include	<menus.h>
#include	<desk.h>
#include	<textedit.h>
#include	<scrap.h>
#include	<segload.h>

#include <:bsd:sys:time.h>
#include <:rpc:rpc.h>
#include <stdio.h>
#include <files.h>
#include <devices.h>
#include <osutils.h>
#include <Micronet-device.h>
#include <Micronet-accessors.h>
#include <errors.h>
#include <serial.h>
#include <sockets.h>
#include <strings.h>
#include <errno.h>
#include <utility.h> 

extern _DataInit();

/*
 * Resource ID constants and menus commands.
 */
#define	appleID			128 			
#define	fileID 			129 			
#define	editID 			130 			
#define	appleMenu		0				
#define	aboutMeCommand		1
#define	fileMenu		1
#define	quitCommand 		1
#define	editMenu		2
#define	undoCommand 		1
#define	cutCommand		3
#define	copyCommand 		4
#define	pasteCommand		5
#define	clearCommand		6

#define menuCount		3
/*
 * For the one and only text window
 */
#define windowID		128	   /* window used for mac flashcard  */	
/*
 * For the About RPC Flashcards DLOG
 */
#define	aboutMeDLOG		128
#define	authorItem		2          /* For SetIText */
#define	languageItem		3	   /* For SetIText */

/*
 * For the RPC-FLASHCARD input DLOG
 */
#define	InputDLOG		227
#define	okButton		1
#define	InstructionItem		2	   /* For SetIText */
#define	ExpressionItem		3	   /* For SetIText */

/*
 * For the RPC
 */
#define RPC_SLEEP_TIME 0		   
#define DRIVER_SLEEP_TIME RPC_SLEEP_TIME 
#define e2m_program_number   0x20000019	   /* RPC program number */
#define version_number                1	   /* RPC version number */
#define procedure_number              1	   /* RPC procedure number */

					   /* macro for dragging windows */
#define SETRECT(rectp, _left, _top, _right, _bottom)	\
	(rectp)->left = (_left), (rectp)->top = (_top), 	\
	(rectp)->right = (_right), (rectp)->bottom = (_bottom)

/*
 * HIWORD and LOWORD macros, for readability.
 */
#define HIWORD(aLong)		(((aLong) >> 16) & 0xFFFF)
#define LOWORD(aLong)		((aLong) & 0xFFFF)

/*
 * contains the integer from the string and the 
 * index of the next integer in that string 
 */		
struct int_in_string {     
   int number;
   int index;
};
/*
 * contains the character from the string and the 
 * index of the next character in that string 
 */
struct char_in_string {
   char ch;
   int index;
};

/*
 * Global Data objects, used by routines external to main().
 */					   
MenuHandle MyMenus[menuCount]; 		   /* The menu handles */
Boolean    DoneFlag;	    		   /* Becomes TRUE when File/Quit chosen*/
TEHandle   TextH;		    	   	   /* The TextEdit handle */
char       OutputLine[256];            /* Text output to window */
int        svc_fds;                    /* Referenced from: xprt_unregister in*/
                                       /* file: HD:microexp:macsys:rpc:rpc.o */
register   WindowPtr myWindow;	 	   /* Referenced often */
Rect	   dragRect;               	   /* Used in main and event_handler */
char       dialog_text[25];        	   /* Holds text returned from dialog box*/

/*
 * The main program is used to initialize various Mac subsystems such as
 * the window and menu managers. It also contains the event handling loop.
 * Unlike most operating systems, the Mac OS does not handle events for
 * the user. The application program must handle all events. 
 */
int main()
{   
	Rect		screenRect;	   			/* rectangle for our window */
	Rect	        txRect;		   		/* a rectangle for text editting */
	WindowRecord	wRecord;          	/* record describibg our window */
	extern void 	setupMenus();
	
	/*
	 * Initialization traps
	 */
	UnloadSeg(_DataInit);
	InitGraf(&qd.thePort);		   		/* Initialize Quickdraw */
	InitFonts();			   			/* Initialize fonr manager */
	FlushEvents(everyEvent, 0);	   		/* Start with a clean slate */
	InitWindows();			   			/* Initialize window manager*/
	InitMenus();			   			/* Initialize menu manager */
	TEInit();			   				/* Initialize Text Edit */
	InitDialogs(nil);		   			/* Initialize dialog manager */
	InitCursor();			   			/* Make cursor visible & arrow-shaped*/
	/*
	 * setupMenus is execute-once code, so we can unload it now.
	 */
	setupMenus();			   			/* Local procedure, below */
	UnloadSeg(setupMenus);
	/*
	 * Calculate the drag rectangle in advance.
	 * This will be used when dragging a window frame around.
	 * It constrains the area to within 4 pixels from the screen edge
	 * and below the menu bar, which is 20 pixels high.
	 */
	screenRect = qd.screenBits.bounds;
	SETRECT(&dragRect, 4, 20 + 4, screenRect.right-4, screenRect.bottom-4);
	/*
	 * Create our one and only window from the WIND resource.
	 * If the WIND resource isn't there, we die.
	 */
	myWindow = GetNewWindow(windowID, &wRecord, (WindowPtr) -1);
	SetPort(myWindow);
	/*
	 * Create a TextEdit record with the destRect and viewRect set
	 * to my window's portRect (offset by 4 pixels on the left and right
	 * sides so that text doesn't jam up against the window frame).
	 */
	txRect = myWindow->portRect;
	InsetRect(&txRect, 4, 0);	  		/* inset text rect by 4 pixels */
	TextH = TENew(&txRect, &txRect);  	/* Not growable, so destRect==viewRect */
    /*
     * Initialize and check status on Micronet, Socket and RPC
     */
	if (initialize_micronet())
	 {
	  sprintf(OutputLine,"initialize_micronet failed.\nPress any key\n");
	  TEInsert(OutputLine,strlen(OutputLine),TextH);
	  wait_key();
	  exit(-1);
	 }
     /* 
      * making sure that PortMap is initialized, look for events forever
      */
    while(!DriverData->portmap_initialized)  
      event_handler(everyEvent, 1);

    if (socket_initialize())		   	/* initialize RPC sockets */
	 {
	  sprintf(OutputLine,"socket_initialize failed.\nPress any key\n");
	  TEInsert(OutputLine,strlen(OutputLine),TextH);
	  wait_key();
 	  exit(-1);
     }
    if (rpc_initialize())		   		/* initialize RPC handler */
	 {
 	  sprintf(OutputLine,"rpc_initialize failed.\nPress any key\n");
	  TEInsert(OutputLine,strlen(OutputLine),TextH);
	  wait_key();
	  exit(-1);
	 }

	/*
	 * Ready to go.
	 * Start with a clean event slate, and cycle the event loop
	 * until the File/Quit menu item sets DoneFlag.
	 */
	DoneFlag = false;
    /*
     * Look for the RPC event.
     */
	do
	 {
      event_handler(everyEvent, RPC_SLEEP_TIME); 
      svc_run();             		   	/* Dispatch RPC calls */
     }					   
	while(!DoneFlag && !DriverData->done_flag); 
	/*
	 * Cleanup here.
	 */
	TEDispose(TextH);		   	/* dispose of and clear window */
	CloseWindow(myWindow);
	
	return 0;				/* Return from main() to allow C runtime cleanup */
} /* end main */

/*
 * Demonstration of the segmenting facility:
 *
 * This code is execute-once, so we toss it in the "Initialize"
 * segment so that main() can unload it after it's called.
 *
 * There really isn't much here, but it demonstrates the segmenting facility.
 */
/*
 * Set the segment to Initialize.  BEWARE: leading and trailing white space
 * would be part of the segment name!
 */
#define	__SEG__ Initialize

/*
 * Set up the Apple, File, and Edit menus.
 * If the MENU resources are missing, we die.
 */
void setupMenus()
{
	extern		MenuHandle	MyMenus[];
	register	MenuHandle	*pMenu;

	/*
	 * Set up the desk accessories menu.
	 * The "About RPC Flashcards" item, followed by a grey line,
	 * is presumed to be already in the resource.  We then
	 * append the desk accessory names from the 'DRVR' resources.
	 */
	MyMenus[appleMenu] = GetMenu(appleID);
	AddResMenu(MyMenus[appleMenu], (ResType) 'DRVR');
	/*
	 * Now the File and Edit menus.
	 */
	MyMenus[fileMenu] = GetMenu(fileID);
	MyMenus[editMenu] = GetMenu(editID);
	/*
	 * Now insert all of the application menus in the menu bar.
	 *
	 * "Real" C programmers never use array indexes
	 * unless they're constants :-)
	 */
	for (pMenu = &MyMenus[0]; pMenu < &MyMenus[menuCount]; ++pMenu) {
		InsertMenu(*pMenu, 0);
	}

	DrawMenuBar();

	return;
} /* end setupMenus */

/*
 * Back to the Main segment.
 */
#define	__SEG__ Main

/*
 * Display the RPC-Flashcards dialog.
 * We insert two static text items in the DLOG:
 *		The author name
 *		The source language
 * Then wait until the OK button is clicked before returning.
 */
void showAboutMeDialog()
{
	GrafPtr 	savePort;
	DialogPtr	theDialog;
	short		itemType;
	Handle		itemHdl;
	Rect		itemRect;
	short		itemHit;

	GetPort(&savePort);
	theDialog = GetNewDialog(aboutMeDLOG, nil, (WindowPtr) -1);
	SetPort(theDialog);

	GetDItem(theDialog, authorItem, &itemType, &itemHdl, &itemRect);
	SetIText(itemHdl, "Texas Instruments");
	GetDItem(theDialog, languageItem, &itemType, &itemHdl, &itemRect);
	SetIText(itemHdl, "C/Pascal/Assembly");

	do {
		ModalDialog(nil, &itemHit);
	} while (itemHit != okButton);

	CloseDialog(theDialog);

	SetPort(savePort);
	return;
} /* end ShowAboutMeDialog */

/*
 * Process mouse clicks in menu bar
 */
void doCommand(mResult)
	long	mResult;
{
	int 				theMenu, theItem;
	char				daName[256];
	GrafPtr 			savePort;
	extern MenuHandle	MyMenus[];
	extern Boolean		DoneFlag;
	extern TEHandle 	TextH;
	extern void 		showAboutMeDialog();

	theItem = LOWORD(mResult);
	theMenu = HIWORD(mResult);		/* This is the resource ID */

	switch (theMenu) {
		case appleID:
			if (theItem == aboutMeCommand) {
				showAboutMeDialog();
			} else {
				GetItem(MyMenus[appleMenu], theItem, daName);
				GetPort(&savePort);
				(void) OpenDeskAcc(daName);
				SetPort(savePort);
			}
			break;

		case fileID:
			switch (theItem) {
				case quitCommand:
					DoneFlag = true;  /* Request exit */
					break;
				default:
					break;
			}
			break;

		case editID:
			/*
			 * If this is for a 'standard' edit item,
			 * run it through SystemEdit first.
			 * SystemEdit will return FALSE if it's not a system window.
			 */
			if ((theItem <= clearCommand) && SystemEdit(theItem-1)) {
				break;
			}
			/*
			 * Otherwise, it's my window.
			 * Cut/Copy/Paste do nothing for RPC-flashcards 
			 */
			switch (theItem) {
				case undoCommand:
					break;
				case cutCommand:
				    break;
				case copyCommand:
					break;
				case pasteCommand:
					break;
				case clearCommand:
					break;
				default:
					break;
			} /*endsw theItem*/
			break;

		default:
			break;

	}/*endsw theMenu*/

	HiliteMenu(0);

	return;
} /* end doCommand */

/*
 * Event Handler. Monitors all events and acts when one is posted.
 * Required to be this name by RPC libraries.
 */
int
event_handler(event_mask, wait_time)
{
	EventRecord 	myEvent;
	WindowPtr	theActiveWindow;
	WindowPtr	whichWindow;
	Point		mousePt;
	CursHandle	watchHdl;
	
	watchHdl = GetCursor(watchCursor);
		/*
		 * Event Handler tasks:
		 */
		SystemTask();
					   /* Used often, avoid repeated calls */
		theActiveWindow = FrontWindow(); 
		/*
		 * Things to do when we are the active window:
		 *	[1] Track the mouse, and set the cursor appropriately:
		 *	    (Watch cursor if in content region, Arrow if outside)
		 *	[2] TEIdle our textedit window, so insertion bar blinks.
		 */
		if (myWindow == theActiveWindow) {
			GetMouse(&mousePt);
			SetCursor(PtInRect(&mousePt, &myWindow->portRect) ? *watchHdl : &qd.arrow);
			TEIdle(TextH);	   /* Blink cusrsor at insertion point */
		}
		/*
		 * Handle the next event.
		 */
	if(WaitNextEvent(event_mask, &myEvent, wait_time, NULL))	 
	 { 
		switch (myEvent.what) 
		 {
			case mouseDown:
				switch (FindWindow(&myEvent.where, &whichWindow)) {
					case inSysWindow:
						SystemClick(&myEvent, whichWindow);
						break;

					case inMenuBar:
						doCommand(MenuSelect(&myEvent.where));
						break;

					case inDrag:
						DragWindow(whichWindow, &myEvent.where, &dragRect);
						break;

					case inGrow:
						
					case inContent:
						if (whichWindow != theActiveWindow) {
							SelectWindow(whichWindow);
						} 
						break;

					default:
						break;
				}/*endsw FindWindow*/
				break;

			case keyDown:
			
			case autoKey:
			
			case activateEvt:
			
			case updateEvt:
		    	if ((WindowPtr) myEvent.message == myWindow) {
			 		BeginUpdate(myWindow);
			 		EraseRect(&myWindow->portRect);
			 		TEUpdate(&myWindow->portRect, TextH);
			 		EndUpdate(myWindow);
			 	}
			 	break; 

			default:
				break;

	     } /* endsw myEvent.what*/
		
	 } /* endif WaitNextEvent */
	
  return myEvent.what;
  
} /* end event_handler */

/******************************************************************************
 * This is the beginning of this RPC application
 ******************************************************************************/

/*
 * Use dialog box to get new expression from user
 */

do_dialog()
{
	GrafPtr 	savePort;
	DialogPtr	theDialog;
	short		itemType;
	Handle		itemHdl;
	Rect		itemRect;
	short		itemHit;
	int             i; 

	GetPort(&savePort);		   			/* Get current Quickdraw grafPort */
					   					/* Open dialog window */
	theDialog = GetNewDialog(InputDLOG, nil, (WindowPtr) -1);
	SetPort(theDialog);		   			/* Make it the current grafPort */
	/*
     * Put default text into dialog box
     */
	GetDItem(theDialog, ExpressionItem, &itemType, &itemHdl, &itemRect);
	SetIText(itemHdl, "2 + 2");
	SelIText(theDialog, ExpressionItem, 0, 32767);

	do {
		ModalDialog(nil, &itemHit); 			/* Display dialog til OK button hit*/
	} while (itemHit != okButton);
    
	for(i=0; i<strlen(dialog_text); i++) {    	/*clear out dialog text array*/
		dialog_text[i] = '\0';
	}
    /*
	 * When the OK button is hit, gather the user's input. This will be 
     *  the arithmetic expression we pass back to the Explorer
	 */
	if (itemHit == okButton)  {     
	       GetDItem(theDialog, ExpressionItem, &itemType, &itemHdl, &itemRect);
	       GetIText(itemHdl, dialog_text);    	/* get the user's expression */
		
	}
	
	CloseDialog(theDialog); 

	SetPort(savePort);		   					/*Restore current port */
	return;

}       /* end do_dialog */

/*
 * convert a number in a string to the integer it represents
 * returns a struct containing the integer and its index
 */
struct int_in_string make_int(str,start)
char str[50];
int start;				            			/* where to start copying */
{
   char new_str[50];
   int i = 0;
   struct int_in_string answer;	       			/* will contain the integer and its index */
   
   while ((new_str[i] = str[i + start]) != '\0')   /* copy string until null */
            i++;
   answer.number = atoi(new_str);	           /* make it an integer */
   answer.index = i;
   return(answer);
   }
   
/* 
 * Return the arithmetic operator. Allowed operators are +, -, *, /, and ! 
 */  
struct char_in_string get_operator(str)

char str[];
{ 
  struct char_in_string operator;	   			/* will contain operator and its index*/
  int i;
					   			/* If it's a valid operator, get it */
  for (i=0; i<=strlen(str); i++) {
    if ((str[i] == '+') ||
	(str[i] == '-') ||
	(str[i] == '*') ||
	(str[i] == '!') ||
	(str[i] == '/'))   {
	  operator.index = i + 1;
	  operator.ch  = str[i];
	  } /*end if*/  
    } /*end for*/
 return operator;
 }
 
 /*
  *Recursive factorial
  */
  double factorial(num)
  double num;
  {
    if((num == 0) || (num == 1))
	  return 1;
	else 
	  return (num * factorial(num - 1));
  }
  

/*
 * Evaluate the arithmetic expression
 */
eval_expr(expr)
char *expr[50];
{
  struct int_in_string op1, op2;           		/* operands */
  struct char_in_string op;		   				/* operator */
  int answer;
  
  TESetSelect(0, 130, TextH);		   			/* clear window */
  TEDelete(TextH);
 
  op1 = make_int(*expr, 0);		   				/* get first operand */
  
  op = get_operator(*expr);		   				/* get operator */
 
  if (op.ch != '!')  {							/* don't read second operand if it's factorial */
  	op2 = make_int(*expr, op.index);
	}
  sprintf(OutputLine,"\nReceived from Explorer and evaluated: %s = ", *expr);    
  TEInsert(OutputLine, strlen(OutputLine), TextH);
  
  switch (op.ch)  {    		/* pick the arithmetic operation to perform */
    	case '+': sprintf(OutputLine, "%d",op1.number + op2.number);
	          break;
	case '-': sprintf(OutputLine, "%d",op1.number - op2.number);
	          break;
	case '*': sprintf(OutputLine, "%d",op1.number * op2.number);
	          break;
	case '/': sprintf(OutputLine, "%f",(float)op1.number / (float)op2.number);
	          break;
	case '!': sprintf(OutputLine, "%f",factorial((double)op1.number));
	          break;
	default : sprintf(OutputLine, "\n%c is not a valid operator", op.ch);
  	          TEInsert(OutputLine, strlen(OutputLine), TextH);
			  break;
	  } /*end switch*/
			  
  TEInsert(OutputLine, strlen(OutputLine), TextH);
  
  return;
} /* end eval_expr */

 char **arithmetic(); 	     				/* I don't know why this pre-declaration is needed. */
  
/*
 * Arithmetic - this is the driver for the flashcard demo. It is called when an RPC
 *  call is received by the RPC server that we registered on the Mac. This must 
 *  return the address of the pointer to a string, i.e. a pointer to a pointer
 *  since that is what XDR_STRING wants.
 */
 char **
 arithmetic(exp_expr)
 
 char exp_expr[50];
 {
    int stat;
    static char  *p;			  		 /* this will hold the address of the 
					      		    stuff we return
to the Explorer */
    long *finalticks;
    long numticks = 60;

    TESetSelect(0, 130, TextH);		  	 	/* clear wondow */
    TEDelete(TextH);
	 
    sprintf(OutputLine,"Waiting for RPC call...\n");
    TEInsert(OutputLine, strlen(OutputLine), TextH);
 
    eval_expr(exp_expr);       				/* evaluate expression passed from explorer*/
	
    Delay(numticks, finalticks);        	/* wait 1 second for dramatic effect */
    do_dialog();                           	/* Get mac expression from dialog 
					    * box and puts it into
					    * the global variable dialog_text. 
					    * This will be passed to explorer. */
	
    sprintf(OutputLine, "\nSending to Explorer:\n  %s", dialog_text);
    TEInsert(OutputLine, strlen(OutputLine), TextH);
    /*
     * Set up value to be returned to Explorer. Dynamically allocating space for 
     * a static character array is the only way to make this work. The MPW 
     * compiler complained when we tried to return the address of a global.
     * Returning the address of a non-static local variable was bad news because
     * its memory got reallocated before the RPC process could pick up the data.
     * Therefore, we do it kile this:
     */
    p = (char *)malloc(1 + strlen (dialog_text)); 
    strcpy(p, dialog_text);
	
    return(&p);
 }


 
/*
 * Initialize and register the RPC
 */
rpc_initialize()
{
	int stat; 
   	if((stat = registerrpc(e2m_program_number,
				  version_number,
				  procedure_number,
				  arithmetic,	   		/* call this function when we get an RPC call */						      
				  xdr_string,           		/* input parm type */
				  xdr_string)) != 0)    		/* output parm type */ 
   {
	 sprintf(OutputLine,"Error %d in registerrpc\n", stat);
	 TEInsert(OutputLine,strlen(OutputLine),TextH);
	 return -1;
   }
   	else {		 
  		sprintf(OutputLine,"RPC Server Registered...\n");
  		TEInsert(OutputLine, strlen(OutputLine), TextH);
		svc_run();
		return 0;
	}
	
    	
} /* end rpc_initialize */


/* ***END OF FILE *** */
