/* 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/gstutils.h>
#include <gst/gstenumtypes.h>
#include <gstreamermm/buffer.h>
#include <gstreamermm/caps.h>
#include <gstreamermm/element.h>
#include <gstreamermm/event.h>
#include <gstreamermm/padtemplate.h>
#include <gstreamermm/iterator.h>

_PINCLUDE(glibmm/private/object_p.h)
_PINCLUDE(gstreamermm/private/object_p.h)

namespace
{

extern "C"
{

static void Pad_Block_gstreamermm_callback(GstPad* pad, gboolean blocked, void* data)
{
  Gst::Pad::SlotBlock * the_slot = static_cast<Gst::Pad::SlotBlock*>(data);

  try
  {
    (*the_slot)(Glib::wrap(pad), blocked);
    delete the_slot;
  }
  catch(...)
  {
    Glib::exception_handlers_invoke();
  }
}

static void Pad_Block_gstreamermm_callback_destroy(void* data)
{
  delete static_cast<Gst::Pad::SlotBlock*>(data);
}

static gboolean Pad_Data_gstreamermm_callback(GstPad* pad, GstMiniObject* mini_obj, void* data)
{
  Gst::Pad::SlotData* the_slot = static_cast<Gst::Pad::SlotData*>(data);

  try
  {
    return (*the_slot)(Glib::wrap(pad, true), Gst::wrap(mini_obj, true));
  }
  catch(...)
  {
    Glib::exception_handlers_invoke();
  }

  return false;
}

static void Pad_Data_gstreamermm_callback_disconnect(void* data)
{
  Gst::Pad::SlotData* the_slot = static_cast<Gst::Pad::SlotData*>(data);

  if(the_slot)
    delete the_slot;
}

} // extern "C"

} // anonymous namespace

namespace Gst
{

Pad::Pad(PadDirection dir)
  : _CONSTRUCT("name", static_cast<char*>(0), "direction", dir)
{}

Pad::Pad(const Glib::RefPtr<const Gst::PadTemplate>& templ,
  const Glib::ustring& name)
  : _CONSTRUCT("name", (name.empty() ? static_cast<char*>(0) : name.c_str()),
    "direction", (templ ? static_cast<GstPadDirection>(templ->get_direction())
      : GST_PAD_UNKNOWN),
    "template", Glib::unwrap(templ))
{
  if(!templ)
    gstreamermm_handle_error(
      "Gst::Pad::Pad(const Glib::RefPtr<Gst::PadTemplate>&, "
      "const Glib::ustring&): Failed to construct pad (name = " + name + ") "
      "from null template.");
}

Pad::Pad(const Glib::RefPtr<const Gst::PadTemplate>& templ)
  : _CONSTRUCT("name", static_cast<char*>(0),
    "direction", (templ ? static_cast<GstPadDirection>(templ->get_direction())
      : GST_PAD_UNKNOWN),
    "template", Glib::unwrap(templ))
{
  if(!templ)
    gstreamermm_handle_error(
      "Gst::Pad::Pad(const Glib::RefPtr<Gst::PadTemplate>&): Failed to "
      "construct pad from null template.");
}

// This is handcoded because the documentation tells us that we need to copy
// the Caps
Glib::RefPtr<const Gst::Caps> Pad::get_pad_template_caps() const
{
  const GstCaps* caps = gst_pad_get_pad_template_caps(const_cast<GstPad*>(gobj()));
  GstCaps* copy_caps = gst_caps_copy(caps);
  return Glib::wrap(copy_caps, false);
}

FlowReturn Pad::alloc_buffer(guint64 offset, int size, const Glib::RefPtr<Gst::Caps>& caps, Glib::RefPtr<Gst::Buffer>& buffer)
{
  GstBuffer* c_buffer = 0;
  GstFlowReturn result = gst_pad_alloc_buffer(gobj(), offset, size, caps->gobj(), &c_buffer);
  buffer = wrap(c_buffer, false);
  return FlowReturn(result);
}

FlowReturn Pad::alloc_buffer_and_set_caps(guint64 offset, int size, const Glib::RefPtr<Gst::Caps>& caps, Glib::RefPtr<Gst::Buffer>& buffer)
{
  GstBuffer* c_buffer = 0;
  GstFlowReturn result = gst_pad_alloc_buffer_and_set_caps(gobj(), offset, size, caps->gobj(), &c_buffer);
  buffer = wrap(c_buffer, false);
  return FlowReturn(result);
}

FlowReturn Pad::get_range(guint64 offset, guint size, Glib::RefPtr<Gst::Buffer>& buffer)
{
  GstBuffer* c_buffer = 0;
  GstFlowReturn result = gst_pad_get_range(gobj(), offset, size, &c_buffer);
  buffer = wrap(c_buffer, false);
  return FlowReturn(result);
}

FlowReturn Pad::push(const Glib::RefPtr<Gst::Buffer>& buffer)
{
  buffer->reference();
  return FlowReturn(gst_pad_push(gobj(), buffer->gobj()));
}

bool Pad::push_event(const Glib::RefPtr<Gst::Event>& event)
{
  event->reference();
  return gst_pad_push_event(gobj(), event->gobj());
}

FlowReturn Pad::pull_range(guint64 offset, guint size, Glib::RefPtr<Gst::Buffer>& buffer)
{
  GstBuffer* c_buffer = 0;
  GstFlowReturn result = gst_pad_pull_range(gobj(), offset, size, &c_buffer);
  buffer = wrap(c_buffer, false);
  return FlowReturn(result);
}

bool Pad::send_event(const Glib::RefPtr<Gst::Event>& event)
{
  event->reference();
  return gst_pad_send_event(gobj(), event->gobj());
}

bool Pad::query_position(Format& format) const
{
  return gst_pad_query_position(const_cast<GstPad*>(gobj()), reinterpret_cast<GstFormat*>(&format), 0);
}

bool Pad::query_duration(Format& format) const
{
  return gst_pad_query_duration(const_cast<GstPad*>(gobj()), reinterpret_cast<GstFormat*>(&format), 0);
}

bool Pad::query_peer_position(Format& format) const
{
  return gst_pad_query_peer_position(const_cast<GstPad*>(gobj()), reinterpret_cast<GstFormat*>(&format), 0);
}

bool Pad::query_peer_duration(Format& format) const
{
  return gst_pad_query_peer_duration(const_cast<GstPad*>(gobj()), reinterpret_cast<GstFormat*>(&format), 0);
}

FlowReturn Pad::chain(const Glib::RefPtr<Gst::Buffer>& buffer)
{
  buffer->reference();
  return FlowReturn(gst_pad_chain(gobj(), buffer->gobj()));
}

bool Pad::set_blocked_async(const SlotBlock& slot, bool blocked)
{
  SlotBlock* slot_copy = new SlotBlock(slot);
  return bool(gst_pad_set_blocked_async_full(GST_PAD(gobj()), gboolean(blocked), &Pad_Block_gstreamermm_callback, slot_copy, &Pad_Block_gstreamermm_callback_destroy));
}

gulong Pad::add_data_probe(const SlotData& slot)
{
  SlotData* slot_copy = new SlotData(slot);

  return gst_pad_add_data_probe_full(gobj(), G_CALLBACK (Pad_Data_gstreamermm_callback), slot_copy, &Pad_Data_gstreamermm_callback_disconnect);
}

gulong Pad::add_buffer_probe(const SlotData& slot)
{
  SlotData* slot_copy = new SlotData(slot);

  return gst_pad_add_buffer_probe_full(gobj(), G_CALLBACK (Pad_Data_gstreamermm_callback), slot_copy, &Pad_Data_gstreamermm_callback_disconnect);
}

gulong Pad::add_event_probe(const SlotData& slot)
{
  SlotData* slot_copy = new SlotData(slot);

  return gst_pad_add_event_probe_full(gobj(), G_CALLBACK (Pad_Data_gstreamermm_callback), slot_copy, &Pad_Data_gstreamermm_callback_disconnect);
}

} //namespace Gst
