///////////////////////////////////////////////////////////////////////
// Exponentially distributed client arrival simulation
// For the current code there is no server, clients simply balk.
///////////////////////////////////////////////////////////////////////

#include "iostream.h"
#include "string.h"

#ifndef GNUGPP
 #ifndef BOOL_DEFN
  #include "bool.h"
 #endif
#endif

#include "ADT.h"
#include "trandom.h"

#define CHATTY 1
#define MAX_WAIT 10.0
const double arrivalRate=0.1; // = mean # of arrivals per sec (clock step of 1)

////////////////////////////////////////////////////////////////////////////
// Declaration of the form of items, and the ordering, in the priority queue
////////////////////////////////////////////////////////////////////////////

//----------------------------------------------------------------------
// Events--the general class on which the specific event types are based
//----------------------------------------------------------------------

class eventClass {
        double localTime;
public:
        eventClass (double T = 0.0) {localTime = T;};
        inline double whatTime () {return localTime;}; // when it happens
        inline void setTime (const double newTime = 0) {localTime = newTime;};
        virtual void makeItHappen () {}; // the event routine
        virtual const char* blurb () {return "";}; // identifier for the event
};

class earlier {
public:
 static bool lt(eventClass, eventClass);
 static bool lt(eventClass*, eventClass*);
};

// Comparison operator for eventClasses (temporal ordering).
bool earlier::lt(eventClass a, eventClass b) 
     { return a.whatTime() < b.whatTime();};
bool earlier::lt(eventClass * pa, eventClass * pb) 
     { return pa->whatTime() < pb->whatTime();};

//---------------------------------------------------------------------------
// Entities: clients 
//---------------------------------------------------------------------------
enum wait_status { WAITING, BALKED, SERVED };

class clientClass {
private:
        double localArrivalTime;
        double localBalkTime;
public:
        wait_status wstatus;
        void setTimes (double T) {localArrivalTime = T;
                                 localBalkTime = T+MAX_WAIT;};
        double getArrivalTime () {return localArrivalTime;};
        double getBalkTime () {return localBalkTime;};

        // generate the length of time between successive arrivals.
        double interArrivalTime () {return Random.Exponential(arrivalRate);};
};

//---------------------------------------------------------------------------
// Entities: events 
//---------------------------------------------------------------------------
class arrivalClass :public eventClass {
public:
        arrivalClass (double T = 0) : eventClass (T) {};
        interArrivalTime ();
        void makeItHappen ();
        const char * blurb ();
};

class balkClass :public eventClass {
public:
        clientClass * client;
        balkClass (double T = 0) : eventClass (T) {};
        void makeItHappen ();
        const char * blurb ();
};

class allDoneClass : public eventClass {
public:
        allDoneClass (double T) : eventClass (T) {};
        void makeItHappen ();
        const char * blurb ();
};

////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
// Global variables
///////////////////////////////////////////////////////////

double currentTime  = 0.0;

// Make a global heap of events ordered by time using earlier::lt
Heap<eventClass *, earlier> eventHeap;

// Make a global queue of clients
Queue<clientClass *> clientQueue;

////////////////////////////////////////////////////////////
// Event routines
///////////////////////////////////////////////////////////

void allDoneClass::makeItHappen() 
{
  cout << "time: " << whatTime() << ": ";
  cout << "Event: " << blurb() << "\n";
}

const char * allDoneClass::blurb() 
{
  return " Simulation reaches end time.";
}

void arrivalClass::makeItHappen ()
// The client arrival event routine
{

 // Create and initialize a new client record
 clientClass * arrivingClient = new clientClass;
 arrivingClient->setTimes(currentTime);

 // Push client on queue
 clientQueue.push( arrivingClient );
 arrivingClient->wstatus = WAITING;

 if (CHATTY) {
  cout << "time: " << currentTime << ": ";
  cout << "Event: Client arrival.\n";
 }

 // Post this client's  balk event on eventHeap
 balkClass * balkEvent = new balkClass;
 balkEvent->client = arrivingClient;
 balkEvent->setTime(arrivingClient->getBalkTime());
 eventHeap.push(balkEvent);

 // Post next arrivalClass event on eventHeap (use current object "this")
 setTime (currentTime + arrivingClient->interArrivalTime());
 eventHeap.push(this);

}

const char* arrivalClass::blurb ()
{
 return "Client arrives.";
}


void balkClass::makeItHappen ()
// The client balks (leaves queue without being served)
{

 client->wstatus = BALKED;

 if (CHATTY) {
  cout << "time: " << currentTime << ": ";
  cout << "Event: Client tArr = " << client->getArrivalTime() << " balks\n";
 }

 // Do not delete the client record until it is popped from
 // the clientQueue.  
}

const char* balkClass::blurb ()
{
 return "Client balks.";
}


void startSimulation(double endTime)
{
 // Put simulation termination event on event heap.
 allDoneClass * endSimulation = new allDoneClass(endTime);
 eventHeap.push(endSimulation);

 // Do first arrival (from time 0)
 arrivalClass * firstArrival = new arrivalClass;
 firstArrival->setTime(Random.Exponential(2.0*arrivalRate));
 eventHeap.push(firstArrival);
 
 if (CHATTY)
  cout << "time: " << 0.0 << ": Start simulation\n";
}


int main()
{
 double endTime = 100.0;
 eventClass * currentEvent;
 eventClass ** pCurrentEvent;



 if (CHATTY) {
  cout << "\n========================\n";
  cout << "==   Echo All Events  ==\n";
  cout << "========================\n";
 }

 startSimulation(endTime);

 while (currentTime < endTime) {
 
  pCurrentEvent = eventHeap.pop();
  if (pCurrentEvent == NULL) {
   cout << " Error: Event heap empty\n";
   break;
  }
  currentEvent = *pCurrentEvent;
  currentTime = currentEvent->whatTime();
  currentEvent -> makeItHappen();
 }

 if (CHATTY) {
  cout << "\n====================================\n";
  cout << "==   Echo Remaining Client Queue  ==\n";
  cout << "====================================\n";

  clientClass ** pClient;
  while ((pClient = clientQueue.pop()) != NULL) {
   clientClass * client = *pClient;
   cout << "Client tArr = " << client->getArrivalTime();
   switch (client->wstatus) {
    case WAITING:
     cout << ", waiting.\n";
     break;
    case BALKED:
     cout << ", balked.\n";
     break;
    case SERVED:
     cout << ", served (Error: should have been popped off queue).\n";
   }
  }

  cout << "\n======================================\n";
  cout << "==   Echo Remaining Events on Heap  ==\n";
  cout << "======================================\n";

  while ((pCurrentEvent = eventHeap.pop()) != NULL) {
   currentEvent = *pCurrentEvent;
   cout << "Event tArr = " << currentEvent->whatTime();
   cout << ", " << currentEvent->blurb() << "\n";
  }

 }

 return 0;
}

