// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
// vim:set sts=4 ts=8:

// Copyright (c) 2001-2011 XORP, Inc and Others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License, Version
// 2.1, June 1999 as published by the Free Software Foundation.
// Redistribution and/or modification of this program under the terms of
// any other version of the GNU Lesser General Public License is not
// permitted.
// 
// This program 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. For more details,
// see the GNU Lesser General Public License, Version 2.1, a copy of
// which can be found in the XORP LICENSE.lgpl file.
// 
// XORP, Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA;
// http://xorp.net


#ifndef __LIBXORP_TIMER_HH__
#define __LIBXORP_TIMER_HH__

#include <assert.h>


#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#include "timeval.hh"
#include "heap.hh"
#include "callback.hh"
#include "task.hh"

class XorpTimer;
class TimerNode;
class TimerList;
class ClockBase;

typedef XorpCallback0<void>::RefPtr OneoffTimerCallback;

// PeriodicTimerCallback methods should return true to reschedule
typedef XorpCallback0<bool>::RefPtr PeriodicTimerCallback;

typedef XorpCallback1<void, XorpTimer&>::RefPtr BasicTimerCallback;

/**
 * @short Abstract class used to receive TimerList notifications
 *
 * TimerListObserverBase is a class that can be subtyped to receive
 * notifications on when timers are created or expired.  All the methods in
 * this class are private, since they must only be invoked by the friend class,
 * TimerList
 *
 * @see TimerList
 */
class TimerListObserverBase {
public:
    virtual ~TimerListObserverBase();

private:
    /**
     * This function will get called when a timer is scheduled.  Periodic
     * timers will produce periodic notifications.
     */
    virtual void notify_scheduled(const TimeVal&) = 0;

   /**
     * This function will get called when a timer is unscheduled.
     */
    virtual void notify_unscheduled(const TimeVal&) = 0;

    TimerList * _observed;

    friend class TimerList;
};

/**
 * @short XorpTimer class
 *
 * Timers allow callbacks to be made at a specific time in the future.
 * They are ordinarily created via TimerList methods, and they
 * must be associated with an TimerList object in order to be
 * runnable.
 *
 * @see TimerList
 */
class XorpTimer {
public:

    /**
     * @return true if the timer has been scheduled, and the callback
     * associated with this timer has not been called yet.
     */
    bool scheduled() const;

    /**
     * @return the expiry time of the @ref XorpTimer
     */
    const TimeVal& expiry() const;

    /**
     * Get the remaining time until the timer expires.
     *
     * @param remain the return-by-reference value with the remaining
     * time until the timer expires. If the current time is beyond
     * the expire time (e.g., if we are behind schedule with the timer
     * processing), the return time is zero.
     * @return true if the remaining time has meaningful value (e.g.,
     * if timer was scheduled), otherwise false.
     */
    bool time_remaining(TimeVal& remain) const;

    /**
     * Expire the @ref XorpTimer object when the TimerList is next run.
     */
    void schedule_now(int priority = XorpTask::PRIORITY_DEFAULT);

    /**
     * Schedule the @ref XorpTimer object at a given time.
     */
    void schedule_at(const TimeVal& when,
		     int priority = XorpTask::PRIORITY_DEFAULT);

    /**
     * Schedule the @ref XorpTimer object to expire in @ref wait
     * after the current time.
     */
    void schedule_after(const TimeVal& wait,
			int priority = XorpTask::PRIORITY_DEFAULT);

    /**
     * Schedule the @ref XorpTimer object.
     *
     * @param ms milliseconds from the current time.
     */
    void schedule_after_ms(int ms,
			   int priority = XorpTask::PRIORITY_DEFAULT);

    /**
     * Reschedule the @ref XorpTimer object.
     *
     * @param wait time from the most recent expiry.
     */
    void reschedule_after(const TimeVal& wait);

    /**
     * Reschedule the @ref XorpTimer object.
     *
     * @param ms milliseconds from the most recent expiry.
     */
    void reschedule_after_ms(int ms);

    /**
     * Unschedule the @ref XorpTimer object.  The XorpTimer callback is not
     * invoked.
     */
    void unschedule();			// unschedule if scheduled

    /**
     * Release reference to underlying state.
     */
    void clear();			// erase timer

    XorpTimer()				: _node(NULL) { }
    XorpTimer(TimerList* list, BasicTimerCallback cb);
    XorpTimer(const XorpTimer&);
    ~XorpTimer();

    XorpTimer& operator=(const XorpTimer&);
    TimerNode* node() const { return _node; }

private:
    TimerNode* _node;

    XorpTimer(TimerNode* n);

    friend class TimerList;
};


/**
 * @short XorpTimer creation and scheduling entity
 *
 * A TimerList is a scheduling entity that provides a means to
 * create @ref XorpTimer objects and run them.
 *
 * XorpTimer objects created via TimerList methods contain pointers to
 * reference counted elements maintained in the TimerList.  The
 * elements on the list need to be referenced by XorpTimer objects or
 * the underlying timer callbacks are never made.  For instance:
 *
<pre>
TimerList timer_list;

XorpTimer t = timer_list.new_oneoff_after(TimeVal(0, 100000),
			callback(some_function, some_arg));

new_oneoff_after(TimeVal(0, 200000), my_callback_b, my_parameter_a);

while ( ! timer_list.empty() ) {
	timer_list.run();
}
</pre>
 *
 * <code>my_callback_a</code> is called 100000us after the @ref XorpTimer
 * object is created.

 * <code>my_callback_b</code> is never called
 * because no XorpTimer references the underlying element on the TimerList
 * after <code>TimerList::new_oneoff_after()</code> is called.
 */
class TimerList {
public:
    /**
     * @param clock clock object to use to query time.
     */
    TimerList(ClockBase* clock);

    ~TimerList();

    /**
     * Expire all pending @ref XorpTimer objects associated with @ref
     * TimerList.
     */
    void run();

    /**
     * Create a XorpTimer that will be scheduled once.
     *
     * @param when the absolute time when the timer expires.
     * @param ocb callback object that is invoked when timer expires.
     *
     * @return the @ref XorpTimer created.
     */
    XorpTimer new_oneoff_at(const TimeVal& when,
			    const OneoffTimerCallback& ocb,
			    int priority = XorpTask::PRIORITY_DEFAULT);

    /**
     * Create a XorpTimer that will be scheduled once.
     *
     * @param wait the relative time when the timer expires.
     * @param ocb callback object that is invoked when timer expires.
     *
     * @return the @ref XorpTimer created.
     */
    XorpTimer new_oneoff_after(const TimeVal& wait,
			       const OneoffTimerCallback& ocb,
			       int priority = XorpTask::PRIORITY_DEFAULT);

    void remove_timer(XorpTimer& t);

    /**
     * Create a XorpTimer that will invoke a callback periodically.
     *
     * @param wait the period when the timer expires.
     * @param pcb user callback object that is invoked when timer expires.
     * If the callback returns false the periodic XorpTimer is unscheduled.
     *
     * @return the @ref XorpTimer created.
     */
    XorpTimer new_periodic(const TimeVal& wait,
			   const PeriodicTimerCallback& pcb,
			   int priority = XorpTask::PRIORITY_DEFAULT);

    /**
     * Create a XorpTimer to set a flag.
     *
     * @param when the absolute time when the timer expires.
     *
     * @param flag_ptr pointer to a boolean variable that is set to
     * @ref to_value when the @ref XorpTimer expires.
     *
     * @return the @ref XorpTimer created.
     */
    XorpTimer set_flag_at(const TimeVal&	when,
			  bool*			flag_ptr,
			  bool			to_value = true,
			  int priority = XorpTask::PRIORITY_DEFAULT);

    /**
     * Create a XorpTimer to set a flag.
     *
     * @param wait the relative time when the timer expires.
     *
     * @param flag_ptr pointer to a boolean variable that is set to
     * @ref to_value when the @ref XorpTimer expires.
     *
     * @return the @ref XorpTimer created.
     */
    XorpTimer set_flag_after(const TimeVal&	wait,
			     bool*		flag_ptr,
			     bool		to_value = true,
			     int priority = XorpTask::PRIORITY_DEFAULT);

    /**
     * Custom XorpTimer creation method.  The @ref XorpTimer object created
     * needs to be explicitly scheduled with the available @ref XorpTimer
     * methods.
     *
     * @param hook user function to be invoked when XorpTimer expires.
     *
     * @param thunk user argument to be passed when user's function is
     * invoked.
     *
     * @return the @ref XorpTimer created.
     */
    XorpTimer new_timer(const BasicTimerCallback& cb) {
	return XorpTimer(this, cb);
    }

    /**
     * @return true if there no @ref XorpTimer objects currently scheduled on
     * list.
     */
    bool empty() const;

    /**
     * @return the number of scheduled objects.
     */
    size_t size() const;

    /**
     * Query the next XorpTimer Expiry time.
     *
     * @param tv reference that is assigned expiry time of next timer.
     * If there is no @ref XorpTimer pending, this value is assigned the
     * maximum @ref TimeVal::MAXIMUM().  The first function returns the
     * absolute time at which the timer expires, where the second returns the
     * difference between now and the expiry time.
     *
     * @return true if there is a XorpTimer awaiting expiry, false otherwise.
     */
    bool get_next_delay(TimeVal& tv) const;

    /**
     * Get the priority of the highest priority timer that has expired.
     *
     * @return the priority of the expired timer, or INFINITE_PRIORITY
     * if no timer has expired.
     */
    int get_expired_priority() const;

    /**
     * Read the latest known value from the clock used by @ref
     * TimerList object.
     *
     * @param now the return-by-reference value with the current time.
     */
    void current_time(TimeVal& now) const;

    /**
     * Advance time.  This method fetches the time from clock object
     * associated with the TimerList and sets the TimerList current
     * time to this value.
     */
    void advance_time();

    /**
     * Default time querier.
     *
     * Get the current time.  This method is analogous to calling
     * the underlying operating system's 'get current system time'
     * function and is implemented as a call to advance_time()
     * followed by a call to current_time().
     *
     * @param tv a pointer to the @ref TimeVal storage to store the current
     * time.
     */
    static void system_gettimeofday(TimeVal* tv);

    /**
     * Suspend process execution for a defined interval.
     *
     * This methid is analogous to calling sleep(3) or usleep(3),
     * and is implemented as a call to sleep(3) and/or usleep(3)
     * followed by a call to advance_time().
     *
     * @param tv the period of time to suspend execution.
     */
    static void system_sleep(const TimeVal& tv);

    /**
     * Register an observer object with this class
     *
     * @param obs an observer object derived from @ref TimerListObserverBase
     */
    void set_observer(TimerListObserverBase& obs);

    /**
     * Unregister the current observer
     */
    void remove_observer();

    /**
     * Get pointer to sole TimerList instance.
     *
     * @return pointer if TimerList has been constructed, NULL otherwise.
     */
    static TimerList* instance();

private:
    void schedule_node(TimerNode* t);		// insert in time ordered pos.
    void unschedule_node(TimerNode* t);		// remove from list

    void acquire_lock() const		{ /* nothing, for now */ }
    bool attempt_lock() const		{ return true; }
    void release_lock() const		{ /* nothing, for now */ }


    // find or create the heap assoicated with this priority level
    Heap* find_heap(int priority);    

    // expire the highest priority timer
    bool expire_one(int worst_priority);

private:
    // The following is not a noncopyable candidate.
    TimerList(const TimerList&);		// Not copyable.
    TimerList& operator=(const TimerList&);	// Assignable only by self.

private:
    // we need one heap for each priority level
    map<int, Heap*>		_heaplist;

    ClockBase* 			_clock;
    TimerListObserverBase* 	_observer;
#ifdef HOST_OS_WINDOWS
    HANDLE			_hirestimer;
#endif

    friend class TimerNode;
    friend class TimerListObserverBase;
};

class TimerNode :
    public NONCOPYABLE,
    public HeapBase
{
protected:
    TimerNode(TimerList*, BasicTimerCallback);
    virtual ~TimerNode();
    void add_ref();
    void release_ref();

    // we want this even if it is never called, to override the
    // default supplied by the compiler.
    TimerNode(const TimerNode&);	// never called
    TimerNode& operator=(const TimerNode&);

    bool scheduled()		const	{ return _pos_in_heap >= 0; }
    int priority()		const	{ return _priority; }
    const TimeVal& expiry()	const	{ return _expires; }
    bool time_remaining(TimeVal& remain) const;

    void schedule_at(const TimeVal&, int priority);
    void schedule_after(const TimeVal& wait, int priority);
    void reschedule_after(const TimeVal& wait);
    void unschedule();
    virtual void expire(XorpTimer&, void*);

    int		_ref_cnt;	// Number of referring XorpTimer objects

    TimeVal	_expires;	// Expiration time
    BasicTimerCallback _cb;

    int		_priority;	// Scheduling priority

    TimerList*	_list;		// TimerList this node is associated w.

    friend class XorpTimer;
    friend class TimerList;
};


// ----------------------------------------------------------------------------
// inline Timer methods

inline XorpTimer::XorpTimer(TimerNode* n)
    : _node(n)
{
    if (_node)
	_node->add_ref();
}

inline XorpTimer::XorpTimer(TimerList* tlist, BasicTimerCallback cb)
    : _node(new TimerNode(tlist, cb))
{
    if (_node)
	_node->add_ref();
}

inline XorpTimer::XorpTimer(const XorpTimer& t)
    : _node(t._node)
{
    if (_node)
	_node->add_ref();
}

inline XorpTimer::~XorpTimer()
{
    if (_node)
	_node->release_ref();
}

inline XorpTimer&
XorpTimer::operator=(const XorpTimer& t)
{
    if (t._node)
	t._node->add_ref();
    if (_node)
	_node->release_ref();
    _node = t._node;
    return *this;
}

inline bool
XorpTimer::scheduled() const
{
    return _node && _node->scheduled();
}

inline const TimeVal&
XorpTimer::expiry() const
{
    assert(_node);
    return _node->expiry();
}

inline bool
XorpTimer::time_remaining(TimeVal& remain) const
{
    if (_node == NULL) {
	remain = TimeVal::ZERO();
	return (false);
    }
    return (_node->time_remaining(remain));
}

inline void
XorpTimer::schedule_at(const TimeVal& t, int priority)
{
    assert(_node);
    _node->schedule_at(t, priority);
}

inline void
XorpTimer::schedule_after(const TimeVal& wait, int priority)
{
    assert(_node);
    _node->schedule_after(wait, priority);
}

inline void
XorpTimer::schedule_after_ms(int ms, int priority)
{
    assert(_node);
    TimeVal wait(ms / 1000, (ms % 1000) * 1000);
    _node->schedule_after(wait, priority);
}

inline void
XorpTimer::schedule_now(int priority)
{
    schedule_after(TimeVal::ZERO(), priority);
}

inline void
XorpTimer::reschedule_after(const TimeVal& wait)
{
    assert(_node);
    _node->reschedule_after(wait);
}

inline void
XorpTimer::reschedule_after_ms(int ms)
{
    assert(_node);
    TimeVal wait(ms / 1000, (ms % 1000) * 1000);
    _node->reschedule_after(wait);
}

inline void
XorpTimer::unschedule()
{
    if (_node)
	_node->unschedule();
}

inline void
XorpTimer::clear()
{
    if (_node)
	_node->release_ref();
    _node = 0;
}

#endif // __LIBXORP_TIMER_HH__
