/*
 *  ZThreads, a platform-independant, multithreading and 
 *  synchroniation library
 *
 *  Copyright (C) 2001, 2002 Eric Crahen, See LGPL.TXT for details
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  SUNY @ Buffalo, hereby disclaims all copyright interest in the
 *  ZThreads library written by Eric Crahen
 */

#ifndef __ZTTIME_H__
#define __ZTTIME_H__

#include <zthread/ZThread.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#if defined(HAVE_FTIME)
#include <sys/timeb.h>
#else
#include <sys/time.h>
#endif

#include <iostream>

namespace ZThread {

/**
 * @class Time
 * @author Eric Crahen <crahen@cse.buffalo.edu>
 * @date <2002-02-28T09:14:33-0500>
 *
 * Utility class for dealing with the time in terms of milliseconds. This 
 * will use ftime() if its available, if not it will fallback on gettimeofday()
 */
class Time {

  //  friend std::ostream& operator<<(std::ostream&, const Time&);

 public:

  /**
   * Create a new Time object initialized with the current time.
   */
  inline Time() {

#if defined(HAVE_FTIME)
#if defined(_WIN32) && !defined(__BCPLUSPLUS__)
    _ftime(&_value);
#else
    ftime(&_value);
#endif
#else
    gettimeofday(&_value, 0);
#endif

  }

  /**
   * Get the seconds portion of the time when this object was created.
   *
   * @return unsigned long
   */
  inline unsigned long seconds() const {
#if defined(HAVE_FTIME)
    return _value.time;
#else
    return _value.tv_sec;
#endif
  }


  /**
   * Get the milliseconds portion of the time when this object was created.
   *
   * @return unsigned long
   */
  inline unsigned long milliseconds() const {
#if defined(HAVE_FTIME)
    return _value.millitm;
#else
    return _value.tv_usec/1000;
#endif
  }


  /**
   * Get a time object representing the time elapsed since the library loaded.
   *
   * @return Time&
   */ 
  inline static Time now() {

    Time t;
    return (t - _first);

  }

  /**
   * Change the value of this Time value.
   *
   * @param const Time&
   */
  inline void operator=(const Time& t) {   
    seconds(t.seconds());
    milliseconds(t.milliseconds());
  }

  /**
   * Add a Time value to another.
   *
   * @param const Time&
   * @return Time
   */
  inline Time operator+(const Time& t) const {   
    return Time(seconds() + t.seconds(), milliseconds() + t.milliseconds());
  }


  /**
   * Subtract a Time value to another.
   *
   * @param const Time&
   */
  inline Time operator-(const Time& t) {   

    unsigned long s;
    unsigned long ms;

    timediff(s, ms, *this, t);
    
    return Time(s, ms);

  }

  /**
   * Add a Time value to this one.
   *
   * @param const Time&
   * @return Time
   */
  inline void operator+=(const Time& t) {   

    unsigned long m = t.milliseconds() + milliseconds();
    unsigned long s = t.seconds() + seconds();
    
    seconds(s + m/1000);
    milliseconds(m%1000);

    seconds(s + m/1000);
    milliseconds(m%1000);

  }

  /**
   * Add some number of milliseconds to this Time object.
   *
   * @param unsigned long 
   * @return Time
   */
  inline void operator+=(unsigned long ms) {   

    unsigned long m = milliseconds() + ms%1000;
    unsigned long s = seconds() + ms/1000;
    
    seconds(s + m/1000);
    milliseconds(m%1000);

  }

  /**
   * Subtract a Time value from this one.
   *
   * @param const Time&
   */
  inline void operator-=(const Time& t) {   

    unsigned long s;
    unsigned long ms;

    timediff(s, ms, *this, t);
    
    seconds(s);
    milliseconds(ms);

  }

  /**
   * Subtract some number of milliseconds to this Time object.
   *
   * @param unsigned long 
   * @return Time
   */
  inline void operator-=(unsigned long ms) {   

    Time t(0, ms);

    unsigned long s;
    unsigned long m;

    timediff(s, m, *this, t);
    
    seconds(s);
    milliseconds(m);

  }

 private:


  /**
   * Create a new Time object initialized with the given time.
   *
   * @param seconds
   * @param milliseconds
   */
  inline Time(unsigned long s, unsigned long ms) {
    seconds(s);
    milliseconds(ms);  
  }

  /**
   * Subtract a Time value from another. If t0 > t1 then s & ms will be set to 0
   *
   * @param unsigned long& address of result (seconds)
   * @param unsigned long& address of result (milliseconds)
   * @param const Time&
   * @param const Time&
   */
  inline void timediff(unsigned long& s, unsigned long& ms, const Time& t0, const Time& t1) {

    unsigned long s0 = t0.seconds();
    unsigned long s1 = t1.seconds();
    unsigned long m0 = t0.milliseconds();
    unsigned long m1 = t1.milliseconds();

    while(s0 > 0 && m0 < m1) {
      m0 += 1000;    
      s0 -= 1;
    }
    
    if(s0 < s1) {
      s = 0; 
      ms = 0;
    } else {
      s = s0 - s1;
      ms = m0 - m1;
    }

  }

  /**
   * Set the seconds portion of the time.
   *
   * @param unsigned long
   */
  inline void seconds(unsigned long s) {
#if defined(HAVE_FTIME)
    _value.time = s;
#else
    _value.tv_sec = s;
#endif
  }

  /**
   * Set the milliseconds portion of the time.
   *
   * @param unsigned long
   */
  inline void milliseconds(unsigned long ms) {
#if defined(HAVE_FTIME)
    _value.millitm = ms;
#else
    _value.tv_usec = ms*1000;
#endif
  }


#if defined(HAVE_FTIME)
#if defined(_WIN32) && !defined(__BCPLUSPLUS__)
  typedef struct _timeb VALUE;
#else
  typedef struct timeb VALUE;
#endif
#else
  typedef struct timeval VALUE;
#endif

  //! Store the time value
  VALUE _value;

  //! Store the first time value seen
  static Time _first;

};

};

//std::ostream& operator<<(std::ostream& o, const ZThread::Time& p) 



#endif
