/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Types for time.
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef BASE_TIME_HH
#define BASE_TIME_HH 1

#include <iostream>

namespace base {

typedef int64 TimeInt;

////////////////////////////////////////////////////////////////////////////////
/// @brief Time units as distinct SIGNED INTEGER types.
///
/// TimeInt is SIGNED since subtraction of time units could result in a negative value.
/// 64-bits of milliseconds can define 300 million years (less than FreeBSD's uptime).
/// These classes would be much simpler based on an fp type but none are wide enough.
///
/// Milliseconds class is elaborate since it is used the most.
///
struct Milliseconds
{
    typedef TimeInt Int;
    Milliseconds( void ) : mUnits(0) { }
    explicit Milliseconds( Int units ) : mUnits(units) { }
    Int INT( void ) const { return mUnits; }  // operator Int() would undermine type distinctiveness by allowing auto-conversion
    fpx FPX( void ) const { return fpx(mUnits); }
    friend inline Milliseconds operator+( const Milliseconds& a, const Milliseconds& b ) { return Milliseconds(a.mUnits + b.mUnits); }
    friend inline Milliseconds operator-( const Milliseconds& a, const Milliseconds& b ) { return Milliseconds(a.mUnits - b.mUnits); }
    friend inline Milliseconds operator*( const Milliseconds& a, const Milliseconds& b ) { return Milliseconds(a.mUnits * b.mUnits); }
    friend inline Milliseconds operator/( const Milliseconds& a, const Milliseconds& b ) { return Milliseconds(a.mUnits / b.mUnits); }
    friend inline         bool operator==( const Milliseconds& a, const Milliseconds& b ) { return a.mUnits ==  b.mUnits; }
    friend inline         bool operator!=( const Milliseconds& a, const Milliseconds& b ) { return a.mUnits !=  b.mUnits; }
    friend inline         bool operator<(  const Milliseconds& a, const Milliseconds& b ) { return a.mUnits <  b.mUnits; }
    friend inline         bool operator<=( const Milliseconds& a, const Milliseconds& b ) { return a.mUnits <= b.mUnits; }
    friend inline         bool operator>(  const Milliseconds& a, const Milliseconds& b ) { return a.mUnits >  b.mUnits; }
    friend inline         bool operator>=( const Milliseconds& a, const Milliseconds& b ) { return a.mUnits >= b.mUnits; }
    Milliseconds& operator+=( const Milliseconds& src ) { mUnits += src.mUnits; return *this; }
    Milliseconds& operator-=( const Milliseconds& src ) { mUnits -= src.mUnits; return *this; }
    friend std::ostream& operator<<( std::ostream& strm, const Milliseconds& t ) { strm << t.INT(); return strm; }
    friend std::istream& operator>>( std::istream& strm, Milliseconds& milliseconds ) { strm >> milliseconds.mUnits; return strm; }

private:
    Int mUnits;
};

// The other time units don't need to be as elaborate.
#define DEFINE_TIME_CLASS( TIME_CLASS )                                         \
struct TIME_CLASS                                                               \
{                                                                               \
    typedef TimeInt Int;                                                        \
    TIME_CLASS( void ) : mUnits(0) { }                                          \
    explicit TIME_CLASS( Int units ) : mUnits(units) { }                        \
    Int INT( void ) const { return mUnits; }                                    \
    fpx FPX( void ) const { return fpx(mUnits); }                               \
    friend std::ostream& operator<<( std::ostream& strm, const TIME_CLASS& t )  \
    { strm << t.INT(); return strm; }                                           \
private:                                                                        \
    Int mUnits;                                                                 \
};

DEFINE_TIME_CLASS( Microseconds )
DEFINE_TIME_CLASS( Seconds )
DEFINE_TIME_CLASS( Minutes )
DEFINE_TIME_CLASS( Hours )

// Common time conversion functions.
// These are templates to support fp calculations (fractions of time)
// Writing numeric type for template makes explicit whether int or fp is being computed,
// as conversion with integers can cause unintended truncation.
// Milliseconds2Seconds<fpx>( rawMilliseconds );      // fractional second
// Milliseconds2Seconds<TimeInt>( rawMilliseconds );  // truncates to integer seconds

template<typename NUM_TYPE>
NUM_TYPE Microseconds2Milliseconds( NUM_TYPE microseconds )
{ return NUM_TYPE( microseconds / 1000 ); }

template<typename NUM_TYPE>
NUM_TYPE Milliseconds2Microseconds( NUM_TYPE milliseconds )
{ return NUM_TYPE( milliseconds * 1000 ); }

template<typename NUM_TYPE>
NUM_TYPE Seconds2Milliseconds( NUM_TYPE seconds )
{ return NUM_TYPE( seconds * 1000 ); }

template<typename NUM_TYPE>
NUM_TYPE Minutes2Milliseconds( NUM_TYPE minutes )
{ return NUM_TYPE( minutes * 60 * 1000 ); }

template<typename NUM_TYPE>
NUM_TYPE Microseconds2Seconds( NUM_TYPE microseconds )
{ return NUM_TYPE( microseconds / 1000000 ); }

template<typename NUM_TYPE>
NUM_TYPE Milliseconds2Seconds( NUM_TYPE milliseconds )
{ return NUM_TYPE( milliseconds / 1000 ); }

} // namespace base

#endif // BASE_TIME_HH
