// timing.cpp
//
// Copyright (C) 2006 - 2009 MicroNeil Research Corporation.
//
// See the corresponding .hpp file for descriptions and history.
//
// This program is part of the MicroNeil Research Open Library Project. For
// more information go to http://www.microneil.com/OpenLibrary/index.html
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.
//
// 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. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA

#include <ctime>
#include <sys/time.h>
#include <cerrno>

// Platform Specific Includes //////////////////////////////////////////////////

#ifdef WIN32
#include <windows.h>
#endif

#include "timing.hpp"

// Introduce the standard namespace ////////////////////////////////////////////

using namespace std;

///////////////////////////////////////////////////////////////////////////////
// class Sleeper - An object that remembers how long it is supposed to sleep.
// This allows an application to create "standard" sleep timers. This also
// helps keep sleeper values within range to avoid weird timing problems.
///////////////////////////////////////////////////////////////////////////////

// Abstracted doRawSleep() function ////////////////////////////////////////////

#ifdef WIN32

// In a WIN32 environment Sleep() is defined and it works in milliseconds so
// we will use that for doRawSleep(). It's important to note that under normal
// circumstances win32 Sleep() may be off by quite a bit (15ms or so) due to
// how timing is done in the OS. There are ways around this, but they are
// sometimes complex - so here I've left things basic. If more precise win32
// timing is needed then this method can be recoded using a workaround that is
// appropriate to the application.

void Sleeper::doRawSleep(int x) {
    Sleep(x);                                                                   // Use windows Sleep()
}

#else

// If we are not in a win32 environment then we're likely on a posix/unix system
// or at least we have the standard posix/unix time functions so we'll redefine
// absSleep to use nanosleep();

void Sleeper::doRawSleep(int x) {
    struct timespec sleeptime;                                                  // How much sleeping to do.
    struct timespec remaining;                                                  // How much sleeping remains.
    int result;                                                                 // The latest result.
    remaining.tv_sec = x/1000;                                                  // Divide ms by 1000 to get secs.
    remaining.tv_nsec = (x%1000)*1000000;                                       // Multiply the remaining msecs to get nsecs.
    do {                                                                        // Just in case we get interruped...
        sleeptime.tv_sec = remaining.tv_sec;                                    // Get our sleep time from the
        sleeptime.tv_nsec = remaining.tv_nsec;                                  // remaining time.
        result = nanosleep(&sleeptime,&remaining);                              // Call nanosleep and get the remaining time.
    } while(0>result && EINTR==errno);                                          // If we were interrupted sleep some more.
}

#endif

Sleeper::Sleeper()                                                              // Constructed empty we set our
    :MillisecondsToSleep(0) {                                                   // sleep time to zero.
}

Sleeper::Sleeper(int x) {                                                       // Constructed with a value we
    setMillisecondsToSleep(x);                                                  // set the sleep time or throw.
}

int Sleeper::setMillisecondsToSleep(int x) {                                    // Safe way to set the vlaue.
    if(x < MinimumSleeperTime ||
       x > MaximumSleeperTime)                                                  // If it's not a good time value
         throw BadSleeperValue();                                               // then throw the exception.
    MillisecondsToSleep = x;                                                    // If it is good - set it.
    return MillisecondsToSleep;                                                 // Return the set value.
}

int Sleeper::getMillisecondsToSleep() {                                         // Safe way to get the value.
      return MillisecondsToSleep;                                               // Send back the value.
}

void Sleeper::sleep() {                                                         // Here's where we snooze.
    if(MillisecondsToSleep > 0) {                                               // If we have a good snooze
        doRawSleep(MillisecondsToSleep);                                        // value then go to Sleep().
    } else {                                                                    // If the value is not good
        throw BadSleeperValue();                                                // throw an exception.
    }
}

void Sleeper::sleep(int x) {                                                    // Reset the sleep time then sleep.
    setMillisecondsToSleep(x);                                                  // Set the sleep time.
    sleep();                                                                    // Sleep.
}

void Sleeper::operator()() {                                                    // Syntactic sugar - operator() on
    sleep();                                                                    // a sleeper calls sleep().
}

///////////////////////////////////////////////////////////////////////////////
// class PollTimer - An object to pause during polling processes where the
// time between polls is expanded according to a Fibonacci sequence. This
// allows self organizing automata to relax a bit when a particular process
// is taking a long time so that the resources used in the polling process are
// reduced if the system is under load - The idea is to prevent the polling
// process from loading the system when there are many nodes poling, yet to
// allow for a rapid response when there are few or when the answer we're
// waiting for is ready quickly. We use a Fibonacci expansion because it is
// a natural spiral.
///////////////////////////////////////////////////////////////////////////////

PollTimer::PollTimer(int Nom, int Max) :
  NominalPollTime(MinimumSleeperTime),
  MaximumPollTime(MinimumSleeperTime)  {                                        // Construction requires a
    setNominalPollTime(Nom);                                                    // nominal delay to use and
    setMaximumPollTime(Max);                                                    // a maximum delay to allow.
}

int PollTimer::setNominalPollTime(int Nom) {                                    // Set the Nominal Poll Time.
    if(Nom < MinimumSleeperTime ||                                              // Check the low and high
       Nom > MaximumSleeperTime)                                                // limits and throw an
        throw BadPollTimerValue();                                              // exception if we need to.
                                                                                // If the value is good then
    NominalPollTime = Nom;                                                      // remember it.

    if(MaximumPollTime < NominalPollTime)                                       // Make sure the Maximum poll
       MaximumPollTime = NominalPollTime;                                       // time is >= the Nominal time.

    reset();                                                                    // Reset due to the change.
    return NominalPollTime;                                                     // Return the new value.
}

int PollTimer::setMaximumPollTime(int Max) {                                    // Set the Maximum Poll Time.
    if(Max < MinimumSleeperTime ||                                              // Check the low and high
       Max > MaximumSleeperTime)                                                // limits and throw an
        throw BadPollTimerValue();                                              // exception if we need to.
                                                                                // If the value is good then
    MaximumPollTime = Max;                                                      // remember it.

    if(MaximumPollTime < NominalPollTime)                                       // Make sure the Maximum poll
       MaximumPollTime = NominalPollTime;                                       // time is >= the Nominal time.

    reset();                                                                    // Reset due to the change.
    return MaximumPollTime;                                                     // Return the new value.
}

void PollTimer::reset() {                                                       // Reset the spiral.
    FibA = NominalPollTime;                                                     // Assume our starting event.
    FibB = 0;                                                                   // Assume no other events.
    LimitReached=false;                                                         // Reset our limit watcher.
}

int PollTimer::pause() {                                                        // Pause between polls.
    int SleepThisTime = MaximumPollTime;                                        // Assume we're at out limit for now.
    if(LimitReached) {                                                          // If actually are at our limit then
        mySleeper.sleep(SleepThisTime);                                         // use the current value.
    } else {                                                                    // If we are still expanding then
        SleepThisTime = FibA+FibB;                                              // Calculate the time to use and
        if(SleepThisTime >= MaximumPollTime) {                                  // check it against the limit. If
            SleepThisTime = MaximumPollTime;                                    // we reached the limit, us that value
            LimitReached = true;                                                // and set the flag.
        } else {                                                                // If we haven't reached the limit yet
            FibB=FibA;                                                          // then shift our events and remember
            FibA=SleepThisTime;                                                 // this one to build our spiral.
        }
        mySleeper.sleep(SleepThisTime);                                         // Take a nap.
    }                                                                           // Then FIRE THE MISSILES!
    return SleepThisTime;                                                       // Tell the caller how long we slept.
}

///////////////////////////////////////////////////////////////////////////////
// class Timer - This one acts much like a stop watch with millisecond
// resolution. The time is based on wall-clock time using gettimeofday().
///////////////////////////////////////////////////////////////////////////////

#ifdef WIN32

// Here is the win32 version of getLocalRawClock()

#define TimerIsUnixBased (false)

msclock Timer::getLocalRawClock() const {
    FILETIME t;                                                                 // We need a FILETIME structure.
    msclock c;                                                                  // We need a place to calculate our value.
    GetSystemTimeAsFileTime(&t);                                                // Grab the system time.
    c = (unsigned long long int) t.dwHighDateTime << 32LL;                      // Put full seconds into the high order bits.
    c |= t.dwLowDateTime;                                                       // Put 100ns ticks into the low order bits.
    c /= 10000;                                                                 // Divide 100ns ticks by 10K to get ms.
    c -= EPOCH_DELTA_IN_MSEC;                                                   // Correct for the epoch difference.
    return c;                                                                   // Return the result.
}

#else

// Here is the unix/posix version of getLocalRawClock()

#define TimerIsUnixBased (true)

msclock Timer::getLocalRawClock() const {
    struct timeval t;                                                           // We need a timval structure.
    msclock c;                                                                  // We need a place to calculate our value.
    gettimeofday(&t,NULL);                                                      // Grab the system time.
    c = t.tv_sec * 1000;                                                        // Put the full seconds in as milliseconds.
    c += t.tv_usec / 1000;                                                      // Add the microseconds as milliseconds.
    return c;                                                                   // Return the milliseconds.
}

#endif

Timer::Timer() {                                                                // Construct by resetting the
    start();                                                                    // clocks by using start();
}

Timer::Timer(msclock startt):                                                   // Construct a timer from a specific time.
  RunningFlag(true),                                                            // Set the running flag,
  StartTime(startt),                                                            // the start time and
  StopTime(startt) {                                                            // the stop time clock to startt.
}

void Timer::clear() {                                                           // Stop, zero elapsed, now.
    StartTime = StopTime = getLocalRawClock();                                  // Set the start and stop time
    RunningFlag = false;                                                        // to now. We are NOT running.
}

msclock Timer::start() {                                                        // (re) Start the timer at this moment.
    return start(getLocalRawClock());                                           // start() using the current raw clock.
}

msclock Timer::start(msclock startt) {                                          // (re) Start a timer at startt.
    StartTime = StopTime = startt;                                              // Set the start and end clocks.
    RunningFlag = true;                                                         // Set the running flag to true.
    return StartTime;                                                           // Return the start clock.
}

msclock Timer::getStartClock() { return StartTime; }                            // Return the start clock value.

bool Timer::isRunning() { return RunningFlag; }                                 // Return the running state.

msclock Timer::getElapsedTime() const {                                         // Return the elapsed timeofday -
    msclock AssumedStopTime;                                                    // We need to use a StopTime simulation.
    if(RunningFlag) {                                                           // If we are running we must get
        AssumedStopTime = getLocalRawClock();                                   // the current time (as if it were stop).
    } else {                                                                    // If we are not running we use
        AssumedStopTime = StopTime;                                             // the actual stop time.
    }
    msclock delta = AssumedStopTime - StartTime;                                // Calculate the difference.
    return delta;                                                               // That's our result.
}

msclock Timer::stop() {                                                         // Stop the timer.
    StopTime = getLocalRawClock();                                              // Grab the time and then stop
    RunningFlag=false;                                                          // the clock.
    return StopTime;                                                            // Return the time we stopped.
}

msclock Timer::getStopClock() { return StopTime; }                              // Return the stop clock value.

double Timer::getElapsedSeconds() const {                                       // Calculate the elapsed seconds.
    msclock e = getElapsedTime();                                               // Get the elapsed time in msecs.
    double secs = (double) e / 1000.0;                                          // Calculate seconds from msecs.
    return secs;
}

bool Timer::isUnixBased() { return TimerIsUnixBased; }                          // Is this timer unix based?

msclock Timer::toWindowsEpoch(msclock unixt) {                                  // Convert a unix based msclock to win32 based.
    return (unixt + EPOCH_DELTA_IN_MSEC);                                       // Going this way we add the epoch delta.
}

msclock Timer::toUnixEpoch(msclock win32t) {                                    // Convert a win32 based msclock to a unix based.
    return (win32t - EPOCH_DELTA_IN_MSEC);                                      // Going this way we subtract the epoch delta.
}

///////////////////////////////////////////////////////////////////////////////
// class Timeout - This one uses a Timer to establish a timeout value.
///////////////////////////////////////////////////////////////////////////////

Timeout::Timeout(msclock duration):myDuration(duration) { }                     // Create, set the duration, start.

msclock Timeout::setDuration(msclock duration) {                                // Set/Change the duration in milliseconds.
    myDuration = duration;                                                      // (re) Set the duration.
    return myDuration;                                                          // Return the current (new) duration.
}

msclock Timeout::getDuration() {                                                // Return the current duration.
    return myDuration;
}

msclock Timeout::restart() {                                                    // Restart the timeout timer.
    return myTimer.start();                                                     // Restart the clock and return the time.
}

msclock Timeout::getElapsedTime() {                                             // Get elapsed milliseconds.
    return myTimer.getElapsedTime();                                            // Return the elapsed time.
}

msclock Timeout::getRemainingTime() {                                           // Get remaining milliseconds.
    msclock remaining = 0ULL;                                                   // Assume we're expired to start.
    msclock elapsed = myTimer.getElapsedTime();                                 // Get the elapsed time.
    if(elapsed < myDuration) {                                                  // If there is still time then
        remaining = myDuration - elapsed;                                       // calculate what is left.
    }
    return remaining;                                                           // Return what we found.
}

bool Timeout::isExpired() {                                                     // Return true if time is up.
    return (!(myTimer.getElapsedTime() < myDuration));                          // Check the elapsed time against myDuration.
}