/* Copyright 2010 The glibmm 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.
 */

_DEFS(glibmm,glib)

#include <glibmmconfig.h>
#include <glibmm/varianttype.h>
#include <glibmm/ustring.h>

namespace Glib
{

/** @defgroup glibmmVariant Variant Datatype
 *
 * Glib::Variant<> are specialized classes that deal with strongly typed
 * variant data.  They are used to wrap glib's GVariant API.  For more
 * information see the <a
 * href="http://library.gnome.org/devel/glib/stable/glib-GVariant.html">glib
 * variant
 * API</a>.
 */

/** The base class used to wrap glib's GVariant API.
 * @newin{2,26}
 * @ingroup glibmmVariant
 */
class VariantBase
{
  _CLASS_OPAQUE_COPYABLE(VariantBase, GVariant, NONE, g_variant_ref_sink, g_variant_unref)
  _IGNORE(g_variant_ref, g_variant_ref_sink, g_variant_unref,
    g_variant_get, g_variant_get_va)
public:

  /** Replace the underlying GVariant.
   * This is for use by methods that take a VariantBase& as an output parameter.
   *
   * @param cobject The GVariant* obtained from a C function.
   * @param take_a_reference Whether this method should take a reference, for instance if the C function has not given one.
   */
  void init(const GVariant* cobject, bool take_a_reference = false);

  _WRAP_METHOD(VariantType get_type() const, g_variant_get_type)
  _WRAP_METHOD(std::string get_type_string() const, g_variant_get_type_string)
  _WRAP_METHOD(bool is_floating() const, g_variant_is_floating)
  _WRAP_METHOD(bool is_of_type(const VariantType& type) const, g_variant_is_of_type)
  _WRAP_METHOD(bool is_container() const, g_variant_is_container)
  _WRAP_METHOD(GVariantClass classify() const, g_variant_classify)

  bool get_maybe(Glib::VariantBase& maybe) const;
  _IGNORE(g_variant_get_maybe)

  //TODO: Somehow put this method only in specializations that use container types?:
  _WRAP_METHOD(gsize get_n_children() const, g_variant_n_children)

  _WRAP_METHOD(gsize get_size() const, g_variant_get_size)
  _WRAP_METHOD(gconstpointer get_data(), g_variant_get_data)
  _WRAP_METHOD(void store(gpointer data) const, g_variant_store)

  _WRAP_METHOD(Glib::ustring print(bool type_annotate = false) const, g_variant_print)
  _IGNORE(g_variant_print_string)

  #m4 _CONVERSION(`const VariantBase&',`gconstpointer',`const_cast<GVariant*>(($3).gobj())')
  _WRAP_METHOD(guint hash() const, g_variant_hash)
  _WRAP_METHOD(bool equal(const VariantBase& other) const, g_variant_equal)

  /** Gets a VariantBase instance that has the same value as this variant and
   * is trusted to be in normal form.
   *
   * If this variant is already trusted to be in normal form then a new
   * reference to the variant is returned.
   *
   * If this variant is not already trusted, then it is scanned to check if it
   * is in normal form. If it is found to be in normal form then it is marked
   * as trusted and a new reference to it is returned.
   *
   * If this variant is found not to be in normal form then a new trusted
   * VariantBase is created with the same value as this variant.
   *
   * It makes sense to call this function if you've received variant data from
   * untrusted sources and you want to ensure your serialised output is
   * definitely in normal form.
   *
   * @param result A location in which to store the trusted VariantBase.
   * @newin{2,24}
   */
  void get_normal_form(VariantBase& result) const;
  _IGNORE(g_variant_get_normal_form)

  _WRAP_METHOD(bool is_normal_form() const, g_variant_is_normal_form)

  /** Performs a byteswapping operation on the contents of this variant. The
   * result is that all multi-byte numeric data contained in the variant is
   * byteswapped. That includes 16, 32, and 64bit signed and unsigned integers
   * as well as file handles and double precision floating point values.
   *
   * This function is an identity mapping on any value that does not contain
   * multi-byte numeric data. That include strings, booleans, bytes and
   * containers containing only these things (recursively).
   *
   * The returned value is always in normal form and is marked as trusted.
   *
   * @param result A location in which to store the byteswapped form of this
   * variant.
   * @newin{2,24}
   */
   void byteswap(VariantBase& result) const;
   _IGNORE(g_variant_byteswap)
};

/** Template class from which other Glib::Variant<> specializations derive.
 * @newin{2,26}
 * @ingroup glibmmVariant
 */
template <class T>
class Variant : public VariantBase
{
public:
  typedef T CppType;
};


// Each specialization has (or should have) a variant_type() method that gets
// the type. So the C g_variant_get_type() function can be ignored.
_IGNORE(g_variant_get_type)

/****************** Specializations ***********************************/

/** Specialization of Glib::Variant containing a Glib::VariantBase.
 * @newin{2,26}
 * @ingroup glibmmVariant
 */
template <>
class Variant<VariantBase> : public VariantBase
{
public:
  typedef GVariant* CType;

  Variant<VariantBase>()
  : VariantBase()
  {}

  explicit Variant<VariantBase>(GVariant* castitem)
  : VariantBase(castitem)
  {}

  static const GVariantType* variant_type() G_GNUC_CONST;

  //This must have a create() method because otherwise it would be a copy constructor.
  static Variant<VariantBase> create(const Glib::VariantBase& data);
  _IGNORE(g_variant_new_variant)

  VariantBase get() const;
  _IGNORE(g_variant_get_variant)
};

/** Specialization of Glib::Variant containing a Glib::ustring.
 * @newin{2,26}
 * @ingroup glibmmVariant
 */
template <>
class Variant<Glib::ustring> : public VariantBase
{
public:
  typedef char* CType;

  Variant<Glib::ustring>()
  : VariantBase()
  {}

  explicit Variant<Glib::ustring>(GVariant* castitem)
  : VariantBase(castitem)
  {}

  static const GVariantType* variant_type() G_GNUC_CONST;
  static Variant<Glib::ustring> create(const Glib::ustring& data);

  Glib::ustring get() const;
  _IGNORE(g_variant_get_string, g_variant_dup_string)
};

} // namespace Glib


//We ignore g_variant_get_*() methods that are wrapped by Variant<> specializations, such as in variant_basictypes.h.m4.
_IGNORE(
  g_variant_get_boolean,
  g_variant_get_byte,
  g_variant_get_uint16,
  g_variant_get_int64,
  g_variant_get_int32,
  g_variant_get_int16,
  g_variant_get_uint32,
  g_variant_get_uint64,
  g_variant_get_double,
  g_variant_iter_new
)

/* Include generated specializations of Glib::Variant<> for fundamental types:
 */
#define _GLIBMM_VARIANT_H_INCLUDE_VARIANT_BASICTYPES_H
#include <glibmm/variant_basictypes.h>
#undef _GLIBMM_VARIANT_H_INCLUDE_VARIANT_BASICTYPES_H
