/* Copyright (C) 2010 and 2011 Chris Vine

The library comprised in this file or of which this file is part is
distributed by Chris Vine under the GNU Lesser General Public
License as follows:

   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, version 2.1, for more details.

   You should have received a copy of the GNU Lesser General Public
   License, version 2.1, along with this library (see the file LGPL.TXT
   which came with this source code package in the c++-gtk-utils
   sub-directory); if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

However, it is not intended that the object code of a program whose
source code instantiates a template from this file or uses macros or
inline functions (of any length) should by reason only of that
instantiation or use be subject to the restrictions of use in the GNU
Lesser General Public License.  With that in mind, the words "and
macros, inline functions and instantiations of templates (of any
length)" shall be treated as substituted for the words "and small
macros and small inline functions (ten lines or less in length)" in
the fourth paragraph of section 5 of that licence.  This does not
affect any other reason why object code may be subject to the
restrictions in that licence (nor for the avoidance of doubt does it
affect the application of section 2 of that licence to modifications
of the source code in this file).

*/

#ifndef CGU_GVAR_HANDLE_H
#define CGU_GVAR_HANDLE_H

#include <functional> // for std::less and std::hash<T*>
#include <cstddef>    // for std::size_t

#include <glib.h>

#if defined(DOXYGEN_PARSING) || GLIB_CHECK_VERSION(2,24,0)

#include <c++-gtk-utils/cgu_config.h>

/**
 * @addtogroup handles handles and smart pointers
 */

namespace Cgu {

/**
 * @class GvarHandle gvar_handle.h c++-gtk-utils/gvar_handle.h
 * @brief This is a handle for managing the reference count of
 * GVariant objects.
 * @ingroup handles
 * @sa GobjHandle
 *
 * This is a class which manages the reference count of GVariant
 * objects.  It does not maintain its own reference count, but
 * interfaces with that kept by the GVariant object.
 *
 * GVariant objects created with one of the g_variant_new*() functions
 * are created with a floating reference.  The constructor of a
 * GvarHandle which takes a pointer will automatically take
 * ownership of such a newly created GVariant object, by calling
 * g_variant_ref_sink().
 *
 * GVariant objects which are obtained by one of the g_variant_get*()
 * or similar getter functions, such as g_variant_get_child_value(),
 * g_variant_get_variant(), g_variant_get() with a "v" or "(v)" format
 * string, or g_variant_iter_next_value(), or as the return value of a
 * dbus method call in gio's dbus implementation such as
 * g_dbus_connection_call_sync(), g_dbus_connection_call_finish(),
 * g_dbus_proxy_call_sync() or g_dbus_proxy_call_finish(), are not
 * created with a floating reference (the variant normally already
 * exists), but instead the reference count is incremented by the
 * function concerned when the object is passed out to the user, so
 * giving ownership to the user.
 *
 * It follows that g_variant_ref_sink() is not called by the
 * constructor taking a pointer if the floating reference has already
 * been sunk.  This behaviour will ensure that the handle behaves the
 * same whether it is passed a GVariant object from one of the
 * g_variant_new*() functions, or from one of the g_variant_get*() and
 * other getter functions mentioned above.  One consequence is that if
 * the constructor taking a pointer is passed a pointer which has
 * already been passed to and is managed by another GvarHandle object,
 * the user must call g_variant_ref() herself explicitly: but GVariant
 * objects already owned by a GvarHandle should not normally be passed
 * to another GvarHandle that way, as GvarHandles have a copy
 * constructor and assignment operator which will increment the
 * reference count automatically.
 *
 * In other words, invoke the constructor taking a pointer only with a
 * newly created GVariant object, or with a GVariant object directly
 * handed out by a GVariant getter function or as the return value of
 * a gio dbus method call, and everything else will take care of
 * itself.  In this respect, GvarHandles work the same way as
 * conventional shared pointer implementations managing objects
 * allocated on free store.  The same applies to the reset() method.
 *
 * Because glib and gio themselves increment the reference count of a
 * GVariant object where they need to take ownership, an
 * already-managed object held by a GvarHandle can safely be passed by
 * pointer as the first argument of one of glib's g_variant_*()
 * functions or as an argument to one of gio's dbus functions, and
 * that is one of its intended usages.  For that purpose, the pointer
 * can be obtained by means of the operatorT*() type conversion
 * operator (which returns the underlying pointer), or by explicitly
 * calling the get() method to obtain the underlying pointer.
 *
 * By automatically handling GVariant reference counts, GvarHandle
 * makes GVariant objects exception-safe, and they also permit
 * GVariant objects to be kept in standard C++ containers.
 *
 * GVariant objects are thread safe, including their reference counts.
 * This means that a GVariant object may be held by GvarHandles in
 * containers in different threads, and accessed concurrently in those
 * different threads.
 *
 * Typical usage might be, for example, as follows:
 *
 * @code
 *    // execute a method "TwoInts" taking two 'int' arguments and
 *    // returning a string-array via g_dbus_proxy_call_sync()
 *    gint32 a = 2;
 *    gint32 b = 10;
 *    Cgu::GvarHandle result(g_dbus_proxy_call_sync(proxy,
 *                                                 "TwoInts",
 *                                                 g_variant_new("(ii)", a, b),
 *                                                 G_DBUS_CALL_FLAGS_NONE,
 *                                                 -1,
 *                                                 0,
 *                                                 0));
 *    if (!result.get()) {
 *      g_critical("Failed to execute method TwoInts");
 *      execute_error_strategy();
 *    }
 *    else {
 *      // gio's dbus implementation wraps all return
 *      // values in a tuple: first extract the string-array
 *      // from the tuple
 *      Cgu::GvarHandle sa_variant(g_variant_get_child_value(result, 0));
 *      // free str_arr with g_strfreev()
 *      gchar** str_arr = g_variant_dup_strv(sa_variant, 0);
 *    }
 * @endcode
 *
 * Further examples of the use of GvarHandle, see @ref Variants
 *
 * @note This class is only available if glib >= 2.24.0 is installed.
 */

class GvarHandle {

  GVariant* obj_p;

  void unreference() noexcept {
    if (obj_p) g_variant_unref(obj_p);
  }

  void reference() noexcept {
    if (obj_p) g_variant_ref(obj_p);
  }

public:

 /**
  * The constructor does not throw.  g_variant_ref_sink() is called if
  * the managed object has a floating reference.
  * @param ptr The GVariant object which the GvarHandle is to manage
  * (if any).
  * @note The pointer passed, if not NULL, should not normally already
  * have been given to and so managed by any other GvarHandle object.
  * Use the copy constructor instead instead in that case.
  */
  explicit GvarHandle(GVariant* ptr = 0) noexcept {
    obj_p = ptr;

    // if an object with a floating reference has been passed to this constructor,
    // take ownership of it
    if (ptr && g_variant_is_floating(ptr)) {
      g_variant_ref_sink(ptr);
    }
  }

 /**
  * Causes the handle to cease to manage its managed object (if any)
  * and decrements its reference count, so destroying it if the
  * reference count thereby becomes 0.  If the argument passed is not
  * NULL, the handle will manage the new GVariant object passed and
  * g_variant_ref_sink() is called if the new object has a floating
  * reference.  This method does not throw.
  * @param ptr NULL (the default), or a new GVariant object to manage.
  * @note The pointer passed, if not NULL, should not normally already
  * have been given to and so managed by any other GvarHandle object.
  * Use the assignment operator instead in that case.
  */
  void reset(GVariant* ptr = 0) noexcept {
    
    unreference();
    obj_p = ptr;

    // if an object with a floating reference has been passed to this method,
    // take ownership of it
    if (ptr && g_variant_is_floating(ptr)) {
      g_variant_ref_sink(ptr);
    }
  }

 /**
  * The copy constructor does not throw.  It increments the reference
  * count of the managed object.
  * @param gvar The handle to be copied.
  */
  GvarHandle(const GvarHandle& gvar) noexcept {
    obj_p = gvar.obj_p;
    reference();
  }

 /**
  * The move constructor does not throw.  It has move semantics.
  * @param gvar The handle to be moved.
  */
  GvarHandle(GvarHandle&& gvar) noexcept {
    obj_p = gvar.obj_p;
    gvar.obj_p = 0;
  }

 /**
  * This method does not throw.  It decrements the reference count of
  * the former managed object (if any), so destroying it if the
  * reference count thereby becomes 0, and increments the reference
  * count of the new managed GVariant object.
  * @param gvar The assignor.
  * @return The GvarHandle object after assignment.
  */
  GvarHandle& operator=(const GvarHandle& gvar) noexcept {

    // check whether we are already referencing this object -
    // if so make this a null op.  This will also deal with
    // self-assignment
    if (obj_p != gvar.obj_p) {

      // first unreference any object referenced by this handle
      unreference();

      // now inherit the GVariant from the assigning handle
      // and reference it
      obj_p = gvar.obj_p;
      reference();
    }
    return *this;
  }

 /**
  * This method does not throw.  It decrements the reference count of
  * the former managed object (if any), so destroying it if the
  * reference count thereby becomes 0, and has move semantics with
  * respect to the new managed object.
  * @param gvar The handle to be moved.
  * @return The GvarHandle object after the move operation.
  */
  GvarHandle& operator=(GvarHandle&& gvar) noexcept {

    // check for self-assignment
    if (this != &gvar) {

      // first unreference any object referenced by this handle
      unreference();

      // now inherit the GVariant from the moving handle
      obj_p = gvar.obj_p;
      gvar.obj_p = 0;
    }
    return *this;
  }

 /**
  * This method does not throw.
  * @return A pointer to the handled GVariant object (or NULL if none
  * is handled).
  */
  GVariant* get() const noexcept {return obj_p;}

 /**
  * This method does not throw.
  * @return A pointer to the handled GVariant object (or NULL if none
  * is handled).
  */
  operator GVariant*() const noexcept {return obj_p;}

 /**
  * The destructor does not throw.  It decrements the reference count
  * of the managed object (if any), so destroying it if the reference
  * count thereby becomes 0.
  */
  ~GvarHandle() {unreference();}
};

#if defined(CGU_USE_SMART_PTR_COMPARISON) || defined(DOXYGEN_PARSING)

// we can use built-in operator == when comparing pointers referencing
// different objects of the same type
/**
 * @ingroup handles
 *
 * This comparison operator does not throw.  It compares the addresses
 * of the managed objects.
 *
 * Since 2.0.0-rc2
 */
inline bool operator==(const GvarHandle& h1, const GvarHandle& h2) noexcept {
  return (h1.get() == h2.get());
}

/**
 * @ingroup handles
 *
 * This comparison operator does not throw.  It compares the addresses
 * of the managed objects.
 *
 * Since 2.0.0-rc2
 */
inline bool operator!=(const GvarHandle& h1, const GvarHandle& h2) noexcept {
  return !(h1 == h2);
}

// we must use std::less rather than the < built-in operator for
// pointers to objects not within the same array or object: "For
// templates greater, less, greater_equal, and less_equal, the
// specializations for any pointer type yield a total order, even if
// the built-in operators <, >, <=, >= do not." (para 20.3.3/8).
/**
 * @ingroup handles
 *
 * This comparison operator does not throw unless std::less applied to
 * pointer types throws (which it would not do with any sane
 * implementation).  It compares the addresses of the managed objects.
 *
 * Since 2.0.0-rc2
 */
inline bool operator<(const GvarHandle& h1, const GvarHandle& h2) {
  return std::less<GVariant*>()(h1.get(), h2.get());
}

#endif // CGU_USE_SMART_PTR_COMPARISON

} // namespace Cgu

// doxygen produces long filenames that tar can't handle:
// we have generic documentation for std::hash specialisations
// in doxygen.main.in
#if defined(CGU_USE_SMART_PTR_COMPARISON) && !defined(DOXYGEN_PARSING)
/* This struct allows GvarHandle objects to be keys in unordered
   associative containers */
namespace std {
template <>
struct hash<Cgu::GvarHandle> {
  typedef std::size_t result_type;
  typedef Cgu::GvarHandle argument_type;
  result_type operator()(const argument_type& h) const {
    // this is fine: std::hash structs do not normally contain data and
    // std::hash<T*> certainly won't, so we don't have overhead constructing
    // std::hash<T*> on the fly
    return std::hash<GVariant*>()(h.get());
  }
 };
} // namespace std
#endif // CGU_USE_SMART_PTR_COMPARISON

#else
#warning GvarHandle not available: glib >= 2.24.0 is required
#endif /*GLIB_CHECK_VERSION(2,24,0)*/

#endif /*CGU_GVAR_HANDLE_H*/
