/* 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/gst.h>
#include <gstreamermm/miniobject.h>
#include <gstreamermm/format.h>
#include <gstreamermm/clock.h>
#include <gstreamermm/segment.h>
#include <gstreamermm/caps.h>

_DEFS(gstreamermm,gst)

namespace Glib
{
    template<typename T>
    Glib::RefPtr<T> wrap_event_derived(GstEvent* object, bool take_copy)
    {
      if(take_copy && object)
        gst_event_ref(object);

      // See the comment at the top of this file, if you want to know why the cast works.
      return Glib::RefPtr<T>(reinterpret_cast<T*>(object));
    }

}

namespace Gst
{

#define GST_EVENT_MAKE_TYPE(num,flags) \
    (((num) << GST_EVENT_NUM_SHIFT) | (flags))

#define FLAG(name) GST_EVENT_TYPE_##name

_WRAP_ENUM(EventType, GstEventType)
_WRAP_ENUM(EventTypeFlags, GstEventTypeFlags)
_WRAP_ENUM(QOSType, GstQOSType)

#undef FLAG

class Message;
class Structure;
class TagList;

namespace Enums
{

/** Gets a printable name for the given event type.
 *
 * @param type The event type.
 * @return A reference to the static name of the event.
 */
Glib::ustring get_name(EventType type);

/** Gets the unique quark for the given event type.
 *
 * @param type The event type.
 * @return The quark associated with the event type.
 */
Glib::QueryQuark get_quark(EventType type);

/** Gets the Gst::EventTypeFlags associated with type.
 * @param type A Gst::EventType.
 * @return A Gst::EventTypeFlags.
 */
EventTypeFlags get_flags(EventType type);

} //namespace Enums

/** A class that represents events that are passed up and down a pipeline.
 * The event class provides factory methods to construct and functions query
 * (parse) events.
 *
 * Events are usually created by using the Gst::Event derrived classes'
 * create() methods. To send an event application will usually use
 * Gst::Element::send_event() and elements will use Gst::Pad::send_event() or
 * Gst::Pad::push_event().
 *
 * Events that have been received can be parsed with their respective parse()
 * functions.
 *
 * Events are passed between elements in parallel to the data stream. Some
 * events are serialized with buffers, others are not. Some events only travel
 * downstream, others only upstream. Some events can travel both upstream and
 * downstream.
 *
 * The events are used to signal special conditions in the datastream such as
 * EOS (end of stream) or the start of a new stream-segment. Events are also
 * used to flush the pipeline of any pending data.
 *
 * Most of the event API is used inside plugins. Applications usually only
 * construct and use seek events. To do that Gst::EventSeek::create() is used
 * to create a seek event. It takes the needed parameters to specity seeking
 * time and mode.  The event is then sent to the element like so:
 * @code
 * pipeline->send_event(event);
 * @endcode
 */
class Event : public MiniObject
{
  _CLASS_OPAQUE_REFCOUNTED(Event, GstEvent, NONE, gst_event_ref, gst_event_unref)
  _IGNORE(gst_event_ref, gst_event_unref)

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

  _WRAP_METHOD(bool has_name(const Glib::ustring& name) const, gst_event_has_name)

  _WRAP_METHOD(guint32 get_seqnum() const, gst_event_get_seqnum)
  _WRAP_METHOD(void set_seqnum(guint32 seqnum), gst_event_set_seqnum)

  /**
   * Makes a writable event from the given event. If the source event is
   * already writable, this will simply return the same event.
   * @return A Gst::Event (possibly the same reference) that is writable.
   */
  Glib::RefPtr<Gst::Event> create_writable();

  /** Check if an event can travel downstream.
   */
  bool is_downstream() const;

  /** Check if an event is serialized with the data stream.
   */
  bool is_serialized() const;

  /** Check if an event can travel upstream.
   */
  bool is_upstream() const;

  /** Get the Gst::EventType of the event.
   */
  _MEMBER_GET(event_type, type, EventType, GstEventType)

  /** Get the Gst::ClockTime timestamp of the event. This is the time when the
   * event was created.
   */
  _MEMBER_GET(timestamp, timestamp, ClockTime, guint64)
};

//TODO: Modify create methods of derived Event classes to return
//Glib::RefPtr<...> to the derived class and not just Gst::Event. 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 flush start event.  See create() for more details.
 */
class EventFlushStart : public Event
{
public:
  /** Allocate a new flush start event. The flush start event can be sent
   * upstream and downstream and travels out-of-bounds with the dataflow.
   *
   * It marks pads as being flushing and will make them return
   * Gst::FLOW_WRONG_STATE when used for data flow with Gst::Pad::push(),
   * Gst::Pad::chain(), Gst::Pad::alloc_buffer(), Gst::Pad::get_range() and
   * Gst::Pad::pull_range(). Any event (except a Gst::EVENT_FLUSH_STOP)
   * received on a flushing pad will return false immediately.
   *
   * Elements should unlock any blocking functions and exit their streaming
   * functions as fast as possible when this event is received.
   *
   * This event is typically generated after a seek to flush out all queued
   * data in the pipeline so that the new media is played as soon as possible.
   *
   * @return A new flush start event.
   */
  static Glib::RefPtr<Gst::EventFlushStart> create();
};

/**  A flush stop event.  See create() for more details.
 */
class EventFlushStop : public Event
{
public:

 /**
  * gst_event_new_flush_stop:
 * @reset_time: if time should be reset
  *
  * Allocate a new flush stop event. The flush stop event can be sent
  * upstream and downstream and travels serialized with the dataflow.
  * It is typically sent after sending a FLUSH_START event to make the
  * pads accept data again.
  *
  * Elements can process this event synchronized with the dataflow since
  * the preceeding FLUSH_START event stopped the dataflow.
  *
  * This event is typically generated to complete a seek and to resume
  * dataflow.
  *
  * Returns: (transfer full): a new flush stop event.
  */
  static Glib::RefPtr<Gst::EventFlushStop> create(bool reset_time);
};

/** An end of stream event. See create() for more details.
 */
class EventEos : public Event
{
public:

  /** Create a new EOS event. The eos event can only travel downstream
   * synchronized with the buffer flow. Elements that receive the EOS event on
   * a pad can return Gst::FLOW_UNEXPECTED as a Gst::FlowReturn when data after
   * the EOS event arrives.
   *
   * The EOS event will travel down to the sink elements in the pipeline which
   * will then post the Gst::MESSAGE_EOS on the bus after they have finished
   * playing any buffered data.
   *
   * When all sinks have posted an EOS message, an EOS message is forwarded to
   * the application.
   *
   * @return The new EOS event.
   */
  static Glib::RefPtr<Gst::EventEos> create();
};

/** A new segment event.  See create() for more details.
 */
class EventNewSegment : public Event
{
public:

  /**
   * gst_event_new_segment:
   * @segment: (transfer none): a #Gst::Segment
   *
   * Create a new SEGMENT event for @segment. The segment event can only travel
   * downstream synchronized with the buffer flow and contains timing information
   * and playback properties for the buffers that will follow.
   *
   * The newsegment event marks the range of buffers to be processed. All
   * data not within the segment range is not to be processed. This can be
   * used intelligently by plugins to apply more efficient methods of skipping
   * unneeded data. The valid range is expressed with the @start and @stop
   * values.
   *
   * The time value of the segment is used in conjunction with the start
   * value to convert the buffer timestamps into the stream time. This is
   * usually done in sinks to report the current stream_time.
   * @time represents the stream_time of a buffer carrying a timestamp of
   * @start. @time cannot be -1.
   *
   * @start cannot be -1, @stop can be -1. If there
   * is a valid @stop given, it must be greater or equal the @start, including
   * when the indicated playback @rate is < 0.
   *
   * The @applied_rate value provides information about any rate adjustment that
   * has already been made to the timestamps and content on the buffers of the
   * stream. (@rate * @applied_rate) should always equal the rate that has been
   * requested for playback. For example, if an element has an input segment
   * with intended playback @rate of 2.0 and applied_rate of 1.0, it can adjust
   * incoming timestamps and buffer content by half and output a newsegment event
   * with @rate of 1.0 and @applied_rate of 2.0
   *
   * After a newsegment event, the buffer stream time is calculated with:
   *
   *   time + (TIMESTAMP(buf) - start) * ABS (rate * applied_rate)
   *
   * Returns: (transfer full): the new SEGMENT event.
   */
  static Glib::RefPtr<Gst::EventNewSegment> create(const Gst::Segment& segment);

  /**
   * gst_event_parse_segment:
   * @event: The event to parse
   * @segment: (out) (transfer none): a pointer to a #GstSegment
   *
   * Parses a segment @event and stores the result in the given @segment location.
   * @segment remains valid only until the @event is freed. Don't modify the segment
   * and make a copy if you want to modify it or store it for later use.
   */
  void parse(const GstSegment** segment) const;
  _IGNORE(gst_event_parse_segment)
};

/** A tag event.
 */
class EventTag : public Event
{
public:

  /** Generates a metadata tag event from the given taglist.
   * @param taglist Metadata list.
   * @return A new Gst::Event.
   */
  static Glib::RefPtr<Gst::EventTag> create(const Gst::TagList& taglist);

  /** Parses a tag event and stores the results in the given taglist location.
   * @param name Tag name.
   * @return Metadata list.
   */
  Gst::TagList parse() const;
  _IGNORE(gst_event_parse_tag)
};

/** A buffer size event.  See create() for more details.
 */
class EventBufferSize : public Event
{
public:

  /** Create a new buffersize event. The event is sent downstream and notifies
   * elements that they should provide a buffer of the specified dimensions.
   *
   * When the async flag is set, a thread boundary is prefered.
   *
   * @param format Buffer format.
   * @param minsize Minimum buffer size.
   * @param maxsize Maximum buffer size.
   * @param async Thread behavior.
   * @return A new Gst::EventBufferSize.
   */
  static Glib::RefPtr<Gst::EventBufferSize>
    create(Format format, gint64 minsize, gint64 maxsize, bool async);

  /** Get the format, minsize, maxsize and async-flag in the buffersize event.
   *
   * @param format A reference to store the format in.
   * @param minsize A reference to store the minsize in.
   * @param maxsize A reference to store the maxsize in.
   * @param async A reference to store the async-flag in.
   */
  void parse(Format& format, gint64& minsize, gint64& maxsize, bool& async)
    const;
  _IGNORE(gst_event_parse_buffer_size)

  /** Get the minsize in the buffersize event.
   *
   * @return The mininum size.
   */
  gint64 parse() const;

  /** Get the format, in the buffersize event.
   *
   * @return The format.
   */
  Gst::Format parse_format() const;

  /** Get the maxsize in the buffersize event.
   *
   * @return The maximum size.
   */
  gint64 parse_max_size() const;

  /** Get the async-flag in the buffersize event.
   *
   * @return The async flag.
   */
  bool parse_async() const;
};

/** A QOS event.  See create() for more details.
 */
class EventQos : public Event
{
public:

 /**
  * gst_event_new_qos:
  * @type: the QoS type
  * @proportion: the proportion of the qos message
  * @diff: The time difference of the last Clock sync
  * @timestamp: The timestamp of the buffer
  *
  * Allocate a new qos event with the given values.
  * The QOS event is generated in an element that wants an upstream
  * element to either reduce or increase its rate because of
  * high/low CPU load or other resource usage such as network performance or
  * throttling. Typically sinks generate these events for each buffer
  * they receive.
  *
  * @type indicates the reason for the QoS event. #GST_QOS_TYPE_OVERFLOW is
  * used when a buffer arrived in time or when the sink cannot keep up with
  * the upstream datarate. #GST_QOS_TYPE_UNDERFLOW is when the sink is not
  * receiving buffers fast enough and thus has to drop late buffers.
  * #GST_QOS_TYPE_THROTTLE is used when the datarate is artificially limited
  * by the application, for example to reduce power consumption.
  *
  * @proportion indicates the real-time performance of the streaming in the
  * element that generated the QoS event (usually the sink). The value is
  * generally computed based on more long term statistics about the streams
  * timestamps compared to the clock.
  * A value < 1.0 indicates that the upstream element is producing data faster
  * than real-time. A value > 1.0 indicates that the upstream element is not
  * producing data fast enough. 1.0 is the ideal @proportion value. The
  * proportion value can safely be used to lower or increase the quality of
  * the element.
  *
  * @diff is the difference against the clock in running time of the last
  * buffer that caused the element to generate the QOS event. A negative value
  * means that the buffer with @timestamp arrived in time. A positive value
  * indicates how late the buffer with @timestamp was. When throttling is
  * enabled, @diff will be set to the requested throttling interval.
  *
  * @timestamp is the timestamp of the last buffer that cause the element
  * to generate the QOS event. It is expressed in running time and thus an ever
  * increasing value.
  *
  * The upstream element can use the @diff and @timestamp values to decide
  * whether to process more buffers. For possitive @diff, all buffers with
  * timestamp <= @timestamp + @diff will certainly arrive late in the sink
  * as well. A (negative) @diff value so that @timestamp + @diff would yield a
  * result smaller than 0 is not allowed.
  *
  * The application can use general event probes to intercept the QoS
  * event and implement custom application specific QoS handling.
  *
  * Returns: (transfer full): a new QOS event.
  */
  static Glib::RefPtr<Gst::EventQos> create(QOSType type, double proportion,
    ClockTimeDiff diff, ClockTime timestamp);

 /**
  * gst_event_parse_qos:
  * @event: The event to query
  * @type: (out): A pointer to store the QoS type in
  * @proportion: (out): A pointer to store the proportion in
  * @diff: (out): A pointer to store the diff in
  * @timestamp: (out): A pointer to store the timestamp in
  *
  * Get the type, proportion, diff and timestamp in the qos event. See
  * gst_event_new_qos() for more information about the different QoS values.
  */
  void parse(QOSType& type, double& proportion, ClockTimeDiff& diff, ClockTime& timestamp) const;
  _IGNORE(gst_event_parse_qos)

  /** Get the proportion in the qos event. See create() for more information
   * about the different QoS values.
   *
   * @return The proportion.
   */
  double parse() const;

  /** Get the diff in the qos event. See create() for more information about
   * the different QoS values.
   *
   * @return The difference.
   */
  ClockTimeDiff parse_diff() const;

  /** Get the timestamp in the qos event. See create() for more information
   * about the different QoS values.
   * @return The timestamp.
   */
  ClockTime parse_timestamp() const;
};

/** A seek event.  See create() for more details.
 */
class EventSeek : public Event
{
public:

  /** Allocate a new seek event with the given parameters.
   *
   * The seek event configures playback of the pipeline between start to stop
   * at the speed given in rate, also called a playback segment. The start and
   * stop values are expressed in format.
   *
   * A rate of 1.0 means normal playback rate, 2.0 means double speed.
   * Negatives values means backwards playback. A value of 0.0 for the rate is
   * not allowed and should be accomplished instead by PAUSING the pipeline.
   *
   * A pipeline has a default playback segment configured with a start position
   * of 0, a stop position of -1 and a rate of 1.0. The currently configured
   * playback segment can be queried with Gst::QUERY_SEGMENT.
   *
   * start_type and stop_type specify how to adjust the currently configured
   * start and stop fields in segment. Adjustments can be made relative or
   * absolute to the last configured values. A type of Gst::SEEK_TYPE_NONE
   * means that the position should not be updated.
   *
   * When the rate is positive and start has been updated, playback will start
   * from the newly configured start position.
   *
   * For negative rates, playback will start from the newly configured stop
   * position (if any). If the stop position if updated, it must be different
   * from -1 for negative rates.
   *
   * It is not possible to seek relative to the current playback position, to
   * do this, PAUSE the pipeline, query the current playback position with
   * Gst::QUERY_POSITION and update the playback segment current position with
   * a Gst::SEEK_TYPE_SET to the desired position.
   *
   * @param rate The new playback rate.
   * @param format The format of the seek values.
   * @param flags The optional seek flags.
   * @param start_type The type and flags for the new start position.
   * @param start The value of the new start position.
   * @param stop_type The type and flags for the new stop position.
   * @param stop The value of the new stop position.
   * @return A new seek event.
   */
  static Glib::RefPtr<Gst::EventSeek> create(double rate, Format format,
    SeekFlags flags, SeekType start_type, gint64 start, SeekType stop_type,
    gint64 stop);

  /** Parses a seek event and stores the results in the given result locations.
   *
   * @param rate Result location for the rate.
   * @param format Result location for the stream format.
   * @param flags Result location for the Gst::SeekFlags.
   * @param start_type Result location for the Gst::SeekType of the start
   * position.
   * @param start Result location for the start postion expressed in format.
   * @param stop_type Result location for the Gst::SeekType of the stop
   * position.
   * @param stop Result location for the stop postion expressed in format.
   */
  void parse(double& rate, Format& format, SeekFlags& flags,
    SeekType& start_type, gint64& start, SeekType& stop_type,
    gint64& stop) const;
  _IGNORE(gst_event_parse_seek)

  /** Parses the start of a seek event.
   * @return The start.
   */
  gint64 parse() const;

  /** Parses the rate of a seek event.
   * @return The rate.
   */
  double parse_rate() const;

  /** Parses the format of a seek event.
   * @return The format.
   */
  Format parse_format() const;

  /** Parses the flags of a seek event.
   * @return The flags.
   */
  SeekFlags parse_flags() const;

  /** Parses the start type of a seek event.
   * @return The start type.
   */
  SeekType parse_start_type() const;

  /** Parses the stop type of a seek event.
   * @return The stop type.
   */
  SeekType parse_stop_type() const;

  /** Parses the stop of a seek event.
   * @param The stop.
   */
  gint64 parse_stop() const;
};

/** A Navigation event.  See create() for more details.
 */
class EventNavigation : public Event
{
public:

  /** Create a new navigation event from the given description.
   *
   * @param structure Description of the event.
   * @return A new Gst::EventNavigation.
   */
  static Glib::RefPtr<Gst::EventNavigation> create(Gst::Structure& structure);
};

/** A latency event.  See create() for more details.
 */
class EventLatency : public Event
{
public:

  /** Create a new latency event. The event is sent upstream from the sinks and
   * notifies elements that they should add an additional latency to the
   * timestamps before synchronising against the clock.
   *
   * The latency is mostly used in live sinks and is always expressed in the
   * time format.
   *
   * @param latency The new latency value.
   * @return A new Gst::EventLatency.
   *
   * Since 0.10.12.
   */
  static Glib::RefPtr<Gst::EventLatency> create(ClockTime latency);

  /** Get the latency in the latency event.
   *
   * @return The latency.
   */
  ClockTime parse() const;
  _IGNORE(gst_event_parse_latency)
};

/** A step event.  See create() for more details.
 */
class EventStep : public Event
{
public:

  /** Create a new step event. The purpose of the step event is to instruct a
   * sink to skip @a amount (expressed in @a format) of media. It can be used
   * to implement stepping through the video frame by frame or for doing fast
   * trick modes.
   *
   * A rate of <= 0.0 is not allowed, pause the pipeline or reverse the
   * playback direction of the pipeline to get the same effect.
   *
   * The @a flush flag will clear any pending data in the pipeline before
   * starting the step operation.
   *
   * The @a intermediate flag instructs the pipeline that this step operation
   * is part of a larger step operation.
   *
   * @param format The format of amount.
   * @param amount The amount of data to step.
   * @param rate The step rate.
   * @param flush Flushing steps.
   * @param intermediate Intermediate steps.
   * @return A new Gst::Event.
   *
   * Since 0.10.24.
   */
  static Glib::RefPtr<Gst::EventStep> create(Gst::Format format,
    guint64 amount, double rate, bool flush, bool intermediate);

  /** Parse the step event.
   *
   * @param format A reference to store the format in.
   * @param amount A reference to store the amount in.
   * @param rate A reference to store the rate in.
   * @param flush A reference to store the flush boolean in.
   * @param intermediate A reference to store the intermediate boolean in.
   */
  void parse(Gst::Format& format, guint64& amount, double& rate,
    bool& flush, bool& intermediate) const;
  _IGNORE(gst_event_parse_step)

  /** Get the amount in a step event.
   * @return The amount.
   */
  guint64 parse() const;

  /** Get the format in a step event.
   * @return The format.
   */
  Gst::Format parse_format() const;

  /** Get the rate in a step event.
   * @return The rate.
   */
  double parse_rate() const;

  /** Get the flush in a step event.
   * @return The flush flag.
   */
  bool parse_flush() const;

  /** Get the intermediate flag in a step event.
   * @return The intermediate flag.
   */
  bool parse_intermediate() const;
};

/** A sink-message event.  See create() for more details.
 */
class EventSinkMessage : public Event
{
public:

  /**
   * Create a new sink-message event. The purpose of the sink-message event is
   * to instruct a sink to post the message contained in the event synchronized
   * with the stream.
   *
   * @name is used to store multiple sticky events on one pad.
   * @ param name A name for the event.
   * @param messsage The #Gst::Message to be posted.
   * @return a new #Gst::Event.
   */
  static Glib::RefPtr<Gst::EventSinkMessage>
    create(const Glib::ustring& name, const Glib::RefPtr<Gst::Message>& message);

  /** Parse the sink-message event.
   * @return the sink-messge's event message.
   */
  Glib::RefPtr<Gst::Message> parse();
  _IGNORE(gst_event_parse_sink_message)

  /** Parse the sink-message event.
   * @return the sink-messge's event message.
   */
  Glib::RefPtr<const Gst::Message> parse() const;
};

/** A stream-start event.  See create() for more details.
 */
class EventStreamStart : public Event
{
public:

  /**
   * Create a new STREAM_START event. The stream start event can only
   * travel downstream synchronized with the buffer flow. It is expected
   * to be the first event that is sent for a new stream.
   *
   * Source elements, demuxers and other elements that create new streams
   * are supposed to send this event as the first event of a new stream. It
   * should not be send after a flushing seek or in similar situations
   * and is used to mark the beginning of a new logical stream. Elements
   * combining multiple streams must ensure that this event is only forwarded
   * downstream once and not for every single input stream.
   *
   * The @stream_id should be a unique string that consists of the upstream
   * stream-id, / as separator and a unique stream-id for this specific
   * stream. A new stream-id should only be created for a stream if the upstream
   * stream is split into (potentially) multiple new streams, e.g. in a demuxer,
   * but not for every single element in the pipeline.
   * gst_pad_create_stream_id() or gst_pad_create_stream_id_printf() can be
   * used to create a stream-id.
   *
   * @param stream_id Identifier for this stream.
   * @return a new #Gst::Event.
   */
  static Glib::RefPtr<Gst::EventStreamStart>
    create(const Glib::ustring& stream_id);
};

class EventCaps : public Event
{
public:

  /**
   * Create a new CAPS event for @caps. The caps event can only travel downstream
   * synchronized with the buffer flow and contains the format of the buffers
   * that will follow after the event.
   *
   * @param caps event caps
   * @return the new CAPS event.
  */
  static Glib::RefPtr<Gst::EventCaps>
    create(const Glib::RefPtr<Gst::Caps>& caps);

  Glib::RefPtr<Gst::Caps> parse();
};

} //namespace Gst
