/* gstreamermm - a C++ wrapper for gstreamer
 *
 * Copyright 2008 The gstreamermm 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, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <gst/gstclock.h>
#include <gstreamermm/object.h>

_DEFS(gstreamermm,gst)

namespace Gst
{

class Clock;

/** A data type to hold a time, measured in nanoseconds.
 */
typedef GstClockTime ClockTime;

/** A data type to hold a timedifference, measured in nanoseconds.
 */
typedef GstClockTimeDiff ClockTimeDiff;

/** A constant to define an undefined clock time.
 */
static GLIBMM_API const ClockTime CLOCK_TIME_NONE = GST_CLOCK_TIME_NONE;

/** A constant that defines one GStreamer second.
 */
static GLIBMM_API const ClockTime SECOND = GST_SECOND;

/** A constant that defines one GStreamer millisecond.
 */
static GLIBMM_API const ClockTime MILLI_SECOND = GST_MSECOND;

/** A constant that defines one GStreamer microsecond.
 */
static GLIBMM_API const ClockTime MICRO_SECOND = GST_USECOND;

/** A constant that defines one GStreamer nanosecond.
 */
static GLIBMM_API const ClockTime NANO_SECOND = GST_NSECOND;

_WRAP_ENUM(ClockEntryType, GstClockEntryType)
_WRAP_ENUM(ClockReturn, GstClockReturn)
_WRAP_ENUM(ClockFlags, GstClockFlags)

/** A data type to hold the handle to an outstanding sync or async clock
 * callback.
 */
class ClockID
{
  _CLASS_OPAQUE_REFCOUNTED(ClockID, GstClockEntry, NONE, gst_clock_id_ref, gst_clock_id_unref)
  _IGNORE(gst_clock_id_ref, gst_clock_id_unref)

public:
  /** For example,
   * bool on_clock(const Glib::RefPtr<Gst::Clock>& clock, ClockTime time, const
   * Glib::RefPtr<Gst::ClockID>& id);.
   * The clock function return value is currently unused (see GstClockCallback).
   */
  typedef sigc::slot< bool, const Glib::RefPtr<Gst::Clock>&, ClockTime, const Glib::RefPtr<Gst::ClockID>& > SlotClock;

  _WRAP_METHOD(ClockTime get_time() const, gst_clock_id_get_time)
  _WRAP_METHOD(ClockReturn wait(ClockTimeDiff& jitter), gst_clock_id_wait)

  /** Perform a blocking wait without requiring a jitter argument.
   * Gst::ClockID should have been created with
   * Gst::Clock::create_single_shot_id() or Gst::Clock::create_periodic_id()
   * and should not have been unscheduled with a call to unschedule().
   * @return The result of the blocking wait. Gst::CLOCK_EARLY will be returned
   * if the current clock time is past the time of Gst::ClockID, Gst::CLOCK_OK
   * if Gst::ClockID was scheduled in time. Gst::CLOCK_UNSCHEDULED if
   * Gst::ClockID was unscheduled with unschedule().
   */
  ClockReturn wait();

  /** Register a slot on the given Gst::ClockID id with the given slot. When
   * passing a Gst::ClockID with an invalid time to this function, the slot
   * will be called immediatly with a time set to Gst::CLOCK_TIME_NONE. The
   * slot will be called when the time of id has been reached.
   *
   * @param slot The slot to callback.
   * @return The result of the non blocking wait. MT safe. 
   */
  ClockReturn wait_async(const SlotClock& slot);
  _IGNORE(gst_clock_id_wait_async)

  _WRAP_METHOD(void unschedule(), gst_clock_id_unschedule)
};

/** Gets the hour component of Gst::ClockTime.
 * @param time The Gst::ClockTime to parse.
 * @return The hour component of the Gst::ClockTime.
 */
guint get_hours(ClockTime time);

/** Gets the minute component of Gst::ClockTime.
 * @param time The Gst::ClockTime to parse.
 * @return The minute component of the Gst::ClockTime.
 */
guint get_minutes(ClockTime time);

/** Gets the seconds component of Gst::ClockTime.
 * @param time The Gst::ClockTime to parse.
 * @return The seconds component of the Gst::ClockTime.
 */
guint get_seconds(ClockTime time);

/** Gets the milliseconds component of Gst::ClockTime.
 * @param time The Gst::ClockTime to parse.
 * @return The milliseconds component of the Gst::ClockTime.
 */
guint get_milliseconds(ClockTime time);

/** Gets the microseconds component of Gst::ClockTime.
 * @param time The Gst::ClockTime to parse.
 * @return The microseconds component of the Gst::ClockTime.
 */
guint get_microseconds(ClockTime time);

/** Gets the nanoseconds component of Gst::ClockTime.
 * @param time The Gst::ClockTime to parse.
 * @return The nanoseconds component of the Gst::ClockTime.
 */
guint get_nanoseconds(ClockTime time);

/** Gets the fractional component (milli, micro and nano seconds) of
 * Gst::ClockTime.
 * @param time The Gst::ClockTime to parse.
 * @return The fractional component of the Gst::ClockTime.
 */
guint get_fractional_seconds(ClockTime time);

/** An abstract class for global clocks.
 * GStreamer uses a global clock to synchronize the plugins in a pipeline.
 * Different clock implementations are possible by implementing this abstract
 * base class.
 *
 * The Gst::Clock returns a monotonically increasing time with the method
 * get_time(). Its accuracy and base time depend on the specific clock
 * implementation but time is always expressed in nanoseconds. Since the
 * baseline of the clock is undefined, the clock time returned is not
 * meaningful in itself, what matters are the deltas between two clock times.
 * The time returned by a clock is called the absolute time.
 *
 * The pipeline uses the clock to calculate the stream time. Usually all
 * renderers synchronize to the global clock using the buffer timestamps, the
 * newsegment events and the element's base time, see Gst::Pipeline.
 *
 * A clock implementation can support periodic and single shot clock
 * notifications both synchronous and asynchronous.
 *
 * One first needs to create a Gst::ClockID for the periodic or single shot
 * notification using create_single_shot_id() or create_periodic_id().
 *
 * To perform a blocking wait for the specific time of the Gst::ClockID use the
 * Gst::ClockID::wait() method. To receive a callback when the specific time is
 * reached in the clock use Gst::ClockID::wait_async(). Both these calls can be
 * interrupted with the Gst::ClockID::unschedule() call. If the blocking wait
 * is unscheduled a return value of Gst::CLOCK_UNSCHEDULED is returned.
 *
 * Periodic callbacks scheduled async will be repeadedly called automatically
 * until it is unscheduled. To schedule a sync periodic callback,
 * Gst::ClockID::wait() should be called repeadedly.
 *
 * The async callbacks can happen from any thread, either provided by the core
 * or from a streaming thread. The application should be prepared for this.
 *
 * A Gst::ClockID that has been unscheduled cannot be used again for any wait
 * operation, a new Gst::ClockID should be created.
 *
 * It is possible to perform a blocking wait on the same ClockID from multiple
 * threads. However, registering the same ClockID for multiple async
 * notifications is not possible, the slot will only be called for the thread
 * registering the entry last.
 *
 * None of the wait operations unref the Gst::ClockID, the owner is responsible
 * for unreffing the ids itself. This holds for both periodic and single shot
 * notifications. The reason being that the owner of the Gst::ClockID has to
 * keep a handle to the Gst::ClockID to unblock the wait on FLUSHING events or
 * state changes and if the entry would be unreffed automatically, the handle
 * might become invalid without any notification. (The RefPtr takes care of
 * unreffing in this case)
 *
 * These clock operations do not operate on the stream time, so the callbacks
 * will also occur when not in PLAYING state as if the clock just keeps on
 * running. Some clocks however do not progress when the element that provided
 * the clock is not PLAYING.
 *
 * When a clock has the Gst::CLOCK_FLAG_CAN_SET_MASTER flag set, it can be
 * slaved to another Gst::Clock with the set_master() method. The clock will
 * then automatically be synchronized to this master clock by repeadedly
 * sampling the master clock and the slave clock and recalibrating the slave
 * clock with set_calibration(). This feature is mostly useful for plugins that
 * have an internal clock but must operate with another clock selected by the
 * Gst::Pipeline.  They can track the offset and rate difference of their
 * internal clock relative to the master clock by using the get_calibration()
 * method.
 *
 * The master/slave synchronisation can be tuned with the "timeout",
 * "window-size" and "window-threshold" properties. The "timeout" property
 * defines the interval to sample the master clock and run the calibration
 * functions. "window-size" defines the number of samples to use when
 * calibrating and "window-threshold" defines the minimum number of samples
 * before the calibration is performed.
 *
 * Last reviewed on 2006-08-11 (0.10.10)
 */
class Clock : public Object
{
  _CLASS_GOBJECT(Clock, GstClock, GST_CLOCK, Object, GstObject)

public:
  _WRAP_METHOD(bool add_observation(ClockTime slave_time, ClockTime master_time, gdouble* result), gst_clock_add_observation)
  _WRAP_METHOD(bool set_master(const Clock& master_clock), gst_clock_set_master)
  _WRAP_METHOD(Glib::RefPtr<Gst::Clock> get_master(), gst_clock_get_master)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Clock> get_master() const, gst_clock_get_master, constversion)
  _WRAP_METHOD(ClockTime set_resolution(ClockTime resolution), gst_clock_set_resolution)
  _WRAP_METHOD(ClockTime get_resolution() const, gst_clock_get_resolution)
  _WRAP_METHOD(ClockTime get_time() const, gst_clock_get_time)
  _WRAP_METHOD(Glib::RefPtr<Gst::ClockID> create_single_shot_id(ClockTime time), gst_clock_new_single_shot_id)
  _WRAP_METHOD(Glib::RefPtr<Gst::ClockID> create_periodic_id(ClockTime start_time, ClockTime intervals), gst_clock_new_periodic_id)
  _WRAP_METHOD(ClockTime get_internal_time() const, gst_clock_get_internal_time)
  _WRAP_METHOD(ClockTime adjust_unlocked(ClockTime internal_time), gst_clock_adjust_unlocked)
  _WRAP_METHOD(ClockTime unadjust_unlocked(ClockTime external_time), gst_clock_unadjust_unlocked)
  _WRAP_METHOD(void get_calibration(ClockTime& internal, ClockTime& external, ClockTime& rate_num, ClockTime& rate_denom) const, gst_clock_get_calibration)
  _WRAP_METHOD(void set_calibration(ClockTime internal, ClockTime external, ClockTime rate_num, ClockTime rate_denom), gst_clock_set_calibration)
  
  _WRAP_PROPERTY("stats", bool)
  _WRAP_PROPERTY("timeout", guint64)
  _WRAP_PROPERTY("window-size", int)
  _WRAP_PROPERTY("window-threshold", int)

  /** Change the resolution of the clock. Not all values might be acceptable.
   * The new resolution should be returned.
   */
  _WRAP_VFUNC(ClockTime change_resolution(ClockTime old_resolution, ClockTime new_resolution), "change_resolution")

  /** Get the resolution of the clock.
   */
  _WRAP_VFUNC(ClockTime get_resolution() const, "get_resolution")

  /** Get the internal unadjusted time of the clock.
   */
  _WRAP_VFUNC(ClockTime get_internal_time() const, "get_internal_time")

#m4 _CONVERSION(`GstClockEntry*',`const Glib::RefPtr<Gst::ClockID>&',`Glib::wrap($3, true)')

  /** Perform a blocking wait for the given Gst::ClockID. Deprecated,
   * implement wait_jitter_vfunc() instead.
   */
  _WRAP_VFUNC(ClockReturn wait(const Glib::RefPtr<Gst::ClockID>& id), "wait")

  /** Perform an asynchronous wait for the given Gst::ClockID.
   */
  _WRAP_VFUNC(ClockReturn wait_async(const Glib::RefPtr<Gst::ClockID>& id), "wait_async")

  /** Unblock a blocking or async wait operation.
   */
  _WRAP_VFUNC(void unschedule(const Glib::RefPtr<Gst::ClockID>& id), "unschedule")

  /** Perform a blocking wait on the given Gst::ClockID and return the jitter.
   * (Since: 0.10.10)
   */
  _WRAP_VFUNC(ClockReturn wait_jitter(const Glib::RefPtr<Gst::ClockID>& id, ClockTimeDiff& jitter), "wait_jitter")
};

} //namespace Gst
