/* 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/gstquery.h>
#include <gstreamermm/miniobject.h>
#include <gstreamermm/structure.h>
#include <gstreamermm/wrap.h>
#include <gstreamermm/format.h>
#include <glibmm/arrayhandle.h>

_DEFS(gstreamermm,gst)

namespace Gst
{

//Gst::IteratorBasic<> forward declaration.
template <class CppType>
class IteratorBasic;

_WRAP_ENUM(QueryType, GstQueryType)
_WRAP_ENUM(BufferingMode, GstBufferingMode)

/** A class to store the details of a Gst::Query retrieved by
 * Gst::Query::get_details().
 */
class QueryTypeDefinition
{
  _CLASS_GENERIC(QueryTypeDefinition, GstQueryTypeDefinition)

public:
  /// Default constructor.
  QueryTypeDefinition();

  /** Constructs a Gst::QueryTypeDefinition from a C GstQueryTypeDefinition
   * type.  The @a castitem is left unaffected; its contents are simply copied.
   * @param castitem The GstQueryTypeDefinition to copy contents from.
   */
  explicit QueryTypeDefinition(const GstQueryTypeDefinition* castitem);

  /// The unique id of the Query type.
  QueryType             value;

  /// A short nickname for the query type.
  Glib::ustring         nick;

  /// A longer description of the query type.
  Glib::ustring         description;

  /// The quark for the nick.
  Glib::QueryQuark      quark;
};

namespace Enums
{

/** Gets a printable name for the given query type.
 *
 * @param query The query type.
 * @return The name of the query.
 */
Glib::ustring get_name(QueryType query);
_IGNORE(gst_query_type_get_name)

/** Gets the unique quark for the given query type.
 *
 * @param query The query type.
 * @return The quark associated with the query type.
 */
Glib::QueryQuark get_quark(QueryType query);
_IGNORE(gst_query_type_to_quark)

} //namespace Enums

/** A class used to perform queries on pads and elements.
*
 * Queries can be created using the derived Gst::Query classes create()
 * methods. Query values can be set using derived classes set() methods, and
 * parsed using derived classes parse() methods.
 *
 * New query types may also be registered to the GStreamer core using
 * register_query_type().
 *
 * The following example shows how to query the duration of a pipeline:
 *
 * @code
 * Glib::RefPtr<Gst::Query> query = Gst::QueryDuration::create(Gst::FORMAT_TIME);
 * bool res = pipeline->query(query);
 * if(res)
 * {
 *  gint64 duration = query.parse();
 *  ...
 * }
 * @endcode
 */
class Query : public MiniObject
{
  _CLASS_GSTMINIOBJECT(Query, GstQuery, GST_QUERY, Gst::MiniObject, GstMiniObject)
  _IGNORE(gst_query_ref, qgst_query_unref)
  _CUSTOM_WRAP_NEW

public:
  /** Wrap a GstQuery in a C++ instance, creating an instance of a derived
   * Gst::Query. Gst::wrap() would just create a Gst::Query (rather than a
   * derived one) because the derived Gst::Query classes do not correspond
   * to GType types in the GStreamer API.
   */
  static Glib::RefPtr<Gst::Query> wrap(GstQuery* query, bool take_copy = false);

   /** Makes a writable query from the given query.  Does exactly what
    * Gst::MiniObject::create_writable() does for the Gst::Query.
    * @return A Gst::Query (possibly the same pointer) that is writable.
    */
   Glib::RefPtr<Gst::Query> create_writable();

// A copy is taken so that the original is not freed by the wrapper.
#m4 _CONVERSION(`GstStructure*',`Gst::Structure',`Glib::wrap(const_cast<GstStructure*>($3), true)')
  _WRAP_METHOD(Gst::Structure get_structure() const, gst_query_get_structure)

  /** Get the Gst::QueryType of the query.
   */
  _MEMBER_GET(query_type, type, QueryType, GstQueryType)

  _WRAP_METHOD(static QueryType register_query_type(const Glib::ustring& nick, const Glib::ustring& description), gst_query_type_register)
  _WRAP_METHOD(static QueryType get_query_type(const Glib::ustring& nick), gst_query_type_get_by_nick)

#m4 _CONVERSION(`const Glib::ArrayHandle<const QueryType>&',`GstQueryType*',`(GstQueryType*)(($3).data())')
  _WRAP_METHOD(static bool query_types_contain(const Glib::ArrayHandle<const QueryType>& types, QueryType type), gst_query_types_contains)

  /** Get details about the given Gst::QueryType.
   *
   * @param type A Gst::QueryType.
   * @param def A Gst::QueryTypeDefinition in which to store the details.
   * @return true if successful, false otherwise.
   */
  bool get_details(QueryType type, QueryTypeDefinition& def) const;
  _IGNORE(gst_query_type_get_details)

  _WRAP_METHOD(static Gst::IteratorBasic<const Gst::QueryTypeDefinition> iterate_definitions(), gst_query_type_iterate_definitions)

  /** Virtual function called when the Gst::Query needs to be copied.
   */
  virtual Glib::RefPtr<Gst::MiniObject> copy_vfunc() const;

  /** Virtual function called when the Gst::Query is about to be finalized.
   */
  virtual void finalize_vfunc();

protected:
#m4begin
  _PUSH(SECTION_PCC_CLASS_INIT_VFUNCS)
  GST_MINI_OBJECT_CLASS(klass)->copy = &copy_vfunc_callback;
  GST_MINI_OBJECT_CLASS(klass)->finalize = &finalize_vfunc_callback;
  _SECTION(SECTION_PH_VFUNCS)
  static GstMiniObject* copy_vfunc_callback(const GstMiniObject* self);
  static void finalize_vfunc_callback(GstMiniObject* self);
  _POP()
#m4end
};

//TODO: Modify create methods of derived Query classes to return
//Glib::RefPtr<...> to the derived class and not just Gst::Query. Must deal
//with GstStructure immutability problem (bug #510301) first because casting
//Glib::RefPtrs references the objects which causes problems when GStreamer API
//tries to modify the GstStructures of the objects.

/** A custom application query object.  See create() for more details.
 */
class QueryApplication : public Query
{
public:
  explicit QueryApplication(GstQuery* query);

  /** Constructs a new custom application query object.  The Gst::Query may be
   * used by applications in their own way.  Please note that a custom
   * Gst::QueryType must be used in order for the returned Gst::Query to
   * actually be a Gst::QueryApplication type.
   * @param type The query type.
   * @param structure A structure for the query.
   * @return The new Gst::QueryApplication.
   */
  static Glib::RefPtr<Gst::Query> create(QueryType type, Gst::Structure& structure);
};

/** A convert query object.  See create() for more details.
 */
class QueryConvert : public Query
{
public:
  explicit QueryConvert(GstQuery* query);

  /** Constructs a new convert query object.  A convert query is used to ask
   * for a conversion between one format and another.
   * @param src_format The source Gst::Format for the new query.
   * @param value The value to convert.
   * @param dest_format The target Gst::Format.
   * @return The new Gst::QueryConvert.
   */
  static Glib::RefPtr<Gst::Query> create(Format src_format, gint64 value, Format dest_format);

  /** Answer a convert query by setting the requested values.
   * @param src_format The source Gst::Format.
   * @param src_value The source value.
   * @param dest_format The destination Gst::Format.
   * @param dest_value The destination value.
   */
  void set(Format src_format, gint64 src_value, Format dest_format, gint64 dest_value);

  /** Parse a convert query answer.  Use the other parse() methods for parsing
   * individual values.
   * @param src_format The storage for the Gst::Format of the source value.
   * @param src_value The storage for the source value.
   * @param dest_format The storage for the Gst::Format of the destination.
   * value.
   * @param dest_value The storage for the destination value.
   */
  void parse(Format& src_format, gint64& src_value, Format& dest_format, gint64& dest_value) const;

  /** Parse the destination format and value of a convert query answer.
   * @param dest_format The storage for the Gst::Format of the destination
   * value.
   * @param dest_value The storage for the destination value.
   */
  void parse(Format& dest_format, gint64& dest_value) const;

  /** Parse the source format of a convert query answer, returning the format.
   * @return The Gst::Format of the source value.
   */
  Format parse_src_format() const;

  /** Parse the source value of a convert query answer returning, the value.
   * @return The source value.
   */
  gint64 parse_src_value() const;

  /** Parse the destination format of a convert query answer, returning the
   * format.
   * @return The Gst::Format of the destination value.
   */
  Format parse_dest_format() const;

  /** Parse the destination value of a convert query answer, returning the
   * value.
   * @return The destination value.
   */
  gint64 parse_dest_value() const;
};

/** A stream position query object.  See create() for more details.
 */
class QueryPosition : public Query
{
public:
  explicit QueryPosition(GstQuery* query);

  /** Constructs a new query stream position query object. A position query is
   * used to query the current position of playback in the streams, in some
   * format.
   * @param format The default Gst::Format for the new query.
   * @return The new Gst::QueryPosition.
   */
  static Glib::RefPtr<Gst::Query> create(Format format);

  /** Answer a position query by setting the requested value in the given
   * format.
  * @param format The requested Gst::Format.
  * @param position The position to set.
   */
  void set(Format format, gint64 position);

  /** Parse a position query, writing the format into format, and the position
   * into @a position.  Use the other parse() methods for parsing individual
   * values.
   * @param format The storage for the Gst::Format of the position values.
   * @param position The storage for the current position.
   */
  void parse(Format& format, gint64& position) const;

  /** Parse the position of a position query, returning the position.
   * @return The current position of the position query.
   */
  gint64 parse() const;

  /** Parse the format of a position query, returning the format.
   * @return The the Gst::Format of the position values.
   */
  Format parse_format() const;
};

/** A stream duration query object.  See create() for more details.
 */
class QueryDuration : public Query
{
public:
  explicit QueryDuration(GstQuery* query);

  /** Constructs a new stream duration query object to query in the given
   * format. A duration query will give the total length of the stream.
   * @param format The Gst::Format for this duration query.
   * @return The new Gst::QueryDuration.
   */
  static Glib::RefPtr<Gst::Query> create(Format format);

  /** Answer a duration query by setting the requested value in the given
   * format.
   * @param format The Gst::Format for the duration.
   * @param duration The duration of the stream.
   */
  void set(Format format, gint64 duration);

  /** Parse a duration query answer. Write the format of the duration into
   * @a format, and the value into @a duration.  Use the other parse() methods
   * for parsing individual values.
   * @param format The storage for the Gst::Format of the duration value.
   * @param duration The storage for the total duration.
   */
  void parse(Format& format, gint64& duration) const;

  /** Parse a duration query answer, returning the duration.  
   * @return The total duration.
   */
  gint64 parse() const;

  /** Parse a duration query answer, returning the format of the duration. 
   * @return The Gst::Format of the duration value.
   */
  Format parse_format() const;
};

/** A latency query object.  See create() for more details.
 */
class QueryLatency : public Query
{
public:
  explicit QueryLatency(GstQuery* query);

  /** Constructs a new latency query object. A latency query is usually
   * performed by sinks to compensate for additional latency introduced by
   * elements in the pipeline.
   * @return The new Gst::QueryLatency.
   */
  static Glib::RefPtr<Gst::Query> create();

  /** Answer a latency query by setting the requested values in the given
   * format.
   * @param live If there is a live element upstream.
   * @param min_latency The minimal latency of the live element.
   * @param max_latency The maximal latency of the live element.
   */
  void set(bool live, ClockTime min_latency, ClockTime max_latency);

  /** Parse a latency query answer.  Use the other parse() methods for parsing
   * individual values.
   * @param live Storage for live.
   * @param min_latency The storage for the min latency.
   * @param max_latency The storage for the max latency.
   */
  void parse(bool& live, ClockTime& min_latency, ClockTime& max_latency) const;

  /** Parse a latency query answer, returning the live status.
   * @return the live satus.
   */
  bool parse_live() const;

  /** Parse a latency query answer, returning the minimum latency.
   * @return The minimum latency.
   */
  ClockTime parse_min() const;

  /** Parse a latency query answer, returning the maximum latency.
   * @return The maximum latency.
   */
  ClockTime parse_max() const;
};

/** A seeking query object.  See create() for more details.
 */
class QuerySeeking : public Query
{
public:
  explicit QuerySeeking(GstQuery* query);

  /** Constructs a new query object for querying seeking properties of the
   * stream.
   * @param format The default Gst::Format for the new query.
   * @return The new Gst::QuerySeeking.
   */
  static Glib::RefPtr<Gst::Query> create(Format format);

  /** Set the seeking query result fields in query.
   * @param format The format to set for @a the segment_start and
   * @a segment_end values.
   * @param seekable The seekable flag to set.
   * @param segment_start The segment_start to set.
   * @param segment_end The segment_end to set.
   */
  void set(Format format, bool seeakable, gint64 segment_start, gint64 segment_end);

  /** Parse a seeking query, writing the format into format, and other results
   * into the passed parameters.  Use the other parse() methods for parsing
   * individual values.
   * @param format The storage location for the foramt.
   * @param seekable The storage location for the seekable flag.
   * @param segment_start The storage location for the segment start value.
   * @param segment_end The storage location for the segment end.
   */
  void parse(Format& format, bool& seekable, gint64& segment_start, gint64& segment_end) const;

  /** Parse a seeking query, returning the format.
   * @return The format.
   */
  Format parse_format() const;

  /** Parse a seeking query, returning the seekable status.
   * @return The seekable flag.
   */
  bool parse_seekable() const;

  /** Parse a seeking query, returning the segment start value.
   * @return The segment start.
   */
  gint64 parse_start() const;

  /** Parse a seeking query, returning the segment end value.
   * @return The segment end.
   */
  gint64 parse_end() const;
};

/** A formats query object.  See create() for more details.
 */
class QueryFormats : public Query
{
public:
  explicit QueryFormats(GstQuery* query);

  /** Constructs a new query object for querying formats of the stream.
   * @return The new Gst::QueryFormats.
   */
  static Glib::RefPtr<Gst::Query> create();

  /** Set the formats query result fields. All the formats in the array are
   * used.
   * @param formats An array containing Gst::Format values. 
   */
  void set(const Glib::ArrayHandle<Format>& formats);

  /** Set the formats query result fields using only @a n_formats from @a
   * formats. The number of formats passed in the formats array must be
   * greater than or equal to @a n_formats.
   * @param n_formats The number of formats to set.
   * @param formats An array containing at least @a n_formats Gst::Format
   * values. 
   */
  void set(int n_formats, const Glib::ArrayHandle<Format>& formats);

  /** Parse and return the number of formats in the formats query.
   * @return The number of formats in this query.
   */
  guint parse_length() const;

  /** Parse the format query and return the @a nth format from it. If the list
   * contains less elements than @a nth, Gst::FORMAT_UNDEFINED will be
   * returned.
   * @param nth The index of the format to return.
   * @return the @a nth format or Gst::FORMAT_UNDEFINED.
   */
  Format parse(guint nth) const;
};

/** A new segment query object.  See create() for more details.
 */
class QuerySegment : public Query
{
public:
  explicit QuerySegment(GstQuery* query);

  /** Constructs a new segment query object. A segment query is used to
   * discover information about the currently configured segment for playback.
   * @param format The Gst::Format for the new query.
   * @return The new Gst::QuerySegment.
   */
  static Glib::RefPtr<Gst::Query> create(Format format);

  /** Answer a segment query by setting the requested values. The normal
   * playback segment of a pipeline is 0 to duration at the default rate of
   * 1.0. If a seek was performed on the pipeline to play a different segment,
   * this query will return the range specified in the last seek.
   *
   * @a start_value and @a stop_value will respectively contain the configured
   * playback range start and stop values expressed in @a format. The values
   * are always between 0 and the duration of the media and @a start_value <=
   * @a stop_value. @a rate will contain the playback rate. For negative
   * rates, playback will actually happen from @a stop_value to @a start_value.
   *
   * @param rate The rate of the segment.
   * @param format The Gst::Format of the segment values (@a start_value and
   * @a stop_value).
   * @param start_value The start value.
   * @param stop_value The stop value.
   */
  void set(double rate, Format format, gint64 start_value, gint64 stop_value);

  /** Parse a segment query answer.  See set() for an explanation of the
   * function arguments.  Use the other parse() methods to parse individual
   * values.
   *
   * @param rate The storage for the rate of the segment.
   * @param format The storage for the Gst::Format of the values.
   * @param start_value The storage for the start value.
   * @param stop_value The storage for the stop value.
   */
  void parse(double& rate, Format& format, gint64& start_value, gint64& stop_value) const;

  /** Parse a segment query answer, returning the rate.  See set() for an
   * explanation of the function arguments.  
   * @return The rate of the segment.
   */
  double parse_rate() const;

  /** Parse a segment query answer, returning the format.  See set() for an
   * explanation of the function arguments.
   * @return The Gst::Format of the start and stop values.
   */
  Format parse_format() const;

  /** Parse a segment query answer, returning the start value.  See set() for
   * an explanation of the function arguments.
   * @return The start value.
   */
  gint64 parse_start() const;

  /** Parse a segment query answer, returning the stop value.  See set() for
   * an explanation of the function arguments.
   * @return The stop value.
   */
  gint64 parse_stop() const;
};

/** A new buffering query object.  See create() for more details.
 */
class QueryBuffering : public Query
{
public:
  explicit QueryBuffering(GstQuery* query);

  /** Constructs a new query object for querying the buffering status of a
   * stream.
   * @param format The default Gst::Format for the new query.
   * @return The new Gst::QueryBuffering.
   */
  static Glib::RefPtr<Gst::Query> create(Format format);

  /** Set the percentage of buffered data. This is a value between 0 and 100.
   * The @a busy indicator is true when the buffering is in progress.
   * @param busy If buffering is busy.
   * @param percent A buffering percent.
   */
  void set(bool busy, int percent);

  /** Get the busy flag and percentage of the buffered data. The percent is a
   * value between 0 and 100.  The busy indicator is true when the buffering
   * is in progress.  Use the other parse() methods to parse individual
   * values.
   * @param busy The location to store the buffering busy flag.
   * @param percent The location to store the buffering percent.
   */
  void parse(bool& busy, int& percent) const;

  /** Get the busy flag of the buffered data. The busy indicator is true when
   * the buffering is in progress.
   * @return The buffering busy flag.
   */
  bool parse_busy() const;

  /** Get the percentage of the buffered data. The percent is a value between
   * 0 and 100.
   * @return The buffering percent.
   */
  int parse_percent() const;

  /** Configures the buffering stats values in query.
   * @param mode A buffering mode.
   * @param avg_in The average input rate.
   * @param avg_out The average output rate.
   * @param buffering_left Amount of buffering time left.
   */
  void set(BufferingMode mode, int avg_in, int avg_out, gint64 buffering_left);

  /** Extracts the buffering stats values from the query.  Use the other
   * parse() methods to parse individual values.
   * @param mode The location to store the buffering mode.
   * @param avg_in The location to store the average input rate.
   * @param avg_out The location to store the average output rate.
   * @param buffering_left The location to store the amount of buffering time
   * left.
   */
  void parse(BufferingMode& mode, int& avg_in, int& avg_out, gint64 buffering_left) const;

  /** Extracts the buffering mode from the query.
   * @return The the buffering mode.
   */
  BufferingMode parse_mode() const;

  /** Extracts the average input rate from the query.
   * @return The average input rate.
   */
  int parse_input_rate() const;

  /** Extracts the average output rate from the query.
   * @return The average output rate.
   */
  int parse_output_rate() const;

  /** Extracts the buffering time left from the query.
   * @return The the amount of buffering time left.
   */
  gint64 parse_time_left() const;

  /** Set the available query result fields in query.
   * @param format The format to set for the start and stop values.
   * @param start The start to set.
   * @param stop The stop to set.
   * @param estimated_total Estimated total amount of download time.
   */
  void set(Format format, gint64 start, gint64 stop, gint64 estimated_total);

  /** Parse the query, writing the format into format, and other
   * results into the passed parameters.  Use the other parse() methods to
   * parse individual values.
   * @param format The location to store the format.
   * @param start The location to store the start.
   * @param stop The location to store the stop.
   * @param estimated_total The location to store the estimated total amount
   * of download time.
   */
  void parse(Format& format, gint64& start, gint64& stop, gint64& estimated_total) const;

  /** Parse the query, returning the format.
   * @return The format.
   */
  Format parse_format() const;

  /** Parse the query, returning the start range value.
   * @return The start value.
   */
  gint64 parse_start() const;

  /** Parse the query, returning the stop range value.
   * @return The stop value.
   */
  gint64 parse_stop() const;

  /** Parse the query, returning the estimated total amount of download time.
   * @return The estimated total amount of download time.
   */
  gint64 parse_total_time() const;
};

} //namespace Gst
