/* The Bakery Algorithm --- in MPI                  Author:  Tim Rolfe

      Customer:
          This process receives from the Controller a message giving
          the pastry to be purchased --- or an EXIT message.  It
          then requests a number from the Number Server, and uses
          that number (as message tag) to request services by the
          Clerk process, sending as the message the pastry to be
          purchased.  On receiving the answering message from the
          Clerk process, the Customer sends a message to the
          Controller indicating that it has finished the current
          assignment and is available for the next one.
*/
#include <iostream.h>
#include <unistd.h>     // sleep
#include <stdlib.h>     // atoi, rand & Co.
#include <string.h>     // for strcmp, strncmp
#include <time.h>       // time(NULL) for srand
#include <mpi.h>
/*
For your information, here's how MPI_Status is defined:
    int MPI_SOURCE;
    int MPI_TAG;
    int MPI_ERROR;
} MPI_Status;
*/

#define DEBUG

// Symbolic definitions for the MPI tags in use
#define INIT 0
// MPI tags 1 through 999 are for service requests
#define EXIT 9999

// Symbolic definitions for the ranks of the first three functionalities
#define CONTROL 0
#define NUMSERV 1
#define CLERK   2

void Customer( int Rank );   // Rank 3..(Size-1) processes

void main ( int argc, char* argv[] )
{
   int Rank, Size;
   int Ninit;
   MPI_Status Status;

   MPI_Init ( &argc, &argv );
   MPI_Comm_rank ( MPI_COMM_WORLD, &Rank );
   MPI_Comm_size ( MPI_COMM_WORLD, &Size );

   if ( Rank > 2 )           // Customer processes
      Customer ( Rank );
   else
      cout << "Customer process at Rank < 3" << endl;

   cout << "Finished with "
        << "Customer process" << endl;

   MPI_Barrier(MPI_COMM_WORLD);

   MPI_Finalize();
}

/* Customer process:  Controller identifies the pastry to be bought.
                      Then this process interacts with the number
   server and the clerk process to handle the request.

   Note that there are several places where the EXIT message might be
   received, so that each MPI_Recv is preceeded by an MPI_Probe to
   check.  When using MPI_Probe we don't need to know buffer informtion,
   which is required to use MPI_Recv.
*/
void Customer ( int Rank )   // In point of fact, we don't use the Rank
{
   MPI_Status Status;
   char Line[128];
   int  Ticket,
        MsgLen;

   while (1)       // Exit when Probe discovers an EXIT message
   {
//    First, get the information about the pastry from CONTROL
      MPI_Probe ( CONTROL, MPI_ANY_TAG, MPI_COMM_WORLD, &Status );
      if ( Status.MPI_TAG == EXIT )
         break;
      MPI_Recv( Line, 128, MPI_CHAR, CONTROL, INIT,
                MPI_COMM_WORLD, &Status );
      MsgLen = strlen(Line) + 1;

//    Then take a number from NUMSERV
      MPI_Send ( Line, 0, MPI_CHAR, NUMSERV, INIT,
                 MPI_COMM_WORLD );
      MPI_Probe ( MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &Status );
      if ( Status.MPI_TAG == EXIT )
         break;
      MPI_Recv( &Ticket, 1, MPI_INT, NUMSERV, INIT,
                MPI_COMM_WORLD, &Status );

//    Send the request for service to the CLERK, using the ticket
      MPI_Send ( Line, MsgLen, MPI_CHAR, CLERK, Ticket,
                 MPI_COMM_WORLD );

//    Wait for the CLERK to wake up and service our request
      MPI_Probe ( MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &Status );
      if ( Status.MPI_TAG == EXIT )
         break;
      MPI_Recv( Line, 128, MPI_CHAR, CLERK, Ticket,
                MPI_COMM_WORLD, &Status );

//    We will wait a second ourselves --- try to force serial messages
      sleep(1);
//    Notify CONTROL that we have finished
      MPI_Send( Line, MsgLen, MPI_CHAR, CONTROL, Ticket,
                MPI_COMM_WORLD );
   }
// Consume the EXIT message
   MPI_Recv(Line, 0, MPI_CHAR, CONTROL, EXIT,
            MPI_COMM_WORLD, &Status);
}
