/* 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/structure.h>

namespace Gst
{

GType Caps::get_type()
{
  return gst_caps_get_type();
}

Glib::RefPtr<Gst::Caps> Caps::create_any()
{
  return Glib::wrap(gst_caps_new_any());
}

Glib::RefPtr<Gst::Caps> Caps::copy() const
{
  return Glib::wrap(gst_caps_copy(const_cast<GstCaps*>(gobj())));
}

/*
  This method is implemented because gst_caps_new_simple is a variable argument
  function and cannot be wrapped.  In addition, because gst_caps_new_simple
  creates a GstStructure for the GstCaps and will not be called, we must create
  our own GstStructure here and append it to the GstCaps.
*/
Glib::RefPtr<Gst::Caps> Caps::create_simple(const Glib::ustring& media_type)
{
  Glib::RefPtr<Gst::Caps> result = Glib::wrap(gst_caps_new_empty());
  GstStructure* gst_struct = gst_structure_new_empty(media_type.c_str());
  gst_caps_append_structure(Glib::unwrap(result), gst_struct);
  return result;
}

Glib::RefPtr<Gst::Caps> Caps::create_writable()
{
  /*
   * This function is generally used in the following pattern:
   * RefPtr<Caps> p = (...);
   * p = p->create_writable();
   *
   * There are two cases:
   * 1. object is not writable, therefore:
   *   - somebody else may have another reference to the object (but it this might change in the meantime)
   *   - gst_caps_make_writable may return a new copy of object
   *   - we have to make additional ref, that will be unreffed in gst_caps_make_writable
   * 2. object is_writable, then:
   *   - our caller has the only one reference to object (therefore nobody can increase refcount in the meantime)
   *   - gst_caps_make_writable will return the same object and will not do any reffing/unreffing
   *   - we cannot make any additional refs before calling gst_caps_make_writable, since it would enforce unnecessary
   *     copying of the object
   *   - we are supposed to create a new RefPtr with is own reference to the object, therefore we need to "take copy"
   *   - however when caller release the pointer (implicitly, during assignment) the refcount will be 1 again
   */
  if(gst_caps_is_writable(gobj()))
  {
    return Glib::wrap(gst_caps_make_writable(gobj()), true); // take copy so original object is left to the current owner (and hopefully will be released soon)
  }
  else
  {
    reference(); // gst_caps_make_writable(buf) will unref the old caps, but our caller is still holding RefPtr to it
    return Glib::wrap(gst_caps_make_writable(gobj()));
  }
}

Glib::RefPtr<Gst::Caps> Caps::create(const Structure& structure)
{
  //We take a copy because gst_caps_append_structure() wants to take ownership:
  GstStructure* copy = gst_structure_copy(structure.gobj());
  return Glib::wrap(gst_caps_new_full(copy, NULL));
}

void Caps::append_structure(const Structure& structure)
{
  //We take a copy because gst_caps_append_structure() wants to take ownership:
  GstStructure* copy = gst_structure_copy(structure.gobj());
  gst_caps_append_structure(gobj(), copy);
}

Glib::RefPtr<Caps> Caps::merge_structure(Structure& structure)
{
  //We take a copy because gst_caps_append_structure() wants to take ownership:
  GstStructure* copy = gst_structure_copy(structure.gobj());
  return Glib::wrap(gst_caps_merge_structure(gobj(), copy), true);
}

//TODO: Want to return RefPtr to Caps but using RefPtr in expressions such
// as 'caps->set_simple(name1, value1)->set_simple(name2, value2)' a
// causes gstreamer Structure immutability warnings because the Caps is
// referenced more than once in the expression (see bug #510301).
/*
  This method is implemented in place of gst_caps_set_simple which is a
  variable argument function and cannot be wrapped.  We don't call
  gst_caps_set_simple() directly because we are using a Glib::ValueBase as the
  value to be set instead of listing out each field in the value as would be
  done with gst_caps_set_simple()
*/
//Glib::RefPtr<Gst::Caps>
void
Caps::set_simple(const Glib::ustring& name, const Glib::ValueBase& value)
{
  g_return_if_fail (g_atomic_int_get(&(this->gobj())->mini_object.refcount) == 1); // IS_WRITABLE(caps) fails

  //The result of gst_caps_get_structure() should not be modified, according to
  //its documentation, but we are reimplementing gst_caps_set_simple() which
  //does that:
  GstStructure* structure = gst_caps_get_structure(gobj(), 0);
  if(structure)
    gst_structure_set_value(structure, name.c_str(), value.gobj());

  //return Glib::wrap(gobj(), true);
}

void
Caps::set_simple(const Glib::ustring& name, const char* data)
{
  set_simple(name, std::string(data));
}

} //namespace Gst
