#include <iostream>
#include <vector>
#include <queue>
#include <functional>
using namespace std;

#include "Random.h"

class Event {
public:
	enum {DIAL_IN=1, HANGUP};
	Event(int name=0, int tm=0, int type=DIAL_IN): time(tm), who(name), what(type) {
	}
	bool operator>(const Event& rhs) const {
		return time>rhs.time;
	}
	int getTime() const {
		return time;
	}
	int getType() const {
		return what;
	}
	int getUser() const {
		return who;
	}
private:
	int who;  // the number of the user
	int time; // when the event will occur
	int what; // DIAL_IN or HANGUP
};

typedef priority_queue<Event, vector<Event>, greater<Event> > PQ;

class ModemSim {
public:
	ModemSim(int modems, double avgLen, int callIntrvl);
	void nextCall(int delta);
	void runSim(int stoppingTime = INT_MAX);
private:
	Random r;                // A random source
	PQ eventSet;             // Pending events
	int freeModems;          // Number of modems unused
	const double avgCallLen; // Length of a call
	const int freqOfCalls;   // Interval between calls
};

ModemSim::ModemSim(int modems, double avgLen, int callIntrvl):
		freeModems(modems), avgCallLen(avgLen), freqOfCalls(callIntrvl) {
	nextCall(freqOfCalls);  // Schedule first call
}

void ModemSim::nextCall(int delta) {
	static int nextCallTime(0);
	static int userNum(0);

	eventSet.push(Event(userNum++, nextCallTime));
	nextCallTime+=delta;
}

void ModemSim::runSim(int stoppingTime) {
	while (!eventSet.empty()) {
		Event e(eventSet.top());
		eventSet.pop();
		if (e.getTime()>stoppingTime)
			break;
		if (e.getType()==Event::HANGUP) {
			freeModems++;
			cout<<"User "<<e.getUser()<<" hangs up at time "<<e.getTime()<<endl;
		}
		else {
			cout<<"User "<<e.getUser()<<" dials in at time "<<e.getTime()<<" ";
			if (freeModems>0) {
				freeModems--;
				int howLong(r.poisson(avgCallLen));
				cout<<"and connects for "<<howLong<<" minutes"<<endl;
				Event hangupEvent(e.getUser(), e.getTime()+howLong, Event::HANGUP);
				eventSet.push(hangupEvent);
			}
			else
				cout<<"but gets busy signal"<<endl;
			nextCall(freqOfCalls);
		}
	}
}

int main() {
	int numModems;
	int totalTime;
	double avgConnectTime;
	int dialInFrequency;

	cout<<"Enter number of modems, length of simulation,\n"
		 <<" average connect time, how often calls occur: ";
	cin>>numModems>>totalTime>>avgConnectTime>>dialInFrequency;
	ModemSim s(numModems, avgConnectTime, dialInFrequency);
	s.runSim(totalTime);
	cin.get();
	cin.get();
	return 0;
}

