#include "discsim.h"
#include <stdlib.h>

// Some underlying functions

void oosError(char* Err)
{
    cerr<<"Simulation terminated with error\n"<<Err<<'\n';
    exit(EXIT_FAILURE);
}

void error(char* Err)
{
    cout<<"Error reported "<<Err<<'\n';
    oosError(Err);
}

void error(char* Err1,char* Err2)
{
    cout<<"Error reported "<<Err1<<" "<<Err2<<'\n';
    oosError(Err1);
}

void trace(char* Mess)
{
    cout<<Mess<<" at "<<'\n';
}

void trace(char* Mess,float When)
{
    cout<<Mess<<" "<<When<<'\n';
}

// These parts define the statistical collection devices

//class Statistic: public mylink

//protected:
  
Statistic::Statistic(char* t)
{
  my_Title = t;
  observations = 0;
}


// class Report: public mylink

//public:

void Report::report()
{
  mylink * s;
  cout<<my_Title<<'\n';
  s = (stats.first());
  while((s&&(mylinkage*)s!=(mylinkage*)(&stats))){
    ((Statistic *)s)->report();
    s = (mylink*)(s->succ());
  }
}

void Report::add_to(Statistic* s)
{
	s->into(&stats);
}

Report::Report(const char* t)
{
  my_Title = t;
}

// Here's a simple integer statistic

// class Count: public Statistic

//public:

Count::Count(char* t):Statistic(t){
  total = 0;
}

void Count::update(float inc)
{
  total = total + (int)inc;
}

   void Count::report()
{
  cout<< my_Title<<"  "<<total<<'\n';
}

   void Count::reset()
{
  observations = 0;
  total = 0;
}

// Here's a simple floating point statistic

//class Average: public Statistic

//public:

Average::Average(char* t):Statistic(t)
{
  sum = 0;
}

void Average::update(float inc)
{
  observations++;
  sum += inc;
}

void Average::report()
{
  cout<< my_Title<<" Observations "<<observations<<" Sum "<<sum;
  cout<<" Mean "<<sum/observations<<'\n';
}

void Average::reset()
{
  observations = 0;
  sum = 0;
}


// These parts define the PassiveEntity stuff

// class PassiveEntity

PassiveEntity::PassiveEntity(char* t)
{
  title = t;
}

// class Resource: public PassiveEntity

//public:

void Resource::acquire(ActiveEntity* a)
{
  if(avail){
  a->trace("acquires successfully");
   avail=0; 
   a->reschedule();
   }
   else 
   {
   a->trace("fails to acquire");
   blocked.append(a);
   }
}

void Resource::release()
{
  if(blocked.empty()) 
    avail=1; 
  else 
    ((ActiveEntity*)blocked.out())->reschedule();
}

Resource::Resource(char* t):PassiveEntity(t)
{
	avail = 1;
}


// The ActiveEntity stuff

// class TimeOrderedList : public Collection

//public:

void TimeOrderedList::addInOrder(ActiveEntity* n)
{
  mylink* t;
  if(empty()) n->into(this); 
  else{
  	t = first();
  	while(n->getTime()>((ActiveEntity*)t)->getTime())
  	{
      t = (mylink*)(t->succ());
      if((mylinkage*)t==this) break;
  	}
  	if(t!=(mylinkage*)this) add_before(t,n); 
  	else append(n);
  }
}

// class ActiveEntity: public mylink

//protected:

ActiveEntity::ActiveEntity(Scheduler * sched, char* t, float when)
{
  title = t;
  nextEvent = 0;
  s = sched;
  trace("created");
  evTime = s->now()+when;
  s->wait(this);
}

//public:

float ActiveEntity::getTime()
{
  return evTime;
}

void ActiveEntity::trace(char* m)
{
  	cout<<s->now()<<" "<<title<<" "<<m<<'\n';
}


void ActiveEntity::reschedule()
{
	evTime = s->now();
  	s->wait(this);
}

//The Scheduler

//class Scheduler

//public:

float Scheduler::now(){
	return s_now;
}

Scheduler::Scheduler(Collection* repts)
{
	reports = repts;
	s_now = 0;
}

void Scheduler::wait(ActiveEntity* a)
{//a->trace("adding");
  evList.addInOrder(a);//a->trace("added");
}

void Scheduler::run(float runTime)
{
	mylinkage * r;
  while(!evList.empty() && s_now<=runTime){
    s_now = ((ActiveEntity*)evList.first())->getTime();
    ((ActiveEntity*)evList.out())->do_it();
  } 
  cout<<"Run finished"<<'\n';
  r = reports->first();
  while(r && r!=(mylinkage*)reports)
  {
  	((Report*)r)->report();
  	r = r->succ();
  }
}

