/* Copyright (C) 2011 The glibmm Development Team
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */

_DEFS(glibmm,glib)

#include <glibmmconfig.h>
#include <glibmm/refptr.h>
#include <glibmm/timezone.h>
#include <glibmm/ustring.h>
#include <glibmm/value.h>
#include <glib.h>

#ifndef DOXYGEN_SHOULD_SKIP_THIS
typedef struct _GDateTime GDateTime;
#endif

//TODO: When we can change API, make DateTime a _CLASS_BOXEDTYPE.

namespace Glib
{

struct TimeVal;

/** A value representing an interval of time, in microseconds.  As GTimeSpan,
 * its underlying type is gint64.
 */
using TimeSpan = GTimeSpan;

/** DateTime - A structure representing Date and Time.
 * DateTime is a structure that combines a Gregorian date and time into a
 * single structure. It provides many conversion and methods to manipulate
 * dates and times. Time precision is provided down to microseconds and the
 * time can range (proleptically) from 0001-01-01 00:00:00 to 9999-12-31
 * 23:59:59.999999. DateTime follows POSIX time in the sense that it is
 * oblivious to leap seconds.
 *
 * DateTime is an immutable object; once it has been created it cannot be
 * modified further. All modifiers will create a new DateTime. Nearly all such
 * functions can fail due to the date or time going out of range, in which case
 * <tt>0</tt> will be returned.
 *
 * Many parts of the API may produce non-obvious results. As an example, adding
 * two months to January 31st will yield March 31st whereas adding one month
 * and then one month again will yield either March 28th or March 29th. Also
 * note that adding 24 hours is not always the same as adding one day (since
 * days containing daylight savings time transitions are either 23 or 25 hours
 * in length).
 * @newin{2,30}
 */
class GLIBMM_API DateTime
{
  _CLASS_OPAQUE_COPYABLE(DateTime, GDateTime, NONE, g_date_time_ref, g_date_time_unref, GLIBMM_API)
  _IGNORE(g_date_time_ref, g_date_time_unref)

public:
  _WRAP_METHOD(static DateTime create_now(const TimeZone& tz), g_date_time_new_now)
  _WRAP_METHOD(static DateTime create_now_local(), g_date_time_new_now_local)
  _WRAP_METHOD(static DateTime create_now_utc(), g_date_time_new_now_utc)

  _WRAP_METHOD(static DateTime create_now_local(gint64 t), g_date_time_new_from_unix_local)
  _WRAP_METHOD(static DateTime create_now_utc(gint64 t), g_date_time_new_from_unix_utc)

  _WRAP_METHOD(static DateTime create_now_local(const TimeVal& tv), g_date_time_new_from_timeval_local,
    deprecated "Use create_now_local(gint64 t) instead.")

  _WRAP_METHOD(static DateTime create_now_utc(const TimeVal& tv), g_date_time_new_from_timeval_utc,
    deprecated "Use create_now_utc(gint64 t) instead.")

  _WRAP_METHOD(static DateTime create_from_iso8601(const Glib::ustring& text, const TimeZone& default_tz{?}),
    g_date_time_new_from_iso8601, newin "2,62")
  _WRAP_METHOD(static DateTime create(const TimeZone& tz, int year, int month, int day, int hour, int minute, double seconds), g_date_time_new)
  _WRAP_METHOD(static DateTime create_local(int year, int month, int day, int hour, int minute, double seconds), g_date_time_new_local)
  _WRAP_METHOD(static DateTime create_utc(int year, int month, int day, int hour, int minute, double seconds), g_date_time_new_utc)

  /** Returns true if the %DateTime object is valid.
   * This will return false, for instance, if the @a text in create_from_iso8601()
   * is not a valid ISO 8601 formatted string.
   *
   * @newin{2,62}
   */
  explicit operator bool() const;

  _WRAP_METHOD(DateTime add(TimeSpan timespan) const, g_date_time_add)
  _WRAP_METHOD(DateTime add_years(int years) const, g_date_time_add_years)
  _WRAP_METHOD(DateTime add_months(int months) const, g_date_time_add_months)
  _WRAP_METHOD(DateTime add_weeks(int weeks) const, g_date_time_add_weeks)
  _WRAP_METHOD(DateTime add_days(int days) const, g_date_time_add_days)
  _WRAP_METHOD(DateTime add_hours(int hours) const, g_date_time_add_hours)
  _WRAP_METHOD(DateTime add_minutes(int minutes) const, g_date_time_add_minutes)
  _WRAP_METHOD(DateTime add_seconds(double seconds) const, g_date_time_add_seconds)
  _WRAP_METHOD(DateTime add_full(int years, int months, int days, int hours, int minutes, double seconds) const, g_date_time_add_full)

  /** Calculates the difference in time between @a *this and @a other.  The
   * TimeSpan that is returned is effectively @a *this - @a other.
   *
   * @newin{2,26}
   *
   * @param other The other DateTime.
   * @return The difference between the two DateTime, as a time
   * span expressed in microseconds.
   */
  _WRAP_METHOD(TimeSpan difference(const DateTime& other) const, g_date_time_difference)

#m4 _CONVERSION(`const DateTime&',`gconstpointer',`static_cast<gconstpointer>($3.gobj())')
  /** A comparison function for DateTimes that is suitable
   * as a CompareFunc.
   *
   * @newin{2,26}
   *
   * @param other The DateTime to compare with.
   * @return -1, 0 or 1 if @a *this is less than, equal to or greater
   * than @a other.
   */
  _WRAP_METHOD(int compare(const DateTime& other) const, g_date_time_compare)

  _WRAP_METHOD(guint hash() const, g_date_time_hash)

  /** Checks to see if @a *this and @a other are equal.
   *
   * Equal here means that they represent the same moment after converting
   * them to the same time zone.
   *
   * @newin{2,26}
   *
   * @param other The DateTime to compare with.
   * @return <tt>true</tt> if @a *this and @a other are equal.
   */
  _WRAP_METHOD(bool equal(const DateTime& other) const, g_date_time_equal)

  _WRAP_METHOD(void get_ymd(int& year, int& month, int& day) const, g_date_time_get_ymd)
  _WRAP_METHOD(int get_year() const, g_date_time_get_year)
  _WRAP_METHOD(int get_month() const, g_date_time_get_month)
  _WRAP_METHOD(int get_day_of_month() const, g_date_time_get_day_of_month)
  _WRAP_METHOD(int get_week_numbering_year() const, g_date_time_get_week_numbering_year)
  _WRAP_METHOD(int get_week_of_year() const, g_date_time_get_week_of_year)
  _WRAP_METHOD(int get_day_of_week() const, g_date_time_get_day_of_week)
  _WRAP_METHOD(int get_day_of_year() const, g_date_time_get_day_of_year)
  _WRAP_METHOD(int get_hour() const, g_date_time_get_hour)
  _WRAP_METHOD(int get_minute() const, g_date_time_get_minute)
  _WRAP_METHOD(int get_second() const, g_date_time_get_second)
  _WRAP_METHOD(int get_microsecond() const, g_date_time_get_microsecond)
  _WRAP_METHOD(double get_seconds() const, g_date_time_get_seconds)
  _WRAP_METHOD(gint64 to_unix() const, g_date_time_to_unix)

  _WRAP_METHOD(bool to_timeval(TimeVal& tv) const, g_date_time_to_timeval,
    deprecated "Use to_unix() instead.")

  _WRAP_METHOD(TimeSpan get_utc_offset() const, g_date_time_get_utc_offset)
#m4 _CONVERSION(`GTimeZone*',`TimeZone',`Glib::wrap($3, true)')
  _WRAP_METHOD(TimeZone get_timezone() const, g_date_time_get_timezone, newin "2,60")
  _WRAP_METHOD(Glib::ustring get_timezone_abbreviation() const, g_date_time_get_timezone_abbreviation)
  _WRAP_METHOD(bool is_daylight_savings() const, g_date_time_is_daylight_savings)
  _WRAP_METHOD(DateTime to_timezone(const TimeZone& tz) const, g_date_time_to_timezone)
  _WRAP_METHOD(DateTime to_local() const, g_date_time_to_local)
  _WRAP_METHOD(DateTime to_utc() const, g_date_time_to_utc)
  _WRAP_METHOD(Glib::ustring format(const Glib::ustring& format_str) const, g_date_time_format)
  _WRAP_METHOD(Glib::ustring format_iso8601() const, g_date_time_format_iso8601)
};

#ifndef DOXYGEN_SHOULD_SKIP_THIS
// This is needed so Glib::DateTime can be used with Glib::Value and _WRAP_PROPERTY.
template <>
class GLIBMM_API Value<Glib::DateTime> : public ValueBase_Boxed
{
public:
  using CppType = Glib::DateTime;
  using CType = GDateTime*;

  static GType value_type();

  void set(const CppType& data);
  CppType get() const;
};
#endif /* DOXYGEN_SHOULD_SKIP_THIS */

} // namespace Glib
