/* time.hh - class definitions to work with time.
 * Copyright 2003-2006 Bas Wijnen <wijnen@debian.org>
 *
 * This program 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef SHEVEK_TIME_HH
#define SHEVEK_TIME_HH

#include <iostream>
#include <glibmm.h>

namespace shevek
{
  /// Schedule a callback for when the main loop has time.
  sigc::connection schedule (sigc::slot0 <void> callback, int prio = Glib::PRIORITY_HIGH_IDLE, Glib::RefPtr <Glib::MainContext> context = Glib::MainContext::get_default () );

  /// Type for storing the time.
  typedef int64_t timetype;
  class relative_time;

  /// The absolute_time class stores a date and time.
  /** Everything is in UTC, except the output of local_* ();
   */
  class absolute_time
  {
    // number of seconds since epoch.
    timetype m_seconds;
    // number of nanoseconds.  Should be less than 1000 000 000.
    unsigned m_nanoseconds;
    static bool l_schedule (sigc::slot0 <void> callback);
    // let schedule use l_schedule
    friend
    sigc::connection schedule (sigc::slot0 <void> callback, int prio,
			       Glib::RefPtr <Glib::MainContext> context);
    static unsigned s_digits;
  public:
    /// Create a new absolute_time containing the current time.
    /** Note that this makes a call to gettimeofday, which is much slower than specifying seconds and nanoseconds.
     *  Thus, if you need an absolute_time object to fill with an actual time later, use absolute_time foo (0, 0); instead of this default constructor.
     */
    absolute_time ();
    /// A specific time.
    /** days may be 0-365, with months 0.
     *  If months > 0, both days and months have a base of 1.
     */
    absolute_time (unsigned years, unsigned months, unsigned days, unsigned hours, unsigned minutes, unsigned seconds, unsigned nanoseconds = 0);
    /// Fast constructor.
    /** This directly fills the internal structures.
     *  It is therefore faster than the other constructors, in particular the default constructor, which makes a kernel call.
     */
    absolute_time (timetype seconds, unsigned nanoseconds);
    /// Semi-constructor which creates a new absolute_time given a date in local time.
    /** The input is the same as for the similar constructor.
     */
    static absolute_time create_from_local (unsigned years, unsigned months, unsigned days, unsigned hours, unsigned minutes, unsigned seconds, unsigned nanoseconds = 0);
    /// Set number of digits to use when printing (for fractions of seconds)
    static void set_digits (unsigned num);
    /// Get the number of digits which is used when printing.
    static unsigned get_digits ();
    /// Add an interval to this moment.
    absolute_time operator+ (relative_time that) const;
    /// Subtract an interval from this moment.
    absolute_time operator- (relative_time that) const;
    /// Compute the interval between two moments.
    relative_time operator- (absolute_time that) const;
    /// Add an interval to this moment.
    absolute_time &operator+= (relative_time that);
    /// Subtract an interval from this moment.
    absolute_time &operator-= (relative_time that);
    /// Compare two moments.
    bool operator< (absolute_time that) const;
    /// Compare two moments.
    bool operator> (absolute_time that) const;
    /// Compare two moments.
    bool operator<= (absolute_time that) const;
    /// Compare two moments.
    bool operator>= (absolute_time that) const;
    /// Compare two moments.
    /** Note that this is rarely a useful operation, because minor errors may be introduced by computations.
     *  In other words: only use this on times which have been set, never on times which have been computed.
     */
    bool operator== (absolute_time that) const;
    /// Compare two moments.
    /** Note that this is rarely a useful operation, because minor errors may be introduced by computations.
     *  In other words: only use this on times which have been set, never on times which have been computed.
     */
    bool operator!= (absolute_time that) const;
    /// Get the nanoseconds.
    unsigned nanoseconds () const;
    /// Get the seconds in local time.
    unsigned local_second () const;
    /// Get the minutes in local time.
    unsigned local_minute () const;
    /// Get the hour in local time.
    unsigned local_hour () const;
    /// Get the day of the year in local time, range 0-365.
    unsigned local_days () const;
    /// Get the day of the month in local time, range 1-31.
    unsigned local_day () const;
    /// Get the day of the week in local time, range 0-6 where 0 means sunday.
    unsigned local_weekday () const;
    /// Get the month in local time, range 1-12.
    unsigned local_month () const;
    /// Get the year in local time.
    unsigned local_year () const;
    /// Get the seconds in UTC.
    unsigned second () const;
    /// Get the minutes in UTC.
    unsigned minute () const;
    /// Get the hour in UTC.
    unsigned hour () const;
    /// Get the day of the year in UTC, range 0-365.
    unsigned days () const;
    /// Get the day of the month in UTC, range 1-31.
    unsigned day () const;
    /// Get the day of the week in UTC, range 0-6 where 0 means sunday.
    unsigned weekday () const;
    /// Get the month in UTC, range 1-12.
    unsigned month () const;
    /// Get the year in UTC.
    unsigned year () const;
    /// Total number of seconds since january 1970, as encoded.
    timetype total () const;
    /// Schedule a callback at a certain time.
    sigc::connection schedule (sigc::slot0 <void> callback, Glib::RefPtr <Glib::MainContext> context = Glib::MainContext::get_default ());
    /// Write the time to a std::ostream.
    friend std::ostream &operator<< (std::ostream &s, absolute_time t);
  };

  /// Time interval.
  class relative_time
  {
    // number of seconds.
    timetype m_seconds;
    // number of nanoseconds.  Should be less than 1000000000.
    int m_nanoseconds;
    static unsigned s_digits;
  public:
    /// The default constructor creates an interval of 0.
    relative_time ();
    /// Construct an interval of a given size.
    relative_time (timetype days, int hours, int minutes, int seconds, int nanoseconds = 0);
    /// Fast constructor.
    /** This directly fills the members and is therefore slightly faster than the other constructors.
     *  However, the others aren't really slow either.
     */
    relative_time (timetype seconds, unsigned nanoseconds);
    /// Set number of digits to use when printing (for fractions of seconds).
    static void set_digits (unsigned num);
    /// Get the number of digits that is used when printing.
    static unsigned get_digits ();
    /// Add two intervals.
    relative_time operator+ (relative_time that) const;
    /// Add an interval to a moment.
    absolute_time operator+ (absolute_time that) const;
    /// Subtract two intervals.
    relative_time operator- (relative_time that) const;
    /// Negate an interval.
    relative_time operator- () const;
    /// Scale an interval.
    relative_time operator* (float c) const;
    /// Scale an interval.
    relative_time operator/ (float c) const;
    /// Modulo operator for two intervals.
    relative_time operator% (relative_time that) const;
    /// Division of two intervals.
    double operator/ (relative_time that) const;
    /// Add an interval.
    relative_time &operator+= (relative_time that);
    /// Subtract an interval.
    relative_time &operator-= (relative_time that);
    /// Scale the interval.
    relative_time &operator*= (float c);
    /// Scale the interval.
    relative_time &operator/= (float c);
    /// Modulo.
    relative_time &operator%= (relative_time that);
    /// Compare with another interval.
    bool operator< (relative_time that) const;
    /// Compare with another interval.
    bool operator> (relative_time that) const;
    /// Compare with another interval.
    bool operator<= (relative_time that) const;
    /// Compare with another interval.
    bool operator>= (relative_time that) const;
    /// Compare two intervals.  Note that this is rarely a useful operation,
    /// because minor errors may be introduced by computations.
    bool operator== (relative_time that) const;
    /// Compare two intervals.  Note that this is rarely a useful operation,
    /// because minor errors may be introduced by computations.
    bool operator!= (relative_time that) const;
    /// Number of nanoseconds.
    unsigned nanoseconds () const;
    /// Number of seconds.
    unsigned seconds () const;
    /// Number of minutes.
    unsigned minutes () const;
    /// Number of hours.
    unsigned hours () const;
    /// Number of days.
    unsigned days () const;
    /// Is this a negative interval?
    bool isnegative () const;
    /// Total number of seconds, as encoded.
    timetype total () const;
    /// Write the interval to a std::ostream.
    friend std::ostream &operator<< (std::ostream &s, relative_time t);
  private:
    // internal function to clean the seconds/nanoseconds
    void l_clean ();
  };
  /// Read a time from a std::istream.
  std::istream &operator>> (std::istream &s, absolute_time &t);
  /// Read an interval from a std::istream.
  std::istream &operator>> (std::istream &s, relative_time &t);
}


#endif
