/* gstreamermm - a C++ wrapper for gstreamer
 *
 * Copyright 2008-2016 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>


_DEFS(gstreamermm,gst)

namespace Gst
{

class Pad;
class GhostPad;

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

_WRAP_ENUM(BinFlags, GstBinFlags)

/**
 * 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 SEGMENT_START have posted a SEGMENT_DONE.
 *
 * - Gst::MESSAGE_DURATION_CHANGED - 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. Note that these messages can be posted before the bin is
 * prerolled, in which case the duration query might fail.
 *
 * - 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 2016-05-05 (1.8.0).
 *
 * @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.  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, the pads will be unlinked
   * before the element is added to the bin.
   *
   * When you add an element to an already-running pipeline, you will have to
   * take care to set the state of the newly-added element to the desired
   * state (usually PLAYING or PAUSED, same you set the pipeline to originally)
   * with set_state(), or use sync_state_with_parent().
   * The bin or pipeline will not take care of this for you.
   *
   * 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.
   *
   * @param element the Gst::Element to remove
   * @return 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)

  _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 sync_children_states(), gst_bin_sync_children_states)

  _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 children cookie.
   */
  _MEMBER_GET(children_cookie, children_cookie, guint32, guint32)

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

  /** Gets the internal bus for handling child messages.
   */
  _MEMBER_GET_GOBJECT(child_bus,child_bus, Gst::Bus, GstBus*)

  #m4 _CONVERSION(`GList*',`Glib::ListHandle< Glib::RefPtr<Gst::Message> >',`$2($3, Glib::OWNERSHIP_NONE)')
  /** Gets the bin's queued and cached messages.
   */
  _MEMBER_GET(messages, messages, Glib::ListHandle< Glib::RefPtr<Gst::Message> >, GList*)

  /** Gets whether the bin is currently calculating its state.
   */
  _MEMBER_GET(polling, polling, bool, gboolean)

  /** Gets whether the bin needs to select a new clock.
   */
  _MEMBER_GET(clock_dirty, clock_dirty, bool, gboolean)

  /** Gets the last clock selected.
   */
  _MEMBER_GET_GOBJECT(provided_clock, provided_clock, Gst::Clock, GstClock*)

  /** Gets the element that provided @a provided_clock.
   */
  _MEMBER_GET_GOBJECT(clock_provider, clock_provider, Gst::Element, GstElement*)

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

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

  _WRAP_SIGNAL(void element_added(const Glib::RefPtr<Gst::Element>& element), "element-added")
  _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>&',`Glib::wrap($3, true)')
  /** Method to handle a message from the children.
   */
  _WRAP_VFUNC(void handle_message(const Glib::RefPtr<Gst::Message>& message), "handle_message")
};

/*! A gstreamermm Gst::Bin example.
 * @example basics/bin.cc
 */

} //namespace Gst
