#ifndef BABBLEMUD_EVENTS_H
#define BABBLEMUD_EVENTS_H

#include "thing.h"

#include <vector>
#include <queue>

const int GetMillisecondsTime ();

// event object
class Event : public thing
  {

  public:

  ThingMap  sub_;                          // for error subsitutions
  
  Event (const string & type,              // what sort of event it is
         const int interval = 1000,        // default, 1 second
         const int count = 1,              // default, fire once
         const int sequence = 0,           // default, unsequenced
         const thing & data = thing ()) :  // core event data from supplied thing
      interval_ (interval),                // time interval
      count_    (count),                   // whether to keep doing it
      when_     (GetMillisecondsTime ()),  // set last fired time to now
      instance_ (0),
      sequence_ (sequence)
      { 
      thing::operator= (data);     // copy core data (is this the best way?)
      if (!type.empty ())          // type may be in data thing
        sets ("type", type);       // if not, remember event type in thing
      Reschedule ();  // calculate when it first fires
      };

  // operator< (for sorting)
  inline const bool operator< (const Event & rhs) const
    {
    // we compare > because the sooner events have the
    // higher priority
    if  (when_ == rhs.when_)
      return  sequence_ > rhs.sequence_;  // sequence order if times same
    else
      return  when_ > rhs.when_;          // time order
    };

  // call to reschedule it - add interval to last fired time
  // so as to avoid event creep
  inline void Reschedule () 
    { 
    when_ += interval_; 
    };

  inline void Fired ()
    {
    ++instance_;
    if (count_ > 0)   // one less time to go
      --count_;
    };

  // get values
  inline const int GetTime     () const { return when_; };
  inline const int GetInterval () const { return interval_; };
  inline const int GetInstance () const { return instance_; };
  inline const int GetCount    () const { return count_; };

  // change number of times to go till we fire (0 means delete it)
  void SetCount (const int count = 0) { count_ = count; }

  protected:

  int  interval_;  // seconds until next one
  int  count_;     // number of times to fire (from now), -1 for indefinite

  private:

  int  when_;       // when event fires
  int  instance_;   // how many times it fired
  int  sequence_;   // what order to sequence events with the same fire time

  };  // end of class Event

// for adding events to a priority_queue
struct event_less : binary_function<const Event &, const Event &, bool> 
  {
  inline bool operator() (const Event & X, const Event & Y) const
    {
    return (X < Y); 
    }
  };

typedef priority_queue<Event, vector<Event>, event_less > EventQueue;

// handle individual events firing
typedef void (*EventHandler) (Event & event) ;

// all events handler
void ProcessEvents (EventQueue & queue, EventHandler handler);

// my own iterator can be used for inserting into the event queue
class event_queue_back_inserter : public iterator <output_iterator_tag, Event>
  {
  public:   

    event_queue_back_inserter (EventQueue & events) : events_ (events) {};

    // assignment is used to insert into the queue
    inline event_queue_back_inserter & operator= (const Event & e)
      { 
      events_.push (e);
      return *this;
      };

    // dereference and increments are no-ops that return 
    // the iterator itself
    inline event_queue_back_inserter & operator* () { return *this; };
    inline event_queue_back_inserter & operator++ () { return *this; };
    inline event_queue_back_inserter & operator++ (int) { return *this; };

  private:

  EventQueue & events_;


  };  // end of class event_queue_back_inserter

#endif  // BABBLEMUD_EVENTS_H
