/*
#  $Id$
# -----------------------------------------------------------------------------
#  The part of 'VideoNEXT MediaClient SDK'
# -----------------------------------------------------------------------------
#  Author: Petrov Maxim, 03/04/2009
#  Edited by:
#  QA by:
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------
*/

#include <sys/time.h>

#ifndef _VN_DELAY_HH
#define _VN_DELAY_HH

#if (defined(__WIN32__) || defined(_WIN32)) && !defined(IMN_PIM) && !defined(__MINGW32CE__) && !defined(__MINGW32__)
// For Windoze, we need to implement our own gettimeofday()
extern int gettimeofday(struct timeval*, int*);
#endif

namespace videonext { namespace media {

#ifdef TIME_BASE
typedef TIME_BASE time_base_seconds;
#else
typedef long time_base_seconds;
#endif

static const int MILLION = 1000000;

class DelayInterval;

class Timeval {

public:
    Timeval(time_base_seconds seconds, time_base_seconds useconds) {
        fTv.tv_sec = seconds; fTv.tv_usec = useconds;
    }
    
    Timeval() {
        struct timeval tv;
        gettimeofday(&tv, 0);
        fTv.tv_sec = tv.tv_sec; fTv.tv_usec = tv.tv_usec;
    }
    
    Timeval(const struct timeval &tv) {
        fTv.tv_sec = tv.tv_sec; fTv.tv_usec = tv.tv_usec;
    }
    
    time_base_seconds seconds() const {
        return fTv.tv_sec;
    }
    time_base_seconds seconds() {
        return fTv.tv_sec;
    }
    time_base_seconds useconds() const {
        return fTv.tv_usec;
    }
    time_base_seconds useconds() {
        return fTv.tv_usec;
    }
    
    int operator>=(Timeval const& arg2) const;
    int operator<=(Timeval const& arg2) const {
        return arg2 >= *this;
    }
    int operator<(Timeval const& arg2) const {
        return !(*this >= arg2);
    }
    int operator>(Timeval const& arg2) const {
        return arg2 < *this;
    }
    int operator==(Timeval const& arg2) const {
        return *this >= arg2 && arg2 >= *this;
    }
    int operator!=(Timeval const& arg2) const {
        return !(*this == arg2);
    }
    
    void operator+=(class DelayInterval const& arg2);
    void operator-=(class DelayInterval const& arg2);
    // returns ZERO if arg2 >= arg1
    
    
private:
    time_base_seconds& secs() {
        return (time_base_seconds&)fTv.tv_sec;
    }
    time_base_seconds& usecs() {
        return (time_base_seconds&)fTv.tv_usec;
    }
    
    struct timeval fTv;
};

class DelayInterval operator-(Timeval const& arg1, Timeval const& arg2);
// returns ZERO if arg2 >= arg1

Timeval operator+(const Timeval& arg1, const DelayInterval& arg2);
Timeval operator-(const Timeval& arg1, const DelayInterval& arg2);
// returns ZERO if arg2 >= arg1

///// DelayInterval /////

class DelayInterval: public Timeval {
public:
    DelayInterval(time_base_seconds seconds, time_base_seconds useconds)
        : Timeval(seconds, useconds) {}
};

DelayInterval operator*(short arg1, DelayInterval const& arg2);
DelayInterval operator*(float arg1, DelayInterval const& arg2);

/*extern DelayInterval const ZERO;
extern DelayInterval const SECOND;
DelayInterval const MINUTE = 60*SECOND;
DelayInterval const HOUR = 60*MINUTE;
DelayInterval const DAY = 24*HOUR;
*/

///// Timeval /////

inline int Timeval::operator>=(const Timeval& arg2) const {
    return seconds() > arg2.seconds()
        || (seconds() == arg2.seconds()
            && useconds() >= arg2.useconds());
}

inline void Timeval::operator+=(const DelayInterval& arg2) {
    secs() += arg2.seconds(); usecs() += arg2.useconds();
    if (usecs() >= MILLION) {
        usecs() -= MILLION;
        ++secs();
    }
}


inline void Timeval::operator-=(const DelayInterval& arg2) {
    secs() -= arg2.seconds(); usecs() -= arg2.useconds();
    if (usecs() < 0) {
        usecs() += MILLION;
        --secs();
    }
    if (secs() < 0)
        secs() = usecs() = 0;
}

inline Timeval operator+(const Timeval& arg1, const DelayInterval& arg2) {
    time_base_seconds secs = arg1.seconds() + arg2.seconds();
    time_base_seconds usecs = arg1.useconds() + arg2.useconds();
    
    if (usecs >= MILLION) {
        usecs -= MILLION;
        ++secs;
    }
    
    return Timeval(secs, usecs);
}

inline Timeval operator-(const Timeval& arg1, const DelayInterval& arg2) {
    time_base_seconds secs = arg1.seconds() - arg2.seconds();
    time_base_seconds usecs = arg1.useconds() - arg2.useconds();
    
    if (usecs < 0) {
        usecs += MILLION;
        --secs;
    }
    if (secs < 0)
        return Timeval(0,0);
    else
        return Timeval(secs, usecs);
}


/*
void Timeval::operator-(const Timeval& arg1, const DelayInterval& arg2) {
    time_base_seconds secs = arg1.seconds() - arg2.seconds();
    time_base_seconds usecs = arg1.useconds() - arg2.useconds();
    
    if (usecs < 0) {
        usecs += MILLION;
        --secs;
    }
    if (secs < 0)
        return Timeval(0, 0);
    else
        return Timeval(secs, usecs);
}
*/
#ifndef INT_MAX
#define INT_MAX 0x7FFFFFFF
#endif
const DelayInterval ZERO(0, 0);
const DelayInterval SECOND(1, 0);
const DelayInterval ETERNITY(INT_MAX, MILLION-1);
// used internally to make the implementation work


inline DelayInterval operator-(const Timeval& arg1, const Timeval& arg2) {
    time_base_seconds secs = arg1.seconds() - arg2.seconds();
    time_base_seconds usecs = arg1.useconds() - arg2.useconds();
    
    if (usecs < 0) {
        usecs += MILLION;
        --secs;
    }
    if (secs < 0)
        return ZERO;
    else
        return DelayInterval(secs, usecs);
}


///// DelayInterval /////

inline DelayInterval operator*(short arg1, const DelayInterval& arg2) {
    time_base_seconds result_seconds = arg1*arg2.seconds();
    time_base_seconds result_useconds = arg1*arg2.useconds();
    
    time_base_seconds carry = result_useconds/MILLION;
    result_useconds -= carry*MILLION;
    result_seconds += carry;
    
    return DelayInterval(result_seconds, result_useconds);
}

inline DelayInterval operator*(float arg1, const DelayInterval& arg2) {
    time_base_seconds result_seconds = arg1*arg2.seconds();
    time_base_seconds result_useconds = arg1*arg2.useconds();
    
    time_base_seconds carry = result_useconds/MILLION;
    result_useconds -= carry*MILLION;
    result_seconds += carry;
    
    return DelayInterval(result_seconds, result_useconds);
}


}}
#endif
