/////////////////////////////////////////////////////////////////////////////
// calendar.cc
//
// SIMLIB version: 2.18
// Date: 2004-01-25
//
// Copyright (c) 1991-2004 Petr Peringer 
//
// This library is licensed under GNU Library GPL. See the file COPYING.
//

//
// description: implementation of class Calendar
// uses double-linked list 
//

////////////////////////////////////////////////////////////////////////////
// interface
//

#include "simlib.h"
#include "internal.h"


////////////////////////////////////////////////////////////////////////////
// implementation
//

SIMLIB_IMPLEMENTATION

class  Calendar;       // (internal)

/////////////////////////////////////////////////////////////////////////////
// EventNotice --- calendar item
//
struct EventNotice {
    EventNotice * pred;         // previous object in list
    EventNotice * succ;         // next object in list
    Entity * entity;            // which entity is scheduled
    double time;                // reactivation time of entity
    Entity::Priority_t priority; // priority at time of scheduling

    EventNotice() : 
        pred(this), succ(this), 
        entity(0), time(0), priority(0) 
    {} // (for Calendar init)
    EventNotice(Entity *p, double t) :
        pred(this), succ(this),  // NOT linked
        entity(p),               // connect: entity
        time(t),                 // reactivation time
        priority(p->Priority)    // priority at time of scheduling
    {
        entity->_Ev = this;      // connect: this activation record
    }
    ~EventNotice() {
        if(pred!=this) {        // remove from calendar
            Unlink();
            entity->_Ev = 0;    // disconect: entity link
	}
    }
    void Unlink() {             // remove from calendar list
        pred->succ = succ; 
        succ->pred = pred;
        pred = succ = this;     // NOT linked
    }
    void insert(EventNotice * s) { 
        if(pred!=this) Unlink(); // is in calendar, remove first
        // --- insert before item s
        succ = s; 
        pred = s->pred; 
        pred->succ = this; 
        s->pred = this; 
    }
  private:
    EventNotice(const EventNotice&); // disable
    void operator=(const EventNotice&); 
};


////////////////////////////////////////////////////////////////////////////
// class Calendar --- list implementation of calendar
//    +-----------------------------------------------+
//    |   +---+                    +---+      +---+   |
//    +-->|   |<--    ......    -->|   |<---->|   |<--+
//     +--+   +---+                +---+      +---+
//     | Calendar |
//     +----------+
// double-linked circular list (faster than std::list)
//
class Calendar : private EventNotice {
  public:
    class iterator { // bidirectional iterator
      EventNotice *p;
      public:
      iterator(EventNotice*pos) : p(pos) {}
      // only prefix version
      iterator &operator++() { p = p->succ; return *this; }
      iterator &operator--() { p = p->pred; return *this; }
      // implicit operator = and copy constructor OK
      EventNotice * operator*() { return p; }
      bool operator != (iterator q) { return p!=q.p; }
      bool operator == (iterator q) { return p==q.p; }
    }; // iterator
    iterator begin()    { return succ; }
    iterator end()      { return this; }
    bool empty()        { return begin() == end(); }    
    ///////////////////////////////////////////////////////////
    EventNotice *EvNot(Entity *p, double t); // create/reuse EventNotice
    static double ActivationTime(Entity *e) { // activation time
        return ((EventNotice*)e->_Ev)->time;    // e!=0 ###
    }
    void ScheduleFirst(Entity *p);
    void ScheduleAt(Entity *p, double t);
    Entity *Current();                   // active process/event
    Entity *Get(Entity *p);              // remove process p from calendar
    Entity *GetFirst() {  // remove first from calendar
       if(empty()) SIMLIB_internal_error(); // empty
       EventNotice *first = *begin();
       Entity *e = first->entity;
       delete first;    // remove from list, delete
       return e;
    }
    void PredIns(EventNotice *what, iterator pos) {
       what->insert(*pos);
    }
    static void clear(bool destroy=false); // remove/destroy all items
    static void create() {  // create single instance
        dprintf(("Calendar::create()")); 
        if(instance==0) instance = new Calendar; 
	else       SIMLIB_error(DuplicateCalendar);
        SIMLIB_atexit(destroy); // last SIMLIB module cleanup calls this
        _SetTime(NextTime,SIMLIB_MAXTIME);
    }
    static void destroy() {  // destroy single instance
        dprintf(("Calendar::destroy()")); 
        clear(true);         // remove all contents
	delete instance;
	instance = 0;
    }
 private:
    Calendar()  { dprintf(("Calendar::Calendar()")); }
    ~Calendar() { dprintf(("Calendar::~Calendar()")); }
    iterator Find(EventNotice *value);
public: ///###remove
    static Calendar *instance;  // unique calendar pointer
#ifndef NDEBUG
    friend void Calendar_print();      // print of calendar contents
#endif
};

Calendar *Calendar::instance = 0;  // unique calendar pointer


////////////////////////////////////////////////////////////////////////////
// public INTERFACE = exported functions...
//
double SQS::ActivationTime(Entity *e) {   // for Facility service interrupt
  if(e->Idle()) SIMLIB_internal_error();  // passive entity
  return Calendar::ActivationTime(e);
}

void SQS::ScheduleFirst(Entity *e) {      // special for Process::Interrupt
  if(Calendar::instance==0) Calendar::create();
  Calendar::instance->ScheduleFirst(e);
}

void SQS::ScheduleAt(Entity *e, double t) { // schedule
  if(Calendar::instance==0) Calendar::create();
  Calendar::instance->ScheduleAt(e,t);
}

Entity *SQS::Get(Entity *e) {             // remove entity p
  if(Calendar::instance==0) Calendar::create();
  return Calendar::instance->Get(e);
}

void SQS::Clear() {                       // remove all
  if(Calendar::instance==0) Calendar::create();
  Calendar::clear(true);
}

bool SQS::Empty() {                       // used by Run()
  if(Calendar::instance==0) Calendar::create();
  return Calendar::instance->empty();
}

Entity *SQS::Current() {                  // used by Run()
  if(Calendar::instance==0) Calendar::create();
  return Calendar::instance->Current();
}

////////////////////////////////////////////////////////////////////////////
// Calendar implementation
//

#define CHECKENTITY(eptr)  if(!eptr) SIMLIB_error(EntityRefError)
#define CHECKTIME(t)       if(t<Time) SIMLIB_error(SchedulingBeforeTime)
#define CHECKSCHEDULED(e)  if(!e->_Ev) SIMLIB_error(EntityIsNotScheduled)


////////////////////////////////////////////////////////////////////////////
// EvNot - creates the EventNotice
//
inline EventNotice *Calendar::EvNot(Entity *p, double t)
{
  register EventNotice *evn = (EventNotice*)p->_Ev;

  if(evn) { // is scheduled already --- REUSE
    evn->Unlink(); // remove from calendar list, ### should be connected
    evn->time = t;             // change reactivation time
    evn->priority = p->Priority; // set priority
  }
  else {
    evn = new EventNotice(p,t);
  }
//  dprintf(("newCalendarEntity(ent=%p,t=%g,prio=%u) %p",
//                p, evn->time, evn->priority, this));
  return evn;
}

////////////////////////////////////////////////////////////////////////////
// Find --- find insert position (Time,priority) // higher priority first
//
inline Calendar::iterator Calendar::Find(EventNotice *en)
{
  Entity::Priority_t prio = en->priority;
  double Time = en->time;
  if(empty()) 
      return end();
  register iterator evn = --end();               // search from back
  while(evn!=end() && (*evn)->time > Time) {     // search time
    --evn;
  }
  while(evn!=end() && (*evn)->time==Time &&      // equal time...
        (*evn)->priority < prio) {               // ...search priority 
    --evn;
  }
  ++evn;
  return evn;  // return insert position or end()
}

////////////////////////////////////////////////////////////////////////////
//  ScheduleAt - schedule entity e at time t
//
void Calendar::ScheduleAt(Entity *e, double t)
{
  dprintf(("Calendar::ScheduleAt(%s,%g)", e->Name(), t));
  CHECKENTITY(e);
  CHECKTIME(t);
  EventNotice *evn = EvNot(e,t);
  iterator pos = Find(evn);
  PredIns( evn, pos );
  // if first item inserted:
  _SetTime(NextTime, (*begin())->time);
}


////////////////////////////////////////////////////////////////////////////
//  ScheduleFirst - schedule entity e -- first in calendar
//
void Calendar::ScheduleFirst(Entity *e)
{
  dprintf(("Calendar::ScheduleFirst(%s)", e->Name()));
  CHECKENTITY(e);
  EventNotice *evn = EvNot(e,Time);
  PredIns( evn, begin() );
  _SetTime(NextTime, evn->time);
}

////////////////////////////////////////////////////////////////////////////
//  Current - pointer to first -- active entity
//
Entity *Calendar::Current()
{
  if(empty()) SIMLIB_error(EmptyCalendar);
  return (*begin())->entity;
}

////////////////////////////////////////////////////////////////////////////
//  Get - remove entity e from calendar
//
Entity *Calendar::Get(Entity *e)
{
  if(empty()) SIMLIB_error(EmptyCalendar);
  CHECKSCHEDULED(e);
  delete (EventNotice*)e->_Ev;   // disconnect, remove item
  if(empty())   _SetTime(NextTime, SIMLIB_MAXTIME);
  else          _SetTime(NextTime, (*begin())->time);
  return e;
}

////////////////////////////////////////////////////////////////////////////
//  Clear --- remove all event notices, destroy
//
void Calendar::clear(bool destroy)
{
  dprintf(("Calendar::clear(%s)", destroy?"true":"false"));
  while(!instance->empty()) {
    Entity *p = instance->GetFirst();
    if (destroy && p->IsAllocated()) delete p; // delete entity
  }
  _SetTime(NextTime, SIMLIB_MAXTIME);  // no scheduled event
}

#ifndef NDEBUG
////////////////////////////////////////////////////////////////////////////
void Calendar_print()      // print of calendar contents
{
  Print("Calendar (SQS):\n");
  if(Calendar::instance==0) return;
  Calendar::iterator p = Calendar::instance->begin();
  Calendar::iterator end = Calendar::instance->end();
  for( int u=1; p!=end; ++p, ++u ) {
    Print("  [%u]: \t", u );            // schedule time
    Print("%s\t", (*p)->entity->Name() );  // print entity ID
    Print("activation time = %g \n", (*p)->time ); // schedule time
  }
  Print("\n");
}
#endif

// end

