/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM is free software; you can redistribute it and/or modify it    
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; only version 2 of the License.      
 * 
 * NMM 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 General Public 
 * License for more details.  
 * 
 * You should have received a copy of the GNU General Public License 
 * along with NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#ifndef NMM_INTERVAL_HPP
#define NMM_INTERVAL_HPP NMM_INTERVAL_HPP

#include "nmm/nmm_prefix.hpp"

#include <iostream>
#include <string>

#include "nmm_timeval.hpp"
#include "nmm/nmm_inttypes.hpp"
#include "nmm/nmm_types.hpp"

#include "visibility.hpp"

namespace NMM {
  
  class Rational;
  class Time;
  
  //! One billion nanoseconds per second.
  const nmm_int64 nano_per_sec = 1000000000L;
  
  //! The DivisionByZero - Exception for the class Interval.
  class IntervalDivisionByZeroException {
  public:
    
    //! The main constructor.
    /*!
     *  Writes a short hint to GLOBAL_ERROR_STREAM2.
     */
    IntervalDivisionByZeroException();
  };
  
  //! This class represents a time interval.
  /*!
   *  A time interval is defined as the difference between two points in 
   *  time. 
   *  Like a time value, an interval is stored in two long ints, one for the 
   *  seconds and one for the nanoseconds. It is important to notice that the
   *  nanoseconds are always between 0 and 10^9 - 1, and that the value of the Interval
   *  must be calculated as seconds + nanoseconds/nano_per_sec. So, e.g. -0.2 sec
   *  is stored as -1 second, 800,000,000 nanoseconds.
   */
  class DLLEXPORT Interval {
  public:
    nmm_int64 sec;      //!< The seconds.
    nmm_int64 nsec;     //!< The nanoseconds.

    //! The main constructor.
    /*!
     *  \return An interval object that is initialized to zero.
     */
    Interval();

    //! This constructor initializes the interval object to a given value.
    /*!
     *  @param nanoseconds The interval value represented as nanoseconds.
     */
    Interval(const nmm_int64& nanoseconds);

    //! This constructor initializes the interval object to a given value.
    /*!
     *  @param seconds The seconds of the interval.
     *  @param nanoseconds The nanoseconds of the interval.
     */
    Interval(const nmm_int64& seconds, const nmm_int64& nanoseconds);

    //! This constructor initializes the interval object to a given value.
    /*!
     *  @param time The interval value represented as a struct timeval.
     */
    Interval(const struct timeval& time);

    //! This constructor initializes the interval object to a given value.
    /*!
     *  @param time The time value represented as a rational number.
     *         (Where 1 represents one second.)
     */
    Interval(const Rational& time);

    //! Adds an interval to another.
    //!
    //! @param interval that is added. 
    //! @return Interval including the result
    Interval& operator+=(const Interval& interval);

    //! Subtracts an interval from another.
    //!
    //! @param interval that is subtracted. 
    //! @return Interval including the result
    Interval& operator-=(const Interval& interval);

    //! Multiplies an Interval by a float
    /*!
     *  @param a The multiplier.
     *  @return Interval including the result
     */
    Interval& operator*=(const float& a);

    //! Divides an Interval by an integer.
    /*!
     *  @param a The divisor. If a is zero, an IntervalDivisionByZeroException
     *  is thrown.
     *  @return Interval including the result
     */
    Interval& operator/=(const int& a)
      throw (const IntervalDivisionByZeroException&);

    //! Converts an Interval into a float
    /*!
     *  @return value of Interval as float
     */
    operator float() const {
      float r = static_cast<float>(sec);
      r += static_cast<float>(static_cast<double>(nsec) / static_cast<double>(nano_per_sec));
      return r;
    }

    //! Get Interval in format: "sec.nsec", where nsec are properly filled with
    //! '0' up to 9 digits
    //!
    //! @return value of Interval as string
    std::string getPrettyString() const;
  };

  //! Returns the negative interval value.
  //! 
  //! @param interval that is negatived. 
  //! @return the negative interval value.
  DLLEXPORT Interval operator-(const Interval& interval);

  //! Adds an interval a to another interval b and returns the result as
  //! a new object.
  //!
  //! @param a first summand. 
  //! @param b second summand. 
  //! @return Interval including the result.
  DLLEXPORT Interval operator+(const Interval& a, const Interval& b);

  //! Subtracts an interval from another and returns the result
  //! as a new object.
  //!
  //! @param a specifies minuend. 
  //! @param b specifies subtrahend
  //! @return Interval including the result
  DLLEXPORT Interval operator-(const Interval& a, const Interval& b);

  //! Returns true if the two intervals are equal.
  //!
  //! @return true if both intervals are equal, otherwise false
  inline bool operator==(const Interval& a, const Interval& b)
  {
    return a.sec == b.sec && a.nsec == b.nsec;
  };

  //! Returns true if the two intervals are not equal.
  //!
  //! @param a interval that is compared
  //! @param b interval that is compared 
  //! @return true iff the two intervals are not equal.
  inline bool operator!=(const Interval& a, const Interval& b)
  {
    return a.sec != b.sec || a.nsec != b.nsec;
  };

  //! Returns true if a is less than b.
  //!
  //! @param a interval that is compared
  //! @param b interval that is compared 
  //! @return true if a is less than b
  inline bool operator<(const Interval& a, const Interval& b)
  {
    return (a.sec < b.sec || (a.sec == b.sec && a.nsec < b.nsec));
  };

  //! Returns true iff a is less than ore equal to b.
  //!
  //! @param a interval that is compared
  //! @param b interval that is compared 
  //! @return true if a is less than ore equal to b.
  inline bool operator<=(const Interval& a, const Interval& b)
  {
    return (a.sec < b.sec || (a.sec == b.sec && a.nsec <= b.nsec));
  };

  //! Returns true if a is greater than b.
  //! 
  //! @param a interval that is compared
  //! @param b interval that is compared 
  //! @return true if a is greater than b.
  inline bool operator>(const Interval& a, const Interval& b)
  {
    return (a.sec > b.sec || (a.sec == b.sec && a.nsec > b.nsec));
  };

  //! Returns true if a is greater than ore equal to b.
  //! 
  //! @param a interval that is compared
  //! @param b interval that is compared 
  //! @return true if a is greater than ore equal to b.
  inline bool operator>=(const Interval& a, const Interval& b)
  {
    return (a.sec > b.sec || (a.sec == b.sec && a.nsec >= b.nsec));
  };

  //! Returns an output stream that represents the interval value.
  /*! The output should be human readable, and the internal representation can be
   *  confusing when having negative values. E.g. -0.2 sec
   *  is stored as -1 second, 800,000,000 nanoseconds. The output operator
   *  calculates a human readable output from the internal representation.
   * 
   *  @param s reference to stream where interval is written to
   *  @param interval that is written into stream s
   *  @return reference to stream s
   */
  DLLEXPORT std::ostream& operator<<(std::ostream& s, const Interval& interval);

  //! Returns an interval that represents the difference between the
  //! two points in time a and b.
  //! 
  //! @param a specifies minuend
  //! @param b specifies subtrahend
  //! @return Interval that represents the difference between the two points in time a and b.
  DLLEXPORT Interval operator-(const Time& a, const Time& b);

  //! Suspends a thread for at least the period which is represented by
  //! interval.
  //! 
  //! @param interval that specifies the time period to stop the thread
  /*!
   *  Note: The thread may be suspended longer than interval!
   */
  DLLEXPORT void suspendThread(const Interval& interval);
}

#include "nmm/nmm_suffix.hpp"

#endif //_NMM_INTERVAL_HPP_
