/* 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/gstelement.h>
#include <gstreamermm/object.h>
#include <gstreamermm/clock.h>
#include <gstreamermm/enums.h>
#include <gstreamermm/error.h>
#include <gstreamermm/event.h>
#include <gstreamermm/message.h>
#include <gstreamermm/pad.h>
#include <gstreamermm/query.h>
#include <glibmm/arrayhandle.h>

_DEFS(gstreamermm,gst)

namespace Gst
{

/** @defgroup GstBaseClasses gstreamermm Base Classes
 *  Wrapped GStreamer base classes (plug-ins derrive from these).
 */

/** @defgroup GstPlugins gstreamermm Plugins
 *  Wrapped GStreamer plugins.
 */

 /** @defgroup GstInterfaces gstreamermm Interfaces
 *  Wrapped GStreamer interfaces and classes related to them.
 */

class Bus;
class Caps;
class Clock;
class ElementFactory;
class Event;
class Index;
class Interface;
class Message;
class Pad;
class PadTemplate;
class Query;
class TagList;

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

_WRAP_ENUM(ElementFlags, GstElementFlags)

//TODO: Fix gmmproc so that definition in gst_enums.defs does not cause errors
//and this can be wrapped with _WRAP_ENUM.
enum StateChange
{
  STATE_CHANGE_NULL_TO_READY = GST_STATE_CHANGE_NULL_TO_READY,
  STATE_CHANGE_READY_TO_PAUSED = GST_STATE_CHANGE_READY_TO_PAUSED,
  STATE_CHANGE_PAUSED_TO_PLAYING = GST_STATE_CHANGE_PAUSED_TO_PLAYING,
  STATE_CHANGE_PLAYING_TO_PAUSED = GST_STATE_CHANGE_PLAYING_TO_PAUSED,
  STATE_CHANGE_PAUSED_TO_READY = GST_STATE_CHANGE_PAUSED_TO_READY,
  STATE_CHANGE_READY_TO_NULL = GST_STATE_CHANGE_READY_TO_NULL 
};

_WRAP_ENUM(StateChangeReturn, GstStateChangeReturn)

namespace Enums
{

/**  Gets a string representing the given state.
 *
 * @param state A Gst::State to get the name of.
 * @return A Glib::ustring with the name of the state.
 */
Glib::ustring get_name(State state);

/** Gets a string representing the given state change result.
 *
 * @param state_ret A Gst::StateChangeReturn to get the name of.
 * @return A Glib::ustring with the name of the state change result.
 */
Glib::ustring get_name(StateChangeReturn state_ret);

} //namespace Enums

/** The abstract base class for all pipeline elements.
 * Gst::Element is the abstract base class needed to construct an element that
 * can be used in a GStreamer pipeline. Please refer to the plugin writers
 * guide for more information on creating Gst::Element subclasses.
 *
 * The name of a Gst::Element can be get with get_name() and set with
 * set_name().
 *
 * All elements have pads (of the type Gst::Pad). These pads link to pads on
 * other elements. Gst::Buffer flow between these linked pads. An Element has a
 * GList of Gst::Pad structures for all their input (or sink) and output (or
 * source) pads.  Core and plug-in writers can add and remove pads with
 * add_pad() and remove_pad().
 *
 * A pad of an element can be retrieved by name with get_request_pad() or
 * get_static_pad(). An iterator of all pads can be retrieved with
 * iterate_pads().
 *
 * Gst::Elements can be linked through their pads. Use the link() function to
 * link elements. Use link_filtered() to link two elements constrained by a
 * specified set of Gst::Caps. For finer control, use link_pads() and
 * link_pads_filtered() to specify the pads to link on each element by name.
 *
 * Each element has a state (see State). You can get and set the state of an
 * element with get_state() and set_state(). To get a string representation of
 * a State, use Gst::Enums::get_name().
 *
 * You can get and set a Clock on an element using get_clock() and set_clock().
 * Some elements can provide a clock for the pipeline if provides_clock()
 * returns true. With the provide_clock() method one can retrieve the clock
 * provided by such an element. Not all elements require a clock to operate
 * correctly. If requires_clock() returns true, a clock should be set on the
 * element with set_clock().
 *
 * Note that clock slection and distribution is normally handled by the
 * toplevel Gst::Pipeline so the clock functions are only to be used in very
 * specific situations.
 *
 * Last reviewed on 2006-03-12 (0.10.5).
 * @ingroup GstBaseClasses
 */
class Element : public Gst::Object
{
  _CLASS_GOBJECT(Element, GstElement, GST_ELEMENT, Gst::Object, GstObject)

protected:
  //Needed for ElementInterfaced derived class
  _CTOR_DEFAULT

public:
  /** Links this source element to the @dest element.
   * The link must be from source to destination - the other direction will not
   * be tried.  The function looks for existing pads that aren't linked yet. It
   * will request new pads if necessary. Such pads need to be released manually
   * when unlinking.  If multiple links are possible, only one is established.
   *
   * Make sure you have added your elements to a bin or pipeline with
   * Gst::Bin::add() before trying to link them.
   *
   * @param dest the Gst::Element containing the destination pad.
   * @return the destination element for further linking if desired.
   * @throws std::runtime_error If the elements could not be linked.
   */
  Glib::RefPtr<Gst::Element> link(const Glib::RefPtr<Gst::Element>& dest);
  _IGNORE(gst_element_link, gst_element_link_many)

  _WRAP_METHOD(bool add_pad(const Glib::RefPtr<Gst::Pad>& pad), gst_element_add_pad)
  _WRAP_METHOD(void create_all_pads(), gst_element_create_all_pads)

  _WRAP_METHOD(Glib::RefPtr<Gst::Pad> create_compatible_pad(const Glib::RefPtr<const Gst::Pad>& pad, const Glib::RefPtr<const Gst::Caps>& caps), gst_element_get_compatible_pad)
  _WRAP_METHOD(Glib::RefPtr<Gst::PadTemplate> get_compatible_pad_template(const Glib::RefPtr<const Gst::PadTemplate>& compattempl), gst_element_get_compatible_pad_template, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Gst::PadTemplate> get_compatible_pad_template(const Glib::RefPtr<const Gst::PadTemplate>& compattempl) const, gst_element_get_compatible_pad_template, refreturn, constversion)

  //TODO: The documentation says "The pad should be released with gst_element_release_request_pad().", which is odd. murrayc
  _WRAP_METHOD(Glib::RefPtr<Gst::Pad> get_request_pad(const Glib::ustring& name), gst_element_get_request_pad)

  //TODO: The documentation says "The pad should be released with gst_element_release_request_pad().", which is odd. murrayc
  _WRAP_METHOD(Glib::RefPtr<Gst::Pad> request_pad(const Glib::RefPtr<Gst::PadTemplate>& templ, const Glib::ustring& name{?}, const Glib::RefPtr<const Gst::Caps>& caps{?}), gst_element_request_pad)

  _WRAP_METHOD(Glib::RefPtr<Gst::Pad> get_static_pad(const Glib::ustring& name), gst_element_get_static_pad)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Pad> get_static_pad(const Glib::ustring& name) const, gst_element_get_static_pad, constversion)
  _IGNORE(gst_element_get_pad)

  _WRAP_METHOD(void no_more_pads(), gst_element_no_more_pads)

  /** Makes the element free the previously requested pad as obtained with
   * get_request_pad().
   * MT safe.
   * @param pad The Gst::Pad to release.
   */
  _WRAP_METHOD(void release_request_pad(const Glib::RefPtr<Gst::Pad>& pad), gst_element_release_request_pad)

  _WRAP_METHOD(bool remove_pad(const Glib::RefPtr<Gst::Pad>& pad), gst_element_remove_pad)
  _WRAP_METHOD(Gst::Iterator<Gst::Pad> iterate_pads(), gst_element_iterate_pads)
  _WRAP_METHOD(Gst::Iterator<const Gst::Pad> iterate_pads() const, gst_element_iterate_pads)
  _WRAP_METHOD(Gst::Iterator<Gst::Pad> iterate_sink_pads(), gst_element_iterate_sink_pads)
  _WRAP_METHOD(Gst::Iterator<const Gst::Pad> iterate_sink_pads() const, gst_element_iterate_sink_pads)
  _WRAP_METHOD(Gst::Iterator<Gst::Pad> iterate_src_pads(), gst_element_iterate_src_pads)
  _WRAP_METHOD(Gst::Iterator<const Gst::Pad> iterate_src_pads() const, gst_element_iterate_src_pads)
  _WRAP_METHOD(void unlink(const Glib::RefPtr<Gst::Element>& dest), gst_element_unlink)
  _IGNORE(gst_element_unlink_many)

  //This is not like link() because link() was written to return the
  //destination element as a convenience for further linking to compensate for
  //C API's convenience function gst_element_link_many().
  _WRAP_METHOD(bool link_pads(const Glib::ustring& srcpadname, const Glib::RefPtr<Gst::Element>& dest, const Glib::ustring& destpadname), gst_element_link_pads)

  _WRAP_METHOD(void unlink_pads(const Glib::ustring& srcpadname, const Glib::RefPtr<Gst::Element>& dest, const Glib::ustring& destpadname), gst_element_unlink_pads)

  //This is not like link() because link() was written to return the
  //destination element as a convenience for further linking to compensate for
  //C API's convenience function gst_element_link_many().
  _WRAP_METHOD(bool link_pads(const Glib::ustring& srcpadname, const Glib::RefPtr<Gst::Element>& dest, const Glib::ustring& destpadname, const Glib::RefPtr<Gst::Caps>& filter), gst_element_link_pads_filtered)

  //This is not like link() because link() was written to return the
  //destination element as a convenience for further linking to compensate for
  //C API's convenience function gst_element_link_many().
  _WRAP_METHOD(bool link_pads(const Glib::ustring& srcpadname, const Glib::RefPtr<Gst::Element>& dest, const Glib::ustring& destpadname, PadLinkCheck flags), gst_element_link_pads_full)

  /** Links @a src to @a dest using the given caps as filtercaps. The link must
   * be from source to destination; the other direction will not be tried. The
   * function looks for existing pads that aren't linked yet. It will request
   * new pads if necessary. If multiple links are possible, only one is
   * established.
   *
   * Make sure you have added your elements to a bin or pipeline with
   * Gst::Bin::add() before trying to link them.
   *
   * @param dest The Gst::Element containing the destination pad.
   * @param filter The Gst::Caps to filter the link.
   * @return the destination element for further linking if desired.
   * @throws std::runtime_error If the elements could not be linked.
   */
  Glib::RefPtr<Gst::Element> link(const Glib::RefPtr<Gst::Element>& dest, const Glib::RefPtr<Gst::Caps>& filter);
  _IGNORE(gst_element_link_filtered)

  _WRAP_METHOD(void set_base_time(ClockTime time), gst_element_set_base_time)
  _WRAP_METHOD(ClockTime get_base_time() const, gst_element_get_base_time)
  _WRAP_METHOD(void set_start_time(Gst::ClockTime time), gst_element_set_start_time)
  _WRAP_METHOD(Gst::ClockTime get_start_time() const, gst_element_get_start_time)
  _WRAP_METHOD(void set_bus(const Glib::RefPtr<Gst::Bus>& bus), gst_element_set_bus)
  _WRAP_METHOD(Glib::RefPtr<Gst::Bus> get_bus(), gst_element_get_bus)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Bus> get_bus() const, gst_element_get_bus, constversion)
  _WRAP_METHOD(void set_index(const Glib::RefPtr<Gst::Index>& index), gst_element_set_index)

  //Note: gst_element_get_index provides a reference.
  _WRAP_METHOD(Glib::RefPtr<Gst::Index> get_index(), gst_element_get_index)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Index > get_index() const, gst_element_get_index, constversion)

  _WRAP_METHOD(Glib::RefPtr<Gst::ElementFactory> get_factory(), gst_element_get_factory, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Gst::ElementFactory> get_factory() const, gst_element_get_factory, refreturn, constversion)

  _WRAP_METHOD(bool is_indexable() const, gst_element_is_indexable)

  // We don't wrap the gst_element_get_name(), gst_element_set_name(), 
  // gst_element_get_parent(), and gst_element_set_parent() macros because they 
  // just calls the base (GstObject) class's functions anyway.

  _WRAP_METHOD(bool requires_clock() const, gst_element_requires_clock)
  _WRAP_METHOD(bool set_clock(const Glib::RefPtr<Gst::Clock>& clock), gst_element_set_clock)
  _WRAP_METHOD(Glib::RefPtr<Gst::Clock> get_clock(), gst_element_get_clock)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Clock> get_clock() const, gst_element_get_clock, constversion)
  _WRAP_METHOD(bool provides_clock() const, gst_element_provides_clock)
  _WRAP_METHOD(Glib::RefPtr<Gst::Clock> provide_clock(), gst_element_provide_clock)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Clock> provide_clock() const, gst_element_provide_clock, constversion)
  _WRAP_METHOD(StateChangeReturn set_state(State state), gst_element_set_state)
  _WRAP_METHOD(StateChangeReturn get_state(State& state, State& pending, ClockTime timeout) const, gst_element_get_state)
  _WRAP_METHOD(bool set_locked_state(gboolean locked_state), gst_element_set_locked_state)
  _WRAP_METHOD(bool is_locked_state() const, gst_element_is_locked_state)
  _WRAP_METHOD(void abort_state(), gst_element_abort_state)
  _WRAP_METHOD(StateChangeReturn continue_state(StateChangeReturn ret), gst_element_continue_state)
  _WRAP_METHOD(void lost_state(), gst_element_lost_state)
  _WRAP_METHOD(void lost_state(bool new_base_time), gst_element_lost_state_full)
  _WRAP_METHOD(bool sync_state_with_parent(), gst_element_sync_state_with_parent)
  _WRAP_METHOD(StateChangeReturn change_state(StateChange transition), gst_element_change_state)
  _WRAP_METHOD(void found_tags(Gst::TagList& list), gst_element_found_tags)
  _WRAP_METHOD(void found_tags_for_pad(const Glib::RefPtr<Gst::Pad>& pad, Gst::TagList& list), gst_element_found_tags_for_pad)

  /** Post an error, warning or info message on the bus from inside an element.
   *
   * type must be of Gst::MESSAGE_ERROR, Gst::MESSAGE_WARNING or
   * Gst::MESSAGE_INFO.
   *
   * MT safe.
   *
   * @param code The GError code belonging to the domain.
   * @param line The source code line where the error was generated.
   * @param type The GstMessageType.
   * @param domain The GStreamer GError domain this message belongs to.
   * @param text A text string to be used as a replacement for the default
   * message connected to code.
   * @param debug A debug message to be used as a replacement for the default
   * debugging information.
   * @param file The source code file where the error was generated.
   * @param function The source code function where the error was generated.
   */
  void post_message(int code, int line, MessageType type = Gst::MESSAGE_INFO,
    const Glib::QueryQuark& domain = get_core_error_quark(),
    const Glib::ustring& text = Glib::ustring(),
    const Glib::ustring& debug = Glib::ustring(),
    const Glib::ustring& file = Glib::ustring(),
    const Glib::ustring& function = Glib::ustring());
  _IGNORE(gst_element_message_full)

  _WRAP_METHOD(bool post_message(const Glib::RefPtr<Gst::Message>& message), gst_element_post_message)

#m4 _CONVERSION(`const GstQueryType*',`Glib::ArrayHandle<QueryType>',`Glib::ArrayHandle<QueryType>((QueryType*)($3), Glib::OWNERSHIP_NONE)')
  _WRAP_METHOD(Glib::ArrayHandle<QueryType> get_query_types() const, gst_element_get_query_types)

  _WRAP_METHOD(bool query(const Glib::RefPtr<Gst::Query>& query) const, gst_element_query)
  _WRAP_METHOD(bool query_convert(Format src_format, gint64 src_val, Format& dest_format, gint64& dest_val) const, gst_element_query_convert)
  _WRAP_METHOD(bool query_position(Format& format, gint64& cur) const, gst_element_query_position)

  /** Queries an element for the stream position.
   *
   * @param format A pointer to the Gst::Format asked for. On return will
   * contain the Gst::Format used.
   * @return true if the query could be performed.
   */
  bool query_position(Format& format) const;

  _WRAP_METHOD(bool query_duration(Format& format, gint64& duration) const, gst_element_query_duration)

  /** Queries an element for the total stream duration.
   *
   * @param format A pointer to the Gst::Format asked for. On return contains
   * the Gst::Format used.
   * @return true if the query could be performed.
   */
  bool query_duration(Format& format) const;

  _WRAP_METHOD(bool seek(Format format, SeekFlags seek_flags, gint64 seek_pos), gst_element_seek_simple)
  _WRAP_METHOD(bool seek(double rate, Format format, SeekFlags flags, SeekType cur_type, gint64 cur, SeekType stop_type, gint64 stop), gst_element_seek)

  /** Sends an event to an element. If the element doesn't implement an event
   * handler, the event will be pushed on a random linked sink pad for upstream
   * events or a random linked source pad for downstream events.
   *
   * This function takes owership of the provided event.
   *
   * @param event The Gst::Event to send to the element.
   * @return true if the event was handled. MT safe. 
   */
  bool send_event(const Glib::RefPtr<Gst::Event>& event);
  _IGNORE(gst_element_send_event)

  _WRAP_METHOD(bool implements_interface(GType iface_type) const, gst_element_implements_interface)

  /** This signals that the element will not generate more dynamic pads.
   */
  _WRAP_SIGNAL(void no_more_pads(), "no-more-pads")

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

  /** Signals that a new Gst::Pad has been added to the element.
   */
  _WRAP_SIGNAL(void pad_added(const Glib::RefPtr<Gst::Pad>& new_pad), "pad-added")

  /** Signals that a Gst::Pad has been removed from the element.
   */
  _WRAP_SIGNAL(void pad_removed(const Glib::RefPtr<Gst::Pad>& old_pad), "pad-removed")

#m4 _CONVERSION(`GstPadTemplate*', `const Glib::RefPtr<const Gst::PadTemplate>&', `Glib::wrap(($3), true)')
  /** Called when a new pad is requested.
   */
  _WRAP_VFUNC(Glib::RefPtr<Gst::Pad> request_new_pad(const Glib::RefPtr<const Gst::PadTemplate>& templ, const Glib::ustring& name), "request_new_pad")

  /** Called when a request pad is to be released.
   */
  _WRAP_VFUNC(void release_pad(const Glib::RefPtr<Gst::Pad>& pad), "release_pad")

  /** Get the state of the element.
   */
  _WRAP_VFUNC(StateChangeReturn get_state(State& state, State& pending, ClockTime timeout) const, "get_state")

  /** Set a new state on the element.
   */
  _WRAP_VFUNC(StateChangeReturn set_state(State state), "set_state")

  /** Called by set_state to perform an incremental state change.
   */
  _WRAP_VFUNC(StateChangeReturn change_state(StateChange transition), "change_state")

#m4 _CONVERSION(`GstBus*', `const Glib::RefPtr<Gst::Bus>&', `Glib::wrap($3, true)')
  /** Set a Gst::Bus on the element.
   */
  _WRAP_VFUNC(void set_bus(const Glib::RefPtr<Gst::Bus>& bus), "set_bus")

  /** Gets the Gst::Clock provided by the element.
   */
  _WRAP_VFUNC(Glib::RefPtr<Gst::Clock> provide_clock(), "provide_clock")

  // This vfunc is hand-coded because it is necessary for the callback to
  // return true instead of false (as would happen with the default gmmproc
  // callback) because GstElement does not set its set_clock() vfunc
  // by default and yet gst_element_set_clock() returns true when GstElement's
  // set_clock() vfunc is not set (it's a strange thing, but look at
  // gst_element_class_init() and gst_element_set_clock() for insight).
  /** Set the Gst::Clock on the element.
   */
  virtual bool set_clock_vfunc(const Glib::RefPtr<Gst::Clock>& clock);

  // This vfunc is handwritten because the default gmmproc code produces a
  // compile error related to tyring to use Glib::ArrayHandle<>'s default
  // constructor when returning a default value at the end of both the callback
  // and the vfunc.
  /** Get the supported Gst::QueryType of this element.
   */
   virtual Glib::ArrayHandle<QueryType> get_query_types_vfunc() const;

  /** Get a Gst::Index on the element.
   */
  _WRAP_VFUNC(Glib::RefPtr<Gst::Index> get_index() const, "get_index")

#m4 _CONVERSION(`GstIndex*',`const Glib::RefPtr<Gst::Index>&',`Glib::wrap($3, true)')
  /** Set the Gst::Index of an element.
   */
  _WRAP_VFUNC(void set_index(const Glib::RefPtr<Gst::Index>& index), "set_index")

#m4 _CONVERSION(`GstEvent*',`const Glib::RefPtr<Gst::Event>&', `Gst::wrap($3, true)')
  /** Send a Gst::Event to the element.
   */
  _WRAP_VFUNC(bool send_event(const Glib::RefPtr<Gst::Event>& event), "send_event")

#m4 _CONVERSION(`GstQuery*',`const Glib::RefPtr<Gst::Query>&', `Gst::wrap($3, true)')
  /** Perform a Gst::Query on the element.
   */
  _WRAP_VFUNC(bool query(const Glib::RefPtr<Gst::Query>& query) const, "query")

protected:
#m4begin
  _PUSH(SECTION_PCC_CLASS_INIT_VFUNCS)
  klass->set_clock = &set_clock_vfunc_callback;
  klass->get_query_types = &get_query_types_vfunc_callback;
  _SECTION(SECTION_PH_VFUNCS)
  static gboolean set_clock_vfunc_callback(GstElement* self, GstClock* clock);
  static const GstQueryType* get_query_types_vfunc_callback(GstElement* self);
  _POP()
#m4end
};

/** A templated class used for casting Gst::Element to interfaces that its
 * underlying gobject implements.
 * Gst::ElementInterfaced is a templated class which, in conjunction with
 * Gst::Interface::cast(), is used to cast a Gst::Element obtained by
 * Gst::ElementFactory::create_element() into underlying GStreamer interfaces
 * that the element may implement.  A 'filesrc' element, for example,
 * implements the Gst::URIHandler interface (which is not reflected in a
 * Gst::Element obtained from a Gst::ElementFactory).  To use the
 * Gst::URIHandler methods with a 'filesrc' element obtained from a
 * Gst::ElementFactory, one would do the following:
 * @code
 * ...
 * Glib::RefPtr<Gst::Element> element =
 * Gst::ElementFactory::create_element("filesrc", "source");
 *
 * Glib::RefPtr< Gst::ElementInterfaced<Gst::URIHandler> > handler =
 *   Gst::Interface::cast<Gst::URIHandler>(element);
 *
 * if(handler)
 * {
 *   std::cout << "element '" << element->get_name() <<
 *     "' implements URIHandler interface." << std::endl;
 *
 *   // Use uri handler interface methods:
 *
 *   handler->set_uri("file:///tmp/media.file");
 *
 *   std::cout << handler->get_name() << " uri = '" << handler->get_uri() <<
 *     "'." << std::endl;
 * }
 * @endcode
 * @ingroup GstInterfaces
 */
template <class T_Interface>
class ElementInterfaced
: public Element,
  public T_Interface
{

#ifndef DOXYGEN_SHOULD_SKIP_THIS
private:
  friend class Gst::Interface;

private:
  // noncopyable
  ElementInterfaced(const ElementInterfaced&);
  ElementInterfaced& operator=(const ElementInterfaced&);

protected:
  explicit ElementInterfaced(GstElement* castitem);
#endif /* DOXYGEN_SHOULD_SKIP_THIS */

public:
  virtual ~ElementInterfaced();

  // The gobj() methods make calls involving access to the underlying gobject
  // unambiguous (specifically, gobj() is ambiguous when called from an
  // ElementInterfaced<..> class).

  /// Gets the underlying gobject.
  GstElement* gobj() { return Element::gobj(); };

  /// Gets the underlying gobject.
  const GstElement* gobj() const { return Element::gobj(); };

  /// Gets a copy of the underlying gobject.  The copy should be freed.
  GstElement* gobj_copy() { return Element::gobj_copy(); };
};

#ifndef DOXYGEN_SHOULD_SKIP_THIS

extern "C"
void ElementInterfaced_WeakNotify_gstreamermm_callback(void* data, GObject* where_the_object_was);

template <class T_Interface>
ElementInterfaced<T_Interface>::ElementInterfaced(GstElement* castitem)
{
  gobject_ = (GObject*)castitem;
  if(gobject_) {
    gst_object_ref(gobj());
    g_object_weak_ref(gobject_,
      &ElementInterfaced_WeakNotify_gstreamermm_callback, this);
  }
}

template <class T_Interface>
ElementInterfaced<T_Interface>::~ElementInterfaced()
{
  // Set the gobject_ to null so that when this is deleted, the gobject doesn't
  // go with it and other elements "wrapping" the gobject are not affected
  gobject_ = 0;
}

#endif /* DOXYGEN_SHOULD_SKIP_THIS */

} // namespace Gst
