/*
 *  ZThreads, a platform-independant, multithreading and 
 *  synchroniation library
 *
 *  Copyright (C) 2000-2002, Eric Crahen, See LGPL.TXT for details
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  SUNY @ Buffalo, hereby disclaims all copyright interest in the
 *  ZThreads library written by Eric Crahen
 */

#ifndef __ZTTIMER_H__
#define __ZTTIMER_H__

#include "zthread/ZThread.h"
#include "zthread/Cancelable.h"
#include "zthread/SynchronizationExceptions.h"
#include "zthread/Condition.h"
#include "zthread/FastMutex.h"
#include "zthread/NullMutex.h"
#include "zthread/Observable.h"
#include "zthread/Thread.h"

#include <set>
#include <deque>

namespace ZThread {

class Timer;
class TimerEvent;

/**
 * @class TimerListener
 *
 * @author Eric Crahen <crahen@cse.buffalo.edu>
 * @date <2002-03-06T07:51:08-0500>
 * @version 2.0.0a
 *
 * An object interested in a Timer can implement this interface and then
 * register itself with a Timer to begin receiving TimerEvents as various
 * lengths of time expire
 */
class ZTHREAD_API TimerListener {
public:

  /**
   * Destroy the TimerListener
   */
  virtual ~TimerListener() throw() {}
 
  /**
   * Called from the Timer thread when an event expires
   *
   * @param const Timer* - timer that waited for the event
   * @param const TimerEvent& - timer event that expired
   */
  virtual void notify(const Timer*, const TimerEvent&) = 0;

};


/**
 * @class TimerEvent
 *
 * @author Eric Crahen <crahen@cse.buffalo.edu>
 * @date <2002-03-06T07:51:08-0500>
 * @version 2.0.3
 *
 * This class just encapsulates the timing details for each event
 * and makes them a bit easier to work with. 
 */
class ZTHREAD_API TimerEvent {

  unsigned long _interval;
  unsigned long _timeExpired;
  unsigned long _id;
  bool _periodic;

public:

  /**
   * Create a TimerEvent by copying another
   *
   * @param const TimerEvent& - event to copy
   */
  TimerEvent(const TimerEvent&);

  /**
   * Create a new event
   * 
   * @param unsigned long - time at which the timer should expire;
   * milliseconds from now
   * @param unsigned long - timer event id
   * @param bool - flag to indicate if this is a repeating/periodic event
   */
  TimerEvent(unsigned long, unsigned long, bool periodic = false);

  /**
   * Get the event id
   *
   * @return unsigned long - event id
   */
  inline unsigned long getId() const {
    return _id;
  }

  /**
   * Get the time left until this event should occur
   *
   * @return unsigned long - time till event
   */
  unsigned long getExpiration() const;

  /**
   * Get the interval for the event
   *
   * @return unsigned long - interval
   */
  inline unsigned long getInterval() const {
    return _interval;
  }

  /**
   * Check if the event has expired
   *
   * @return bool - event expired if true
   */
  bool isExpired() const;

  /**
   * Check if this is a periodic/repeating event.
   *
   * @return bool - event is periodic/repeating if true
   */
  inline bool isPeriodic() const {
    return _periodic;
  }

  /**
   * Copy an exisitng TimerEvent
   *
   * @param const TimerEvent& - timer event to copy
   */
  TimerEvent& operator=(const TimerEvent&);

  /**
   * Compare two TimerEvents for equality, based on expiration
   * time and id.
   *
   * @param const TimerEvent& - timer event to compare
   */
  inline bool operator==(const TimerEvent& e) const {
    return _timeExpired == e._timeExpired && _id == e._id;
  }

  /**
   * Compare two TimerEvents, by expirations time and id.
   *
   * @param const TimerEvent& - timer event to compare
   */
  inline bool operator<(const TimerEvent& e) const {
    return _timeExpired < e._timeExpired && _id < e._id;
  }

  /**
   * Compare two TimerEvents, by expirations time and id.
   *
   * @param const TimerEvent& - timer event to compare
   */
  inline bool operator>(const TimerEvent& e) const {
    return _timeExpired > e._timeExpired && _id < e._id;
  }

  /**
   * Casting operator to that evaluates to the event id
   *
   * @return unsigned long
   */
  inline operator unsigned long() const {
    return getId();
  }

};  

// Explicit instantiation for Win32/MSDEV
EXPLICIT_TEMPLATE(std::set<TimerListener*>)
EXPLICIT_TEMPLATE(std::deque<unsigned long>)

/**
 * @class Timer
 *
 * @author Eric Crahen <crahen@cse.buffalo.edu>
 * @date <2002-03-06T07:51:08-0500>
 * @version 2.0.3
 *
 * This class defines a simple Timer object base. Other objects can be
 * derived from this class to create more specialzed Timers. This encapsulates 
 * the timing routines and doesn't force a Timer to have to deal directly with 
 * a thread. 
 *
 * Interest in a timer is manipulated using the inherited Observable methods 
 */
class ZTHREAD_API Timer : public Cancelable, public Runnable, 
  public Observable<TimerListener, NullMutex> {

protected:

  //! Event list
  typedef std::set<TimerEvent> EventList;

  //! ID list
  typedef std::deque<int> KeyList;

  //! Registered events
  EventList _events;

  //! TODO: Replace this with a user supplied key generator
  //! Used ids
  KeyList _keys;

  //! Next id
  unsigned long _next;

  //! Key reuse flag
  bool _reuseKeys;

  //! Used to perform the timing
  Condition _interrupt;

  //! Used to serialize access
  FastMutex _lock;

public:

  /**
   * Create a new Timer object.
   *
   * @param reuseKeys If true, then the keys this Timer generates 
   * can be reused as events expire. If false, then id's will simply
   * increment as they are generated.
   */
  Timer(bool reuseKeys=true)
    throw(Synchronization_Exception);

  /**
   * Destroy this Timer object
   */
  virtual ~Timer() throw();

  /**
   * Set a timer. An event will occur after the given amount of time
   * expires. This method will create a new TimerEvent with a unique
   * id which will be submitted to the Timer's event list. 
   *
   * @param unsigned long - milliseconds until event
   * @param bool - event repeats
   *
   * @return unsigned long - event id 
   *
   * @exception Synchronization_Exception
   */
  unsigned long addEvent(unsigned long, bool periodic = false)
    throw(Synchronization_Exception);

  /**
   * Delete a TimerEvent. If an event exists in this Timer's event
   * list (the event has not expired) with the given id then it will be
   * removed from that list.
   *
   * @param unsigned lone - event id
   * @return bool - true if the event is successfulyl removed, false if not.
   *
   * @exception Synchronization_Exception
   */
  bool delEvent(unsigned long)
    throw(Synchronization_Exception);

  /**
   * This method enters a loop, waiting on the condition variable that
   * is associated with this object. This loop will executed until
   * the Timer has been canceled.
   */
  virtual void run() throw(); 

  /**
   * Get the curent time in milliseconds relative to the time the library was
   * loaded.
   *
   * @return unsigned long
   */
  static unsigned long now() throw();

}; /* Timer */

};

#endif
