/* -*- Mode:C; Tab-width:4 -*- */

 /* 
 This file contains the mac-side rpc server, HOOKS, that corresponds to
 the lisp-side rpc client, HOOKS, in MICROEXP:EXPSYS:RPC-EXAMPLES.
 HOOKS;HOOKS.LISP.  In addition to demonstrating Remote Procedure Call
 between the mac and lisp processors, the example shows how to use the
 application hooks built into the TBServer.  HOOKS is a simple four 
 function calculator that accepts an operator and two operands.  Refer 
 to the instructions in MICROEXP:EXPSYS:RPC-EXAMPLES.HOOKS;HOOKS.LISP on
 how to call the server from lisp.

 This demonstration was built and tested under the following configuration:
   Finder 6.1, System 6.03, MPW 3.01
   TBServer 6.3.01
   microExplorer 6.1 with latest patches
*/

 
#include <rpc.h>
#include <wlw.h>
#include <fonts.h>
#include <strings.h>
#include <menus.h>
#include <dialogs.h>
#include <desk.h>
#include <toolutils.h>

#define RPC_SLEEP_TIME 0

static Boolean verbose = false;

static Boolean doneFlag = false;

#define appleM		1		
#define	fileM		2
#define	verboseM	3
#define MenuCount	3

#define appleID 	128 	
#define	fileID		129
#define	verboseID	130

#define verboseON   1
#define verboseOFF  2

MenuHandle myMenus[MenuCount+1];

static void
SetUpMenus()

/* setup the menus and menu bar */

{
  int x;
  
  myMenus[appleM] = GetMenu(appleID);  		/* read Apple menu from resource file */
  AddResMenu(myMenus[appleM], 'DRVR');  	/* add desk accessory names to Apple menu */
  myMenus[fileM] = GetMenu(fileID);     	/* read file menu from resource file */
  myMenus[verboseM] = GetMenu(verboseID);   /* read verbose menu from resource file */

  for(x=1; x<=MenuCount; x++)       
	InsertMenu(myMenus[x], 0); 
  CheckItem(myMenus[verboseM], verboseOFF, true);
  DrawMenuBar();					
}

#define aboutMeDLOG 	128
#define	aboutMeCommand 	1

#define okButton 		1

static void
ShowAboutMeDialog()

/* Display a dialog box in response to the 'About Register' menu item */

{
  short itemHit = NULL;
  DialogPtr theDialog;
  
  theDialog = GetNewDialog(aboutMeDLOG, NULL, (WindowPtr)- 1);
  
  do
	ModalDialog(NULL, &itemHit);
  while (itemHit != okButton);
  
  CloseDialog(theDialog);
}

static void 
DoVerbose(theItem)
	short theItem;

/* set default mode to verbose or non-verbose */

{
	if(theItem == verboseON){
		verbose = true;	      
		CheckItem(myMenus[verboseM], verboseON,  true);
		CheckItem(myMenus[verboseM], verboseOFF, false);
		}
	if(theItem == verboseOFF){
		verbose = false;	                  
		CheckItem(myMenus[verboseM], verboseOFF, true);
		CheckItem(myMenus[verboseM], verboseON,  false);
		}
}


static void
DoCommand(mResult)
	 long mResult;

/* Execute command specified by mResult, the result of MenuSelect */

{
  short theItem; 		/* menu item number from mResult low-order word */
  short theMenu; 		/* menu number from mResult high-order word */
  char *name = NULL; 	/* desk accessory name */
  short temp;
  
  theItem = LoWord(mResult);	/* call Toolbox Utility routines to */
  theMenu = HiWord(mResult);	/* set menu item number and menu number */
  
  switch(theMenu)
	{
	
	case appleID:
	  if(theItem == aboutMeCommand)
		ShowAboutMeDialog();
	  else
		{					  
		  GetItem(myMenus[appleM], theItem, name);  
		  temp = OpenDeskAcc(name);                 
		  SetPort(qd.thePort);		           
		} 		                            
	  break;
	  
	case fileID:
	  doneFlag = true;	       
	  break;

	case verboseID:
	  DoVerbose(theItem);
	  break;
	}			            
  HiliteMenu(0);			
}

#define program_number       0x20118699
#define version_number                1
#define procedure_number              1

float *my_calculator();
static float myresult = 0.0;


/* In this example the client uses a structure because it needs
   to pass in three arguments but can only pass one.           */

struct mystruct {
	int    operator;
	float  operand1;
	float  operand2;
};



/* My filter function for MYSTRUCT.  It "deserializes" the argument 
   passed in from the client from the universal XDR language to its 
   own native C language.   

   Because it is the XDR-IN filter (specified in the REGISTERRPC
   call below), it will actually get called twice as follows: 

     X_OP = XDR_DECODE to deserialize the argument.
     X_OP = XDR_FREE to allow memory deallocation.

*/

bool_t xdr_mystruct(xdrs, mystruct_ptr)
XDR *xdrs;
struct mystruct *mystruct_ptr;
{

	switch (xdrs->x_op) {

	case XDR_ENCODE:           /* Cannot handle this case */
		return (FALSE);

	case XDR_DECODE:
		return (xdr_int(xdrs, &mystruct_ptr->operator) &&
				xdr_float(xdrs, &mystruct_ptr->operand1) &&
				xdr_float(xdrs, &mystruct_ptr->operand2));

	case XDR_FREE:             /* Not doing memory management */
		return (TRUE);
	}
	return (FALSE);            /* Should not be any other callers */

}


float *
my_calculator(s)

struct mystruct *s;

/* This is the actual procedure registered for the server via REGISTERRPC, and
   the one that gets called when the client does a CALLRPC.  When it gets called,
   the argument, S, has already been deserialized.  When it returns, the result
   &MYRESULT will be serialized before being passed back to the client.   */

{		
	switch (s->operator) {
	
		case 0:
			myresult = (s->operand1 + s->operand2);	
			if (verbose) 
				printf("%f\n", myresult);
			break;
		
		case 1:
			myresult = (s->operand1 - s->operand2);	
			if (verbose) 
				printf("%f\n", myresult);
			break;

		case 2:
			myresult = (s->operand1 * s->operand2);	
			if (verbose) 
				printf("%f\n", myresult);
			break;

		case 3:
			myresult = (s->operand1 / s->operand2);	
			if (verbose) 
				printf("%f\n", myresult);
			break;
	
		default:
			if (verbose) 
				printf ("Invalid operator: %d\n", s->operator);
			break;
	}
	return (&myresult);
}	


application_init_hook()

/* Initialization code goes in APPLICATION_INIT_HOOK.  It gets 
   called once after all of the TBServer initializations are done, 
   but before the main program loop is entered.  TBServer's init- 
   ialization includes all of the toolbox device managers and RPC. */

{
 
    WWInit() ;
	printf("Register Calc Server From Mac\n");
	printf("Calc: waiting for calls.\n");

	SetUpMenus();

    if(registerrpc(program_number,
				  version_number,
				  procedure_number,
				  my_calculator,
				  xdr_mystruct,
				  xdr_float) != 0) {
	 printf("Error in registerrpc \n");
   }
}


void application_run_hook()	

/* Code that normally goes in the main event loop should be put into
   APPLICATION_RUN_HOOK.  It gets called each time through TBServer's 
   main event loop.  */

{
  short whichPart;
  WindowPtr whichWindow;
  EventRecord event;
  
  if(WaitNextEvent(everyEvent, &event, RPC_SLEEP_TIME, NULL))
	{
	  switch(event.what)
		{
		case mouseDown:
		  whichPart = FindWindow(event.where, &whichWindow);
		  if(whichWindow == gDebugWindowPtr)
			WWMouseDown(whichPart,event.where,event.modifiers);
		  else
			switch(whichPart)
			  {
			  case inSysWindow:	
				SystemClick(&event, whichWindow);
				break;
			  case inMenuBar:
				DoCommand(MenuSelect(event.where));
				break;
			  default:			  
				break;
			  }
		case activateEvt:
		  if(((WindowPtr)event.message) == gDebugWindowPtr)
			WWActivateEvent(event.modifiers);
		  break;
		case updateEvt:
		  if(((WindowPtr) event.message) == gDebugWindowPtr) WWUpdateEvent();
		  break;
		default:
		  break;
		}
	}
	if (doneFlag)
		exit(0);
}


void application_cleanup_hook(error_code)

/* Cleanup code goes in APPLICATION_CLEANUP_HOOK.  This function
   is called from the tbserver's function Exit, which gets called 
   on normal shutdown of the application and on certain error 
   conditions.  Error_code is the value that was passed to Exit() 
   by the calling function.  */

	int	error_code;
{
	if (verbose) 
		printf("Application Cleanup Hook\n");
	svc_unregister(program_number, version_number);
	if (verbose) 
		printf("Register program terminating, error = %d\n", error_code);

}
