#ifndef UtlTime_HH
#define UtlTime_HH

#include <sys/time.h>

#include <string>
#include <iostream>


class UtlTime : public timeval {
  friend class UtlTimeDifference;

public:
  UtlTime(int s, int u);
  UtlTime(bool sett=false);

  int update();

  int seconds() const;
  int microseconds() const;
  double time() const;

  UtlTime operator+(const UtlTimeDifference &d) const;
  UtlTime& operator+=(const UtlTimeDifference &d);
  UtlTime operator-(const UtlTimeDifference &d) const;
  UtlTime& operator-=(const UtlTimeDifference &d);
  UtlTimeDifference operator-(const UtlTime &t) const;
  bool operator!=(const UtlTime &that) const;

  std::ostream& print(std::ostream &o, std::string s="") const;

  //bool write(std::ostream &o) const;
  //bool read(std::istream &i) const;

private:
  void correctSigns();
};


class UtlTimeDifference {
  friend class UtlTime;

public:
  UtlTimeDifference();
  UtlTimeDifference(int s, int u);
  UtlTimeDifference(const UtlTime& t);

  UtlTime operator+(const UtlTime& t);
  bool operator>(const UtlTimeDifference& t);
  bool operator<(const UtlTimeDifference& t);

  double deltaTime() const;
  
  std::ostream& print(std::ostream &o, std::string s="") const;

private:
  void correctSigns();

  int dt_sec;
  int dt_usec;
};

std::ostream& operator<<(std::ostream &o, const UtlTime &tv);


#ifdef CALICE_DAQ_ICC

#include <cstring>
#include <cstdio>


UtlTime::UtlTime(int s, int u) {
  tv_sec=s;
  tv_usec=u;
  correctSigns();
}

UtlTime::UtlTime(bool sett) {
  tv_sec=0;
  tv_usec=0;
  if(sett) update();
}

int UtlTime::update() {
  return gettimeofday(this,0);
}

int UtlTime::seconds() const {
  return tv_sec;
}

int UtlTime::microseconds() const {
  return tv_usec;
}

double UtlTime::time() const {
    return tv_sec+0.000001*tv_usec;
}

UtlTime UtlTime::operator+(const UtlTimeDifference &d) const {
  UtlTime t(tv_sec+d.dt_sec,tv_usec+d.dt_usec);
  t.correctSigns();
  return t;
}

UtlTime& UtlTime::operator+=(const UtlTimeDifference &d) {
  tv_sec+=d.dt_sec;
  tv_usec+=d.dt_usec;
  correctSigns();
  return *this;
}

UtlTime UtlTime::operator-(const UtlTimeDifference &d) const {
  UtlTime t(tv_sec-d.dt_sec,tv_usec-d.dt_usec);
  t.correctSigns();
  return t;
}

UtlTime& UtlTime::operator-=(const UtlTimeDifference &d) {
  tv_sec-=d.dt_sec;
  tv_usec-=d.dt_usec;
  correctSigns();
  return *this;
}

UtlTimeDifference UtlTime::operator-(const UtlTime &t) const {
  UtlTimeDifference d(tv_sec-t.tv_sec,tv_usec-t.tv_usec);
  d.correctSigns();
  return d;
}

bool UtlTime::operator!=(const UtlTime &that) const {
  if(tv_sec != that.tv_sec) return false;
  if(tv_usec!=that.tv_usec) return false;
  return true;
}

std::ostream& UtlTime::print(std::ostream &o, std::string s) const {
  o << s << "UtlTime::print() " << tv_sec << " sec  " 
    << tv_usec << " usec" << std::endl;
  return o;
}

/*
bool UtlTime::write(std::ostream &o) const {
  if(!o) return false;
  o << tv_sec << " " << tv_usec;
  return true;
}

bool UtlTime::read(std::istream &i) const {
  if(!i) return false;
  i >> static_cast<int>(tv_sec) >> static_cast<int>(tv_usec);
  return true;
}
*/

void UtlTime::correctSigns() {
  if(tv_usec<0) {
    tv_usec+=1000000;
    tv_sec--;
  }
  if(tv_sec<0) {
    tv_sec=0;
    tv_usec=0;
  }
}

std::ostream& operator<<(std::ostream &o, const UtlTime &tv) {
  
  // Should be replaced by ostringstream code at some point...
  
  char t[34],c[26];

  strcpy(c,ctime((time_t*)&tv.tv_sec));
  strncpy(t,c+11,8);
  strncpy(t+17,c,11);
  strncpy(t+28,c+20,4);
  sprintf(t+8,":%3.3lu:%3.3lu",tv.tv_usec/1000,tv.tv_usec%1000);
  t[16]=' ';
  t[32]='\0';

  o << t;
  return o;
}

UtlTimeDifference::UtlTimeDifference() : dt_sec(0), dt_usec(0) {
  correctSigns();
}

UtlTimeDifference::UtlTimeDifference(int s, int u) : dt_sec(s), dt_usec(u) {
  correctSigns();
}

UtlTimeDifference::UtlTimeDifference(const UtlTime& t) {
  UtlTime n(true);
  *this=n-t;
}

UtlTime UtlTimeDifference::operator+(const UtlTime& t) {
  return UtlTime(dt_sec+t.tv_sec,dt_usec+t.tv_usec);
}

bool UtlTimeDifference::operator>(const UtlTimeDifference& t) {
  if(dt_sec>t.dt_sec) return true;
  if(dt_sec<t.dt_sec) return false;
  return dt_usec>t.dt_usec;
}

bool UtlTimeDifference::operator<(const UtlTimeDifference& t) {
  if(dt_sec<t.dt_sec) return true;
  if(dt_sec>t.dt_sec) return false;
  return dt_usec<t.dt_usec;
}

double UtlTimeDifference::deltaTime() const {
  if(dt_sec>=0) return dt_sec+0.000001*dt_usec;
  else          return dt_sec-0.000001*dt_usec;
  }

std::ostream& UtlTimeDifference::print(std::ostream &o, std::string s) const {
  o << s << "UtlTimeDifference::print() " << dt_sec << " sec  " 
    << dt_usec << " usec" << std::endl;
  return o;
}

void UtlTimeDifference::correctSigns() {
  if(dt_sec>0) {
    if(dt_usec<0) {
      dt_usec+=1000000;
      dt_sec--;
    }
  }
  
  if(dt_sec<0) {
    if(dt_usec>0) {
      dt_usec-=1000000;
      dt_sec++;
    }
  }
}

#endif
#endif
