/** \file
 *
 *  Contains the Timestamp and Timespan class declarations.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef TIMESTAMP_H_
#define TIMESTAMP_H_

#include <sys/time.h>

class Str;
class Timespan;

/**
 *  Represents absolute times.
 *
 *  Accomplishes two goals.  First, it provides a first-class abstraction
 *  for storing time/date information, including sensical access and math
 *  functions.
 *
 *  Second, it abstracts many time-related library calls (current time,
 *  sleep) through a controlled interface.
 *  We can then manipulate this interface behind the scenes to do faster-
 *  than-realtime simulation.
 *
 *  \ingroup utils
 */
class Timestamp
{
public:
    /// Constant value to represent "no time".
    /// Set to \> 1 year before beginning of (1970 Jan 1) epoch,
    /// so if clock resets, uninitialized items still have an "old" timestamp.
    static const Timestamp NOT_SET_TIME;

    /// Constant value to represent beginning of (1970 Jan 1) epoch
    static const Timestamp EPOCH_START_TIME;

    /// Constructor, set time from double
    Timestamp( const double& seconds );

    /// Constructor, set time from separate seconds and microseconds
    Timestamp( const int seconds, const unsigned int microseconds );

    /// Constructor, set time from separate date and time integers
    Timestamp( const int year, const int month, const int day,
               const int hour, const int minute, const int second );

    /// Constructor, set time from struct timeval
    Timestamp( const struct timeval& timeval );

    /// Constructor, set time from simple y,m,d, h,m,s type formats including ISO9601
    Timestamp( const char* timeString );

    /// Copy constructor
    Timestamp( const Timestamp& copyMe = NOT_SET_TIME );

    /// Named Constructor, set from seconds << 20 + microseconds,
    static Timestamp FromMicros( const long long& micros );

    /// add seconds << 20 + microseconds
    void addFromMicros( const long long& micros );

    /// Named Constructor, set from seconds << 10 + milliseconds >> 10
    static Timestamp FromMillis( const long long& millis );

    /// add seconds << 10 + microseconds >> 10
    void addFromMillis( const long long& millis );

    // Round to the nearest #of seconds indicated.
    // i.e., use 60 for nearest minute, 3600 for hour, etc
    void round( int seconds );

    /// Destructor
    virtual ~Timestamp()
    {}
    ;

    /// Returns the time elapsed since this timestamp
    Timespan elapsed() const;

    /// Accessor, convert to double
    virtual double asDouble() const;

    /// Accessor, convert to float
    virtual double asFloat() const;

    /// Quickly returns the (seconds << 20) + microseconds (52 bits)
    long long asCompactMicros() const;

    /// Quickly returns the (seconds << 10) + (microseconds >> 10) (42 bits)
    long long asCompactMillis() const;

    /// Accessor, convert to usec
    long long asMicros() const;

    /// Accessor, convert to ms
    long long asMillis() const;

    inline time_t asTimeT() const
    {
        return timeval_.tv_sec;
    }

    /// Returns value as struct tm:
    /// int tm_sec;         /* seconds */
    /// int tm_min;         /* minutes */
    /// int tm_hour;        /* hours */
    /// int tm_mday;        /* day of the month */
    /// int tm_mon;         /* month */
    /// int tm_year;        /* year */
    /// int tm_wday;        /* day of the week */
    /// int tm_yday;        /* day in the year */
    /// int tm_isdst;       /* daylight saving time */
    struct tm asStructTm() const;

    //== Copy operators ==
    /// Copy operator
    Timestamp& operator= ( const Timestamp& rhs );
    /// Set from double
    Timestamp& operator= ( const double& seconds );
    /// Set from struct timeval
    Timestamp& operator= ( const struct timeval& timeval );

    //== Math operator ==
    /// Addition with a Timespan
    Timestamp& operator+= ( const Timespan& rhs );
    /// Subtraction of a Timespan
    Timestamp& operator-= ( const Timespan& rhs );
    /// Addition to Timespan
    Timestamp operator+ ( const Timespan& rhs ) const;
    /// Subtraction from Timespan
    Timestamp operator- ( const Timespan& rhs ) const;
    /// Subtraction from struct timeval
    Timespan operator- ( const struct timeval& rhs ) const;
    /// Subtraction of two times yields a timespan
    Timespan operator- ( const Timestamp& rhs ) const;

    //== Inequalities ==
    /// Equalities/inequalities
    bool operator== ( const Timestamp& rhs ) const;
    /// Equalities/inequalities
    bool operator!= ( const Timestamp& rhs ) const;
    /// Greater-than operator
    bool operator> ( const Timestamp& rhs ) const;
    /// Less-than operator
    bool operator< ( const Timestamp& rhs ) const;
    /// Greater-than-equal operator
    bool operator<= ( const Timestamp& rhs ) const;
    /// Less-than-equal operator
    bool operator>= ( const Timestamp& rhs ) const;

    //== Functions for setting time ==
    /// Set to the current time
    Timestamp& setToCurrentTime();

#ifdef __FASTSIM
    /// Set to the current time, unadjusted for running faster than real-time
    Timestamp& setToRealCurrentTime();
#endif

    /// Set to clock that represents monotonic time since some unspecified starting point.
    Timestamp& setToMonoTime();

    /// Static for current time for use on RHS
    static Timestamp Now();

#ifdef __FASTSIM

    static Timespan SetNow( const Timestamp newNow );

#endif

    /// Sleep until the given time
    void sleepTill();

    /// Internal conversion function
    void toTimespec( struct timespec* ) const;

    /// Internal conversion function
    void fromTimespec( const struct timespec* in );

    /// Get the internal storage
    const struct timeval& getTimeval() const
    {
        return timeval_;
    };

    //== Put input/output functions here... ==

    /// Writes the Timestamp as to a unsigned char buffer.
    ///
    /// \param buf  Buffer to write to
    /// \param buflen Maximum number of characters to write the buffer
    /// \param precision Number of decimal places to include in the seconds value
    /// \return Pointer to buf
    const char* toString( char* buf, int buflen, int precision = 3 ) const;

    /// Writes the Timestamp as a Str.
    const Str toString( int precision = 3 ) const;

    /// Writes the Timestamp as a small Str (YYYYmmdd'T'HHMMSS).
    const Str toSmallString() const;

    /// Writes the Timestamp as a smaller Str (YYYYmmddHHMM).
    const Str toSmallerString() const;

    /// Writes the Timestamp as a date-hour Str (YYYYmmddHH).
    const Str toDateHourString() const;


protected:

    /// Internal storage for time
    struct timeval timeval_;

    /// Set the internal storage to the specified value
    struct timeval& fromDouble( const double& seconds );

    /// Set the internal storage to the specified values
    struct timeval& fromIntegers( const int seconds, const unsigned int microseconds );

    /// Set the internal storage to the specified values
    struct timeval& fromIntegers( const int year, const int month, const int day,
                                  const int hour, const int minute, const int second );

    /// Set the internal storage to the specified ISO9601 string value
    struct timeval& fromString( const char* timeString );

    /// Returns a struct timeval from the specified values
    static struct timeval TimevalFromIntegers( const int seconds, const unsigned int microseconds );

    /// Returns a struct timeval from asCompactMicros() result
    static struct timeval TimevalFromMicros( const long long& micros );

    /// Returns a struct timeval from asCompactMillis() result
    static struct timeval TimevalFromMillis( const long long& millis );

#ifdef __FASTSIM
    // Allows for fast simulation
    static Timespan TimeOffset_;
#endif

};

/**
 *  Timespan class, represents a delta of time
 *
 *  \ingroup utils
 */
class Timespan : public Timestamp
{
public:
    /// Constant value to represent an invalid timespan.
    static const Timespan INVALID_TIMESPAN;

    /// Constant value to represent instant of time.
    static const Timespan ZERO_TIMESPAN;

    /// Constructor, set time from double
    Timespan( const double& seconds );

    /// Constructor, set time from struct timeval
    Timespan( const struct timeval& timeval );

    /// Copy constructor
    Timespan( const Timespan& copyMe = ZERO_TIMESPAN );

    /// Named Constructor, set from seconds << 20 + microseconds,
    static Timespan FromMicros( const long long& micros );

    /// Named Constructor, set from seconds << 10 + milliseconds >> 10
    static Timespan FromMillis( const long long& millis );

    /// Destructor
    ~Timespan()
    {}
    ;

    /// Accessor, convert to double
    virtual double asDouble() const;

    /// Accessor, convert to float
    virtual double asFloat() const;

    /// Sets the timespan using an XML-like duration string.
    /// format is "P(\d+D)?(\d+H)?(\d+M)?(\d+(.\d+)?S)?"
    /// returns true if parsed correctly, false otherwise
    bool setDuration( const char* duration );

    //== Math operator ==
    /// Addition with a Timespan
    Timespan& operator+= ( const Timespan& rhs );
    /// Subtraction of a Timespan
    Timespan& operator-= ( const Timespan& rhs );
    /// Subtraction of a Timeval
    Timespan& operator-= ( const struct timeval& rhs );

    //== Functions for dealing with "whole units" of time ==
    /// Static allows creation of Timespans denominated in milliseconds
    static Timespan Milliseconds( float msec )
    {
        return Timespan( msec / 1000.0 );
    }

    /// Static allows creation of Timespans denominated in seconds (as a float)
    /// This function originally took an integer, but I think this syntax also
    /// makes sense.  When called with an integer parameter, it does rely on
    /// the implicit conversion from ints to doubles
    static Timespan Seconds( double sec )
    {
        return Timespan( sec );
    };

    /// Static allows creation of Timespans denominated in minutes
    static Timespan Minutes( int min )
    {
        return Timespan( 60.0 * min );
    };

    /// Static allows creation of Timespans denominated in hours
    static Timespan Hours( int hour )
    {
        return Timespan( hour * 3600.0 );
    };

    /// Static allows creation of Timespans denominated in days
    static Timespan Days( int day )
    {
        return Timespan( day * 3600.0 * 24.0 );
    };

#ifdef __FASTSIM
    void sleepFast( void ) const
    {
        TimeOffset_ += *this;
    }
#endif

    /// Sleep for the timespan
    void sleepFor( void ) const;

    //== Input/output ==


    /// Writes the Timespan to a unsigned char buffer
    ///
    /// \param buf  Buffer to write to
    /// \param buflen Maximum number of characters to write the buffer
    /// \param precision Number of decimals to include in seconds result (up to 6).
    /// \return Pointer to buf
    const char* toString( char* buf, int buflen, int precision = 3 ) const;

    /// Writes the Timespan as a Str.
    const Str toString( int precision = 3 ) const;

};

#endif /*TIMESTAMP_H_*/
