/* 
 * client - send & receive messages to/from server.
 *
 * Programming Assignment #8.
 *
 * CSc 645  Spring 1995  Jim Warhol
 *
 * client sends a series of messages to a server.
 *
 * The client and server may use either datagram or a reliable stream
 * connection messaging method, depending on which remote procedure call 
 * library routines are used to compile the programs.
 *
 * A random delay in the range [0..MAXDELAY] seconds is effected before
 * many shared-memory server communications (SM_init(), Tread(), Twrite() & 
 * SM_term()), to ensure all concurrent client processes do not issue all 
 * their requests simultaneously. This improves the likelihood that the 
 * server will be processing data for multiple clients at the same time.
 *
 * Compile with: cc -o Apps/client client.c common.c SMlib.c RPCdg.c
 *    or
 *               cc -o Apps/client client.c common.c SMlib.c RPCst.c
 *
 * Execute with: client hostname portnum numclients
 *
 * Where hostname is the name of the remote host on which the server is 
 * executing, portnum is the port number of the server on that host, and
 * numclients is the number of clients that are/will be executing.
 *
 * Related components: startup.c, server.c, common.h/common.c, SMlib.c, 
 *                     and RPGdg.c/RPGst.c.
 *
 */

#include "common.h"

/* client macro and compile-time definitions */

#define	delay_random_time() sleep(random() % (MAXDELAY + 1))
					/* sleep for [0..MAXDELAY] seconds */
#define END_SORT	2		/* end sort flag */
#define MONITOR		numclients	/* client ID of client monitor */
#define START_SORT	1		/* start sort flag */
#define wait_until(cond) do sleep(1); while (!(cond)); /* wait for condition */

/* client function prototypes */

void execute_monitor();			/* execute client monitor process */
void execute_sort_client();		/* execute client sort process */
void initialize(int argc, char *argv[]); /* initialize client program */
void print_values(char *text);		/* print client sort mem. values */
int shared_X(int which);		/* call Tread for X value */
int shared_Y(int which);		/* call Tread for Y value */

/* data definitions */

char *hostname;				/* pointer to host name */
int portnum;				/* port number for server */
int numclients;				/* number of clients started */
int sortcount;				/* number of sort values */
int thisX, thisY;			/* shared memory values for this
					   sort client */
int previousY;				/* used by sort client */

int clientID;				/* client ID assigned by server */
char *response;				/* pointer to response from server */

int datum1, datum2;			/* values to/from SM server */
int values[MAXCLIENTS][2];		/* recent client values read */
int currentID;				/* current client being processed */

char strbuf[150];			/* string buffer */

/* main program - client */

void main(int argc, char *argv[])
{
   /* initialize client process - set hostname, portnum & numclients */

   initialize(argc, argv);

   /* create connection to server */

   delay_random_time();			/* wait a random time */
   printns("  client: Initiating communication with port #%d on host %s.\n", 
      portnum, hostname);
   clientID = SM_init(hostname, portnum); /* init. SM client processing */
   printn("  client: Assigned client ID #%d.\n", clientID);

   /* send test messages to the server and print responses */

   sortcount = numclients - 1;		/* set number of sort values */
   if (clientID == MONITOR)
      execute_monitor();		/* last client is monitor */
   else
      execute_sort_client();

   /* terminate shared memory access and end program */

   printn("  client%d: terminating shared memory processing.\n", clientID);
   SM_term();
   exit(0);
}

/*
 * void execute_monitor();
 *
 */

void execute_monitor()
{
   if (sortcount == 0)
      printn("  client%d: There are no other clients scheduled.\n", clientID);
   else /* wait for remaining clients to start and write data */
      printnnn("  client%d: Client ID is %d of %d; this client will monitor "
         "the other clients.\n", clientID, numclients, numclients);

   /* wait for clients [1..sortcount] (if any) to start up and write data */

   for (currentID = 1; currentID <= sortcount; currentID++) {
      do {
         sleep(1);		/* wait for client to start/write # */
         st = Tread(currentID, &values[currentID][0], &values[currentID][1]);
      } while (st != SM_request_ok);
      printnn("  client%d: Client ID #%d has started and written data.\n",
         clientID, currentID);
   }
   printnn("  client%d: All %d client sort processes have started and "
      "written data.\n", clientID, sortcount);

   /* print all sort client's initial (unsorted) shared memory values */

   print_values("  client%d: Original values:  ");

   /* now write start status to shared memory as flag to indicate to
      client sort processes to begin sort */

   printn("  client%d: Writing start-sort flag to shared memory to "
      "initiate sort.\n", clientID);
   Twrite(START_SORT, 0);		/* write start flag to shared memory */

   /* wait for shared memory values to be sorted */

   do {
      sleep(4);				/* just give sort a chance */
      printnn("  client%d: Checking to see if all %d values are sorted yet.\n",
         clientID, sortcount);
      sprintf(strbuf, "  client%d: Values checked:  ", clientID);
      previousY = i = -999999;
      for (currentID=1; currentID <= sortcount && previousY <= i; currentID++) {
         previousY = i;
         st = Tread(currentID, &values[currentID][0], &values[currentID][1]);
         if (st != SM_request_ok)
            errornn("  client%d: Unexpected status %d from Tread.\n",
               clientID, st);
         sprintf(&strbuf[strlen(strbuf)], "[%d]=(%d,%d), ", currentID,
            values[currentID][0], values[currentID][1]);
         if (values[currentID][0] == 0)
            i = values[currentID][1];	/* value to test */
         else
            i = previousY - 1;		/* not done - force loop failure */
      }
      sprintf(&strbuf[strlen(strbuf)-2], ".\n");
      print(strbuf);			/* print out sort values */
      if (currentID > sortcount)
         printn("  client%d:    Values are sorted!\n", clientID);
      else
         printn("  client%d:    Values are not yet sorted.\n", clientID);
   } while (currentID <= sortcount);

   /* now write stop status to shared memory as flag to indicate to
      client sort processes to terminate */

   printn("  client%d: Writing stop-sort flag to shared memory to "
      "terminate sort.\n", clientID);
   Twrite(END_SORT, 0);		/* write stop flag to shared memory */

   /* wait until all client sort processes have terminated */

   sleep(1);				/* give them a bit of time */
   for (currentID = 1; currentID <= sortcount; currentID++) {
      do {
         sleep(1);		/* wait for client to start/write # */
         st = Tread(currentID, &datum1, &datum2);
      } while (st != SM_inactive_ID);	/* wait for client currentID to stop */
      printnn("  client%d: Client ID #%d has terminated.\n", clientID, 
         currentID);
   }
   printnn("  client%d: All %d client sort processes have stopped.\n",
      clientID, sortcount);

   /* print all sorted shared memory values */

   print_values("  client%d: Sorted values:   ");

   /* terminate client monitor process */

   printn("  client%d: Terminating client monitor process.\n", clientID);
   return;
}

/*
 * void execute_sort_client();
 *
 */

void execute_sort_client()
{
   printnnn("  client%d: This is client %d of %d; it will be a client sort "
      "process.\n", clientID, clientID, numclients);

   /* write 0 and random number to shared memory; signals to monitor 
      that this client process is ready to start sort */

   delay_random_time();			/* wait a random time */
   i = random() % 100 + 1;		/* pick number in range [1..100] */
   printnn("  client%d: Writing (0,%d) to shared memory.\n", clientID, i);
   thisX = 0; thisY = i;
   Twrite(thisX, thisY);		/* write to shared memory */

   /* wait for client monitor process to start up and write start flag 
      (or possibly the stop flag, depending on timing) */

   sleep(3);				/* give it a fair chance */
   while (Tread(MONITOR, &datum1, &datum2) != SM_request_ok) sleep(2);

   /* start sort algorithm - continue until client monitor writes stop flag */

   do {

      if (clientID < sortcount && shared_X(clientID+1) == -1) {
         if (thisY != (i = shared_Y(clientID+1)))
            errornnn("  client%d: Expected to find %d, not %d, in "
               "succeeding shared memory value.", clientID, thisY, i);
         thisY = thisX; thisX = 0;
         Twrite(thisX, thisY);
         wait_until(shared_X(clientID+1) != -1);
      }
      else if (clientID > 1 && thisX == 0 && shared_X(clientID-1) == thisY) {
         previousY = thisY;
         thisX = -1; thisY = shared_Y(clientID-1);
         Twrite(thisX, thisY);
         wait_until(shared_Y(clientID-1) == previousY);
         thisX = 0;
         Twrite(thisX, thisY);
      }
      else if (clientID < sortcount && (i = shared_Y(clientID+1)) < thisY)
         if (thisX != i) {
            thisX = i;
            Twrite(thisX, thisY);
         }

      /* check to see if client monitor wants to stop */

      sleep(1);				/* just to be cautious */
   } while(shared_X(MONITOR) != END_SORT); /* run until monitor says stop */

   /* terminate client sort process */

   printn("  client%d: Terminating client sort process.\n", clientID);
   return;
}

/*
 * void initialize(int argc, char *argv[]);
 *
 * initialize processes the client command parameters and initializes random 
 * number generation. The first client command parameter specifies the name
 * of the host on which the server resides; the second parameter indicates 
 * the port number of the server on that host; the third parameter contains
 * the number of clients that have been or will be started. These values are
 * stored in the global variables hostname, portnum & numclients.
 *
 */

void initialize(int argc, char *argv[])
{
   /* make sure correct parameters are specified */

   if (argc != 4) 
      error("  client: You must specify:\n   client hostname port# numclients");

   /* save address of host name string, port number and number of clients */

   hostname = argv[1];

   if (sscanf(argv[2], "%d", &portnum) != 1)
      error("  client: Error in port number parameter.");

   if (sscanf(argv[3], "%d", &numclients) != 1)
      error("  client: Error in number of clients parameter.");

   /* initialize random number generator */

   srandom((int) time());

   return;
}

/*
 * void print_values(char *text);
 *
 * print_values prints out the X and Y values for all client sort processes.
 *
 * text is a pointer to a string containing the initial text for the 
 * output line, which should begin with "  client%d:".
 *
 */

void print_values(char *text)
{
   sprintf(strbuf, text, clientID);
   for (currentID = 1; currentID <= sortcount; currentID++)
      sprintf(&strbuf[strlen(strbuf)], "[%d]=(%d,%d), ", currentID,
         values[currentID][0], values[currentID][1]);
   sprintf(&strbuf[strlen(strbuf)-2], ".\n");
   print(strbuf);			/* print out sort clients' values */
   return;
}

int shared_X(int which)
{
   st = Tread(which, &datum1, &datum2);
   if (st != SM_request_ok)
      errornn("  client%d/shared_X: Unexpected status %d returned from Tread.", 
         clientID, st);
   return datum1;
}

int shared_Y(int which)
{
   st = Tread(which, &datum1, &datum2);
   if (st != SM_request_ok)
      errornn("  client%d/shared_Y: Unexpected status %d returned from Tread.", 
         clientID, st);
   return datum2;
}
