//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
//-----------------------------------------------------------------------------
//
// Description:
//       C++ Class for time value 
//
// Author:  Jie Chen
//
// Revision History:
//   cdevTimeValue.cc,v
// Revision 1.2  1998/02/10  18:04:57  chen
// add cdevSystem timer handler
//
// Revision 1.1.1.1  1995/06/16  17:14:02  epics
// initial import of cdev
//
//
#include "cdevTimeValue.h"


const long ONE_SECOND = 1000000L;

const cdevTimeValue cdevTimeValue::zero;

cdevTimeValue::cdevTimeValue (long sec, long usec)
:tv_sec_ (sec), tv_usec_ (usec)
{
  this->normalize ();
}

#ifdef _WIN32
void cdevTimeValue::set (const _timeb &tv)
{
  this->tv_sec_ = tv.time;
  this->tv_usec_ = tv.millitm;
  this->normalize ();
}
#endif

void cdevTimeValue::set ( const timeval &tv )
{
  this->tv_sec_ = tv.tv_sec;
  this->tv_usec_ = tv.tv_usec;
  this->normalize ();
}

#ifdef _WIN32
cdevTimeValue::cdevTimeValue ( const _timeb &tv )
{
  this->set(tv);
}
#endif

/* Initializes the cdevTimeValue object from a timeval */
cdevTimeValue::cdevTimeValue (const timeval &tv)
{
  this->set (tv);
}

/* Initializes the cdevTimeValue object from another cdevTimeValue */

cdevTimeValue::cdevTimeValue (const cdevTimeValue &tv)
: tv_sec_ (tv.tv_sec_), tv_usec_ (tv.tv_usec_)
{
  // empty
}

// assignment operator
cdevTimeValue&
cdevTimeValue::operator = (const cdevTimeValue& tv)
{
  if (this != &tv){
    tv_sec_ = tv.tv_sec_;
    tv_usec_ = tv.tv_usec_;
  }
  return *this;
}

// Initialize the cdevTimeValue Object from a float value
cdevTimeValue::cdevTimeValue (double seconds)
{
  double sec = floor (seconds);
  long   usec = (long)((seconds - sec)*ONE_SECOND);
  tv_sec_ = (long)sec;
  tv_usec_ = usec;
}

void
cdevTimeValue::normalize (void)
{
  while ((this->tv_usec_ >= ONE_SECOND) 
	 || (this->tv_sec_ < 0 && this->tv_usec_ > 0 )){
    this->tv_usec_ -= ONE_SECOND;
    this->tv_sec_++;
  }

  while ((this->tv_usec_ <= -ONE_SECOND) 
	 || (this->tv_sec_ > 0 && this->tv_usec_ < 0)){
    this->tv_usec_ += ONE_SECOND;
    this->tv_sec_--;
  }
}

/* Returns number of seconds. */
long 
cdevTimeValue::sec (void) const
{
  return this->tv_sec_;
}

/* Sets the number of seconds. */

void 
cdevTimeValue::sec (long sec) 
{
  this->tv_sec_ = sec;
}

/* Returns number of micro-seconds. */

long 
cdevTimeValue::usec (void) const
{
  return this->tv_usec_;
}

/* Sets the number of micro-seconds. */

void 
cdevTimeValue::usec (long usec) 
{
  this->tv_usec_ = usec;
}

/* Returns the value of the object as a timeval. */

#ifdef _WIN32 
cdevTimeValue::operator _timeb () const
{
  _timeb tv;
  tv.time = this->tv_sec_;
  tv.millitm = this->tv_usec_;
  return tv;
} 
#endif

cdevTimeValue::operator timeval () const
{
  timeval tv;
  tv.tv_sec = this->tv_sec_;
  tv.tv_usec = this->tv_usec_;
  return tv;
}

/* return the value of the object a double */
cdevTimeValue::operator double () const
{
  double tval = this->tv_sec_ + this->tv_usec_ / (double)(ONE_SECOND);
  return tval;
}

/* return a new value with increment */
void
cdevTimeValue::operator += (const cdevTimeValue& val)
{
  tv_sec_ += val.tv_sec_;
  tv_usec_ += val.tv_usec_;
  normalize ();
}

void
cdevTimeValue::operator -= (const cdevTimeValue& val)
{
  tv_sec_ -= val.tv_sec_;
  tv_usec_ -= val.tv_usec_;
  normalize ();
}

/* Adds two cdevTimeValue objects together, returns the sum. */

cdevTimeValue 
operator + (cdevTimeValue src1, cdevTimeValue src2)
{
  cdevTimeValue sum (src1.tv_sec_ + src2.tv_sec_, src1.tv_usec_ + src2.tv_usec_);

  sum.normalize ();
  return sum;
}

/* Subtracts two cdevTimeValue objects, returns the difference. */

cdevTimeValue 
operator - (cdevTimeValue src1, cdevTimeValue src2)
{
  cdevTimeValue delta (src1.tv_sec_ - src2.tv_sec_, src1.tv_usec_ - src2.tv_usec_);

  delta.normalize ();
  return delta;
}

/* True if tv1 > tv2. */

int
operator > (cdevTimeValue src1, cdevTimeValue src2)
{
  if (src1.tv_sec_ > src2.tv_sec_)
    return 1;
  else if (src1.tv_sec_ == src2.tv_sec_ && src1.tv_usec_ > src2.tv_usec_)
    return 1;
  else
    return 0;
}

/* True if tv1 >= tv2. */

int
operator >= (cdevTimeValue src1, cdevTimeValue src2)
{
  if (src1.tv_sec_ > src2.tv_sec_)
    return 1;
  else if (src1.tv_sec_ == src2.tv_sec_ && src1.tv_usec_ >= src2.tv_usec_)
    return 1;
  else
    return 0;
}

/* True if tv1 < tv2. */

int
operator < (cdevTimeValue src1, cdevTimeValue src2)
{
  return src2 > src1;
}

/* True if tv1 >= tv2. */

int
operator <= (cdevTimeValue src1, cdevTimeValue src2)
{
  return src2 >= src1;
}

/* True if tv1 == tv2. */

int
operator == (cdevTimeValue src1, cdevTimeValue src2)
{
  return src1.tv_sec_ == src2.tv_sec_ && src1.tv_usec_ == src2.tv_usec_;
}

/* True if tv1 != tv2. */

int
operator != (cdevTimeValue src1, cdevTimeValue src2)
{
  return !(src1 == src2);
}

cdevTimeValue
cdevTimeValue::currentTime (void)
{
  cdevTimeValue     tv;

#ifdef _WIN32
  _timeb cur_time;
  _ftime (&cur_time);
#else
  timeval cur_time;
  ::gettimeofday (&cur_time, 0);
#endif
  tv.set (cur_time);
  return tv;
}
