//  Cars will enter and wait in line until the wash is not busy. The
//  first in line will then start the wash process. When it is done,
//  it will enter the line for the vacuum. When done, the car leaves.

#include "distrib.h"

#include <iostream>
#include <queue>

// enums can make your code much more readable.

typedef enum {ENTER, STARTWASH, ENDWASH, STARTVAC, ENDVAC} Event;

//  Note that the class car is defined completely here. There is no
//  separate implementation file. This simply indicates the
//  possibility but doesn't endorse it as a good idea.

class car {
public:  
  int id;
  Event nextevent;
  float nexteventtime;
  float entertime;
  int linelengthonentry;
  float startwashtime;
  float endwashtime;
  int linelengthforvac;
  float startvactime;
  float endvactime;
  car(int xid) {
    id = xid;
    nextevent = ENTER;
    nexteventtime = rand_uniform(0, 300); // Let cars arrive at random
                                          // over a period of 300 minutes.
  }
  friend bool operator<(car x, car y);
};

// We'll need '<' defined to use priority queues from STL.

bool operator<(car x, car y)
{
  if (y.nexteventtime < x.nexteventtime)
    return 1;
  else
    return 0;
}

main()
{
  float clock = 0;
  car currentcar(0);
  // Declare a priority queue 'eventq' and two regular queues washline
  // and vacline.
  priority_queue<car> eventq;
  queue<car> washline;
  queue<car> vacline;
  bool washbusy = 0;
  bool vacbusy = 0;

  // build cars for entry
  for (int i=0; i<100; i++)
    eventq.push(i);
    // Put car i into the priority queue.
  while (clock < 300 && !eventq.empty() )
    {
      // check wash line
      if ( !washline.empty() && !washbusy)
	{
	  // Put the car in front of the line in currentcar and remove
	  // it from the line.
          currentcar = washline.front();
          washline.pop();
	  currentcar.startwashtime = clock;
	  currentcar.nexteventtime = clock;
	  currentcar.nextevent = STARTWASH;
	  // Put current car back into the priority queue with its new times.
          eventq.push( currentcar );
	  washbusy = 1;
	}
      // check vac line
      if ( !vacline.empty() && !vacbusy)
	{
	  // Put the car in front of the line in currentcar and remove
	  // it from the line.
          currentcar = vacline.front();
          vacline.pop();
	  currentcar.nextevent = STARTVAC;
	  currentcar.startvactime = clock;
	  currentcar.nexteventtime = clock;
	  vacbusy = 1;
	  // Put current car back into the priority queue with its new times.
          eventq.push( currentcar );
	}
      
      // Remove a car from the priority queue and put it in current car.
      currentcar = eventq.top();
      eventq.pop();
      clock = currentcar.nexteventtime;
      switch(currentcar.nextevent)
	{
	case ENTER:
	  currentcar.entertime = clock;
	  currentcar.linelengthonentry = washline.size();
	  // Put current car into the wash line
          washline.push( currentcar );
	  break;
	case STARTWASH:
	  currentcar.startwashtime = clock;
	  currentcar.nextevent = ENDWASH;
	  // Assume it takes an average of 4 minutes (stdev = 1) to wash a car.
	  currentcar.nexteventtime = clock + rand_normal(4.0, 1.0);
	  // Put currentcar back in the priority queue with new times.
          eventq.push( currentcar );
	  break;
	case ENDWASH:
	  currentcar.endwashtime = clock;
	  currentcar.nextevent = STARTVAC;
	  currentcar.linelengthforvac = vacline.size();
	  // Put current car into the vacuum line.
          vacline.push( currentcar );
	  washbusy = 0;
	  break;
	case STARTVAC:
	  currentcar.startvactime = clock;
	  currentcar.nextevent = ENDVAC;
	  // Assume it takes and average of 4 minutes (stdev = 1) to
	  // vacuum a car.
	  currentcar.nexteventtime = clock + rand_normal(4.0, 1.0);
	  // Put currentcar back in the priority queue with new times.
          eventq.push( currentcar );
	  break;
	case ENDVAC:
	  currentcar.endvactime = clock;
	  cout << currentcar.entertime << "\t"
	       << currentcar.linelengthonentry << "\t" 
	       << currentcar.startwashtime << "\t"
	       << currentcar.endwashtime << "\t"
	       << currentcar.linelengthforvac << "\t"
	       << currentcar.startvactime << "\t"
	       << currentcar.endvactime 
	       << endl;
	  vacbusy = 0;
	  break;
	default:
	  cout << "Bad event" << endl;
	  exit(1);
	}
    }
}
