// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/Basic.h>
#include <OB/Timer.h>

#ifdef WIN32
#   include <sys/timeb.h>
#endif

OBTimerList* OBTimerList::instance_ = 0;

class OBTimerListDestroyer
{
public:

    ~OBTimerListDestroyer()
    {
	delete OBTimerList::instance_;
	OBTimerList::instance_ = 0;
    }
};

static OBTimerListDestroyer destroyer;

// ----------------------------------------------------------------------
// OBTimer private and protected member implementation
// ----------------------------------------------------------------------

OBTimer::OBTimer()
    : next_(0), activated_(false)
{
    timeout_.tv_sec =0;
    timeout_.tv_usec =0;
}

OBTimer::OBTimer(const timeval& timeout, bool delta)
    : next_(0), activated_(false)
{
    activate(timeout, delta);
}

OBTimer::~OBTimer()
{
    if(activated_)
	stop();

    assert(!next_);
    assert(!activated_);
}

// ----------------------------------------------------------------------
// OBTimer public member implementation
// ----------------------------------------------------------------------

void
OBTimer::activate(const timeval& timeout, bool delta)
{
    assert(!next_);
    assert(!activated_);

    if(delta)
    {
	timeout_ = OBTimerList::timeNow();
   	timeout_ = OBTimerList::timeAdd(timeout, timeout_);
    }
    else
    {
	timeout_ = timeout;
    }
    
    OBTimerList::instance() -> add(this);
    
    assert(activated_);
}

void
OBTimer::stop()
{
    assert(activated_);
    
    OBTimerList::instance() -> remove(this);
    
    assert(!next_);
    assert(!activated_);
}

// ----------------------------------------------------------------------
// OBTimerList private and protected member implementation
// ----------------------------------------------------------------------

OBTimerList::OBTimerList()
    : head_(0) 
{
}

OBTimerList::~OBTimerList()
{
}

// ----------------------------------------------------------------------
// OBTimerList public member implementation
// ----------------------------------------------------------------------

unsigned int
OBTimerList::expire(const timeval& timeout)
{
    unsigned int count = 0;

    if(head_)
    {
	OBTimer* t;
	while((t = head_) && timeCmp(timeout, t -> timeout()) > 0)
	{
	    remove(t);
	    t -> notify();
	}
    }

    return count;
}

bool
OBTimerList::expireOneTimer(const timeval& timeout)
{
    if(head_)
    {
	OBTimer* t;
	while((t = head_) && timeCmp(timeout, t -> timeout()) > 0)
	{
	    remove(t);
	    t -> notify();
	    return true;
	}
    }

    return false;
}

void
OBTimerList::add(OBTimer* timer)
{
    assert(!timer -> next_);
    assert(!timer -> activated_);

    OBTimer** p = &head_;
    while(*p && timeCmp(timer -> timeout(), (*p) -> timeout()) >= 0)
    {
	p = &((*p) -> next_);
    }
    
    timer -> next_ = *p;
    timer -> activated_ = true;
    *p = timer;
}

void
OBTimerList::remove(OBTimer* timer)
{
    assert(timer -> activated_);

    OBTimer** p = &head_;
    while(*p && *p != timer)
    {
	p = &((*p) -> next_);
    }

    *p = timer -> next_;
    timer -> next_ = 0;
    timer -> activated_ = false;
}

OBTimerList*
OBTimerList::instance()
{
    if(!instance_)
	instance_ = new OBTimerList();
    
    return instance_;
}

int
OBTimerList::timeCmp(const timeval& a, const timeval& b)
{
    assert(a.tv_sec >= 0);
    assert(a.tv_usec >= 0);
    assert(a.tv_usec < 1000000);

    assert(b.tv_sec >= 0);
    assert(b.tv_usec >= 0);
    assert(b.tv_usec < 1000000);

    if(a.tv_sec != b.tv_sec)
	return a.tv_sec > b.tv_sec ? 1 : -1;
    
    if(a.tv_usec != b.tv_usec)
	return a.tv_usec > b.tv_usec ? 1 : -1;
    
    return 0;
}

timeval
OBTimerList::timeSub(const timeval& a, const timeval& b)
{
    assert(a.tv_sec >= 0);
    assert(a.tv_usec >= 0);
    assert(a.tv_usec < 1000000);

    assert(b.tv_sec >= 0);
    assert(b.tv_usec >= 0);
    assert(b.tv_usec < 1000000);

    timeval tv;

    tv.tv_sec = a.tv_sec - b.tv_sec;
    tv.tv_usec = a.tv_usec - b.tv_usec;

    tv.tv_sec += tv.tv_usec / 1000000;
    tv.tv_usec = tv.tv_usec % 1000000;

    if(tv.tv_usec < 0)
    {
	tv.tv_usec += 1000000;
	tv.tv_sec -= 1;
    }

    assert(tv.tv_sec >= 0);
    assert(tv.tv_usec >= 0);
    assert(tv.tv_usec < 1000000);

    return tv;
}

timeval
OBTimerList::timeAdd(const timeval& a, const timeval& b)
{
    assert(a.tv_sec >= 0);
    assert(a.tv_usec >= 0);
    assert(a.tv_usec < 1000000);

    assert(b.tv_sec >= 0);
    assert(b.tv_usec >= 0);
    assert(b.tv_usec < 1000000);

    timeval tv;

    tv.tv_sec = a.tv_sec + b.tv_sec;
    tv.tv_usec = a.tv_usec + b.tv_usec;
    
    tv.tv_sec += tv.tv_usec / 1000000;
    tv.tv_usec = tv.tv_usec % 1000000;

    if(tv.tv_usec < 0)
    {
	tv.tv_usec += 1000000;
	tv.tv_sec -= 1;
    }

    assert(tv.tv_sec >= 0);
    assert(tv.tv_usec >= 0);
    assert(tv.tv_usec < 1000000);

    return tv;
}

timeval
OBTimerList::timeNow()
{
    timeval tv;
#ifdef WIN32
    struct _timeb timebuffer;
    _ftime(&timebuffer);
    tv.tv_sec = timebuffer.time;
    tv.tv_usec = timebuffer.millitm * 1000;
#else
    gettimeofday(&tv, 0);
#endif
    return tv;
}
