// -*- c-basic-offset: 4; related-file-name: "../../lib/gaprate.cc" -*-
#ifndef CLICK_GAPRATE_HH
#define CLICK_GAPRATE_HH
#include <click/timestamp.hh>
CLICK_DECLS
class ErrorHandler;

/** @file <click/gaprate.hh>
 *  @brief  A Click helper class for implementing a uniform rate.
 */

/** @class GapRate include/click/gaprate.hh <click/gaprate.hh>
 *  @brief  A helper class for implementing a uniform rate.
 *
 *  The GapRate class helps its user implement a process with a uniform rate:
 *  a process in which one user event happens every T seconds.  GapRate is
 *  designed to efficiently model high rates.  (Contrast this with Timer,
 *  which can serve a similar function at low rates.)
 *
 *  GapRate models an underlying "true" rated process with the correct rate.
 *  It also keeps a counter, maintained by its user via the update() method,
 *  that measures the progress of the user's rated process.  The
 *  needs_update() method compares this counter with its expected value, which
 *  is determined by the true rated process.  If the user's rated process is
 *  running behind, then needs_update() returns true; the user should trigger
 *  an event and call the update() method.  If the user's rated process is
 *  just right or is running faster than the true process, then needs_update()
 *  returns false; the user should <em>not</em> trigger an event.
 *
 *  Creating a non-bursty rate can be expensive and difficult.  GapRate
 *  attempts to create a non-bursty rate using timestamps and some interesting
 *  microsecond-based arithmetic.  External factors can cause scheduling
 *  hiccups where GapRate is not called as much as expected.  GapRate
 *  compensates for hiccups: the user's rated process may fall up to a full
 *  second behind the true rated process, then catch up in a burst.  More than
 *  one second's worth of lag is ignored.
 *
 *  The maximum rate GapRate can implement is MAX_RATE events per second.
 *
 *  @sa  Timer
 */
class GapRate { public:

    /** @brief  Construct a GapRate object with initial rate 0. */
    inline GapRate();

    /** @brief  Construct a GapRate object with initial rate @a r.
     *  @param  r  initial rate (events per second) */
    inline GapRate(unsigned r);


    /** @brief  Return the current rate. */
    inline unsigned rate() const;

    /** @brief  Set the current rate to @a r.
     *  @param  r  desired rate (events per second)
     *
     *  Rates larger than MAX_RATE are reduced to MAX_RATE. */
    inline void set_rate(unsigned r);

    /** @brief  Set the current rate to @a r.
     *  @param  r     desired rate (events per second)
     *  @param  errh  error handler
     *
     *  Acts like set_rate(@a r), except that an warning is reported to @a errh
     *  if @a r is larger than MAX_RATE. */
    void set_rate(unsigned r, ErrorHandler *errh);


    /** @brief  Returns whether the user's rate is behind the true rate.
     *  @param  ts  current timestamp
     *
     *  Returns true iff the user's rate is currently behind the true rate,
     *  meaning the user should cause an event and call update(). */
    inline bool need_update(const Timestamp &ts);

    /** @brief  Returns a time when the user's rate will be behind the true rate.
     *  @pre  need_update() has been called at least once.
     *  @return If the rate is 0, or need_update() has not been called,
     *  returns Timestamp().  If the user's rate is already behind the true
     *  rate, returns a time no greater than the argument passed to the last
     *  need_update().  Otherwise, returns a time in the future when
     *  need_update() will return true.
     */
    inline Timestamp expiry() const;

    /** @brief  Increment the user event counter.
     *
     *  Call this function when causing a user event. */
    inline void update();

    /** @brief  Increment the user event counter by @a delta.
     *  @param  delta  number of user events
     *
     *  @note This may be faster than calling update() @a delta times.
     *  Furthermore, @a delta can be negative. */
    inline void update_with(int delta);

    /** @brief  Resets the true rate counter.
     *
     *  This function flushes any old information about the true rate counter
     *  and its relationship to the user's rate counter. */
    inline void reset();


    enum { UGAP_SHIFT = 12 };
    enum { MAX_RATE = 1000000U << UGAP_SHIFT };

  private:

    unsigned _ugap;		// (1000000 << UGAP_SHIFT) / _rate
    int _sec_count;		// number of updates this second so far
    Timestamp::seconds_type _tv_sec;	// current second
    unsigned _rate;		// desired rate
#if DEBUG_GAPRATE
    Timestamp _last;
#endif

    inline void initialize_rate(unsigned rate);

};

/** @brief  Reset the underlying rated process. */
inline void
GapRate::reset()
{
    _tv_sec = -1;
#if DEBUG_GAPRATE
    _last.set_sec(0);
#endif
}

inline void
GapRate::initialize_rate(unsigned r)
{
    _rate = r;
    _ugap = (r == 0 ? MAX_RATE + 1 : MAX_RATE / r);
#if DEBUG_GAPRATE
    click_chatter("ugap: %u", _ugap);
#endif
}

inline void
GapRate::set_rate(unsigned r)
{
    if (r > MAX_RATE)
	r = MAX_RATE;
    if (_rate != r) {
	initialize_rate(r);
	if (_tv_sec >= 0 && r != 0) {
	    Timestamp now = Timestamp::now();
	    _sec_count = (now.usec() << UGAP_SHIFT) / _ugap;
	}
    }
}

inline
GapRate::GapRate()
{
    initialize_rate(0);
    reset();
}

inline
GapRate::GapRate(unsigned r)
    : _rate(0)
{
    initialize_rate(r);
    reset();
}

inline unsigned
GapRate::rate() const
{
    return _rate;
}

inline bool
GapRate::need_update(const Timestamp &now)
{
    // this is an approximation of:
    // unsigned need = (unsigned) ((now.usec() / 1000000.0) * _rate)
    unsigned need = (now.usec() << UGAP_SHIFT) / _ugap;

    if (_tv_sec < 0) {
	// 27.Feb.2005: often OK to send a packet after reset unless rate is
	// 0 -- requested by Bart Braem
	// check include/click/gaprate.hh (1.2)
	_tv_sec = now.sec();
	_sec_count = need + ((now.usec() << UGAP_SHIFT) - (need * _ugap) > _ugap / 2);
    } else if (now.sec() > _tv_sec) {
	_tv_sec = now.sec();
	if (_sec_count > 0)
	    _sec_count -= _rate;
    }

#if DEBUG_GAPRATE
    click_chatter("%{timestamp} -> %u @ %u [%d]", &now, need, _sec_count, (int)need >= _sec_count);
#endif
    return ((int)need >= _sec_count);
}

inline void
GapRate::update()
{
    _sec_count++;
}

inline void
GapRate::update_with(int delta)
{
    _sec_count += delta;
}

inline Timestamp
GapRate::expiry() const
{
    if (_tv_sec < 0 || _rate == 0)
        return Timestamp();
    else if (_sec_count < 0)
	return Timestamp(_tv_sec, 0);
    else {
	Timestamp::seconds_type sec = _tv_sec;
	int count = _sec_count;
	if ((unsigned) count >= _rate) {
	    sec += count / _rate;
	    count = count % _rate;
	}

	// uint32_t usec = (int) (count * 1000000.0 / _rate)
	uint32_t usec = (count * _ugap) >> UGAP_SHIFT;
	return Timestamp::make_usec(sec, usec);
    }
}

CLICK_ENDDECLS
#endif
