/* 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 <gstreamermm/element.h>
#include <gstreamermm/childproxy.h>
#include <gstreamermm/pad.h>

_DEFS(gstreamermm,gst)

namespace Gst
{

class Pad;
class GhostPad;

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

/** A base class and element that can contain other elements.
 * Gst::Bin is an element that can contain other Gst::Element, allowing them to
 * be managed as a group. Pads from the child elements can be ghosted to the
 * bin, see Gst::GhostPad. This makes the bin look like any other elements and
 * enables creation of higher-level abstraction elements.
 *
 * A new Gst::Bin is created with create(). Use a Gst::Pipeline instead if you
 * want to create a toplevel bin because a normal bin doesn't have a bus or
 * handle clock distribution of its own.
 *
 * After the bin has been created you will typically add elements to it with
 * add(). You can remove elements with remove().
 *
 * An element can be retrieved from a bin with get_element(), using the
 * elements name. get_element_recurse_up() is mainly used for internal purposes
 * and will query the parent bins when the element is not found in the current
 * bin.
 *
 * An iterator of elements in a bin can be retrieved with iterate_elements().
 * Various other iterators exist to retrieve the elements in a bin.
 *
 * The element-added signal is fired whenever a new element is added to the
 * bin. Likewise the element-removed signal is fired whenever an element is
 * removed from the bin.
 *
 * Notes:
 *
 * A Gst::Bin internally intercepts every Gst::Message posted by its children
 * and implements the following default behaviour for each of them:
 *
 * - Gst::MESSAGE_EOS - This message is only posted by sinks in the PLAYING
 * state. If all sinks posted the EOS message, this bin will post and EOS
 * message upwards.
 *
 * - Gst::MESSAGE_SEGMENT_START - just collected and never forwarded upwards.
 * The messages are used to decide when all elements have completed playback of
 * their segment.
 *
 * - Gst::MESSAGE_SEGMENT_DONE - Is posted by Gst::Bin when all elements that
 * posted a Gst::MESSAGE_SEGMENT_START have posted a Gst::MESSAGE_SEGMENT_DONE.
 *
 * - Gst::MESSAGE_DURATION - Is posted by an element that detected a change in
 * the stream duration. The default bin behaviour is to clear any cached
 * duration values so that the next duration query will perform a full duration
 * recalculation. The duration change is posted to the application so that it
 * can refetch the new duration with a duration query.
 *
 * - Gst::MESSAGE_CLOCK_LOST - This message is posted by an element when it can
 * no longer provide a clock. The default bin behaviour is to check if the lost
 * clock was the one provided by the bin. If so and the bin is currently in the
 * PLAYING state, the message is forwarded to the bin parent. This message is
 * also generated when a clock provider is removed from the bin. If this
 * message is received by the application, it should PAUSE the pipeline and set
 * it back to PLAYING to force a new clock distribution.
 *
 * - Gst::MESSAGE_CLOCK_PROVIDE - This message is generated when an element can
 * provide a clock. This mostly happens when a new clock provider is added to
 * the bin. The default behaviour of the bin is to mark the currently selected
 * clock as dirty, which will perform a clock recalculation the next time the
 * bin is asked to provide a clock. This message is never sent tot the
 * application but is forwarded to the parent of the bin.
 *
 * - OTHERS - posted upwards.
 *
 * A Gst::Bin implements the following default behaviour for answering to a
 * Gst::Query:
 *
 * - Gst::QUERY_DURATION - If the query has been asked before with the same
 * format and the bin is a toplevel bin (ie. has no parent), use the cached
 * previous value. If no previous value was cached, the query is sent to all
 * sink elements in the bin and the MAXIMUM of all values is returned. If the
 * bin is a toplevel bin the value is cached. If no sinks are available in the
 * bin, the query fails.
 *
 * - Gst::QUERY_POSITION - The query is sent to all sink elements in the bin
 * and the MAXIMUM of all values is returned. If no sinks are available in the
 * bin, the query fails.
 *
 * - OTHERS - the query is forwarded to all sink elements, the result of the
 * first sink that answers the query successfully is returned. If no sink is in
 * the bin, the query fails.
 *
 * A Gst::Bin will by default forward any event sent to it to all sink
 * elements. If all the sinks return true, the bin will also return true, else
 * false is returned. If no sinks are in the bin, the event handler will return
 * true.
 *
 * Last reviewed on 2006-04-28 (0.10.6)
 * @ingroup GstBaseClasses
 */
class Bin
: public Element,
  public ChildProxy
{
  _CLASS_GOBJECT(Bin, GstBin, GST_BIN, Element, GstElement)
  _IMPLEMENTS_INTERFACE(ChildProxy)

protected:
  _WRAP_CTOR(Bin(const Glib::ustring& name), gst_bin_new)
  Bin();

public:
  /** Creates a new bin with the given name.
   *
   * @param name The name of the new bin.
   * @return A new Gst::Bin.
   */
  _WRAP_CREATE(const Glib::ustring& name)

  /** Creates a new bin with a unique generic name.
   *
   * @return A new Gst::Bin.
   */
   _WRAP_CREATE()

  /** Adds the given element to the bin.
   * This sets the element's parent, and thus takes ownership of the element.
   * An element can only be added to one bin.
   *
   * If the element's pads are linked to other pads then the pads will be
   * unlinked before the element is added to the bin.
   *
   * MT safe.
   *
   * @param element The Gst::Element to add.
   * @return This Gst::Bin if successful, for chained calls to add().
   *
   * @throws std::runtime_error if the Bin does not want to accept the Element.
   */
  Glib::RefPtr<Gst::Bin> add(const Glib::RefPtr<Gst::Element>& element);
  _IGNORE(gst_bin_add, gst_bin_add_many)

  /** Removes the element from the bin, unparenting it as well. Unparenting the
   * element means that the element will be dereferenced, so if the bin holds
   * the only reference to the element, the element will be freed in the
   * process of removing it from the bin.
   *
   * If the element's pads are linked to other pads, the pads will be unlinked
   * before the element is removed from the bin.
   *
   * MT safe.
   *
   * element the Gst::Element to remove
   * Returns this Gst::Bin if successful, for chained calls to remove().
   *
   * @throws std::runtime_error if the Bin does not want to remove the Element.
   */
  Glib::RefPtr<Gst::Bin> remove(const Glib::RefPtr<Gst::Element>& element);
  _IGNORE(gst_bin_remove, gst_bin_remove_many)

  //We could add a bool recurse_up = false parameter, 
  //but get_element() and get_element_recurse_up() seem different enough to be separate.
  //One recurses down and the other recurses up, so it's not just additional behaviour. 
  _WRAP_METHOD(Glib::RefPtr<Gst::Element> get_element(const Glib::ustring& name), gst_bin_get_by_name)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Element> get_element(const Glib::ustring& name) const, gst_bin_get_by_name, constversion)

  _WRAP_METHOD(Glib::RefPtr<Gst::Element> get_element_recurse_up(const Glib::ustring& name), gst_bin_get_by_name_recurse_up)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Element> get_element_recurse_up(const Glib::ustring& name) const, gst_bin_get_by_name_recurse_up, constversion)
 
  _WRAP_METHOD(Glib::RefPtr<Gst::Element> get_element(GType interface), gst_bin_get_by_interface)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Element> get_element(GType interface) const, gst_bin_get_by_interface, constversion)

  /** Adds a ghost pad to the bin using an element's static pad as a target.
   * @param element The element with the target static pad.
   * @param pad_name The static pad to use as the target.
   * @param name An optional name for the ghost pad.
   * @return The newly created Gst::GhostPad.
   */
  Glib::RefPtr<Gst::GhostPad> add_ghost_pad(const Glib::RefPtr<Gst::Element>&
    element, const Glib::ustring& pad_name, const Glib::ustring& name =
    Glib::ustring());

  _WRAP_METHOD(Glib::RefPtr<Gst::Pad> find_unlinked_pad(PadDirection dir), gst_bin_find_unlinked_pad)
  _WRAP_METHOD(Glib::RefPtr<const Gst::Pad> find_unlinked_pad(PadDirection dir) const, gst_bin_find_unlinked_pad, constversion)
  _IGNORE(gst_bin_find_unconnected_pad)

  _WRAP_METHOD(Gst::Iterator<Gst::Element> iterate_elements(), gst_bin_iterate_elements)
  _WRAP_METHOD(Gst::Iterator<const Gst::Element> iterate_elements() const, gst_bin_iterate_elements)
  _WRAP_METHOD(Gst::Iterator<Gst::Element> iterate_recurse(), gst_bin_iterate_recurse)
  _WRAP_METHOD(Gst::Iterator<const Gst::Element> iterate_recurse() const, gst_bin_iterate_recurse)
  _WRAP_METHOD(Gst::Iterator<Gst::Element> iterate_sorted(), gst_bin_iterate_sorted)
  _WRAP_METHOD(Gst::Iterator<const Gst::Element> iterate_sorted() const, gst_bin_iterate_sorted)
  _WRAP_METHOD(Gst::Iterator<Gst::Element> iterate_sources(), gst_bin_iterate_sources)
  _WRAP_METHOD(Gst::Iterator<const Gst::Element> iterate_sources() const, gst_bin_iterate_sources)
  _WRAP_METHOD(Gst::Iterator<Gst::Element> iterate_sinks(), gst_bin_iterate_sinks)
  _WRAP_METHOD(Gst::Iterator<const Gst::Element> iterate_sinks() const, gst_bin_iterate_sinks)
  _WRAP_METHOD(Gst::Iterator<Gst::Element> iterate_by_interface(GType iface), gst_bin_iterate_all_by_interface)
  _WRAP_METHOD(Gst::Iterator<const Gst::Element> iterate_by_interface(GType iface) const, gst_bin_iterate_all_by_interface)

  _WRAP_METHOD(bool recalculate_latency(), gst_bin_recalculate_latency)

#m4 _CONVERSION(`GList*',`Glib::ListHandle< Glib::RefPtr<Gst::Element> >',`$2($3, Glib::OWNERSHIP_NONE)')
  /** Gets the bin's list of children.
   */
  _MEMBER_GET(children, children, Glib::ListHandle< Glib::RefPtr<Gst::Element> >, GList*)

  /** Gets the number of children in the bin.
   */
  _MEMBER_GET(num_children, numchildren, int, int)

  _WRAP_PROPERTY("async-handling", bool)
  _WRAP_PROPERTY("message-forward", bool)

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

  /** Will be emitted after the element was added to the bin.
   */
  _WRAP_SIGNAL(void element_added(const Glib::RefPtr<Gst::Element>& element), "element-added")

  /** Will be emitted after the element was removed from the bin.
   */
  _WRAP_SIGNAL(void element_removed(const Glib::RefPtr<Gst::Element>& element), "element-removed")

  //TODO: The default signal handler is marked as private in the C header. Is that intendend?
  _WRAP_SIGNAL(bool do_latency(), "do-latency")

  /** Method to add an element to a bin.
   */
  _WRAP_VFUNC(bool add_element(const Glib::RefPtr<Gst::Element>& element), "add_element")

  /** Method to remove an element from a bin.
   */
  _WRAP_VFUNC(bool remove_element(const Glib::RefPtr<Gst::Element>& element), "remove_element")

#m4 _CONVERSION(`GstMessage*',`const Glib::RefPtr<Gst::Message>&',`Gst::Message::wrap($3, true)')
  /** Method to handle a message from the children.
   */
  _WRAP_VFUNC(void handle_message(const Glib::RefPtr<Gst::Message>& message), "handle_message")
};

} //namespace Gst
