// -*- c++ -*-
#ifndef _GLIBMM_WEAKREF_H
#define _GLIBMM_WEAKREF_H

/* Copyright 2002 The gtkmm 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 <glib-object.h>
#include <glibmm/refptr.h>

namespace Glib
{

/** WeakRef<> is a weak reference smartpointer
 *
 * WeakRef can store any class that is derived from Glib::ObjectBase.
 *
 * Unlike a RefPtr, a WeakRef does not contribute to the reference counting of
 * the underlying object.
 */
template <class T_CppObject>
class WeakRef
{
public:
  /** Default constructor
   *
   * Create an empty weak reference.
   */
  inline WeakRef();

  /** Copy constructor
   */
  inline WeakRef(const WeakRef<T_CppObject>& src);

  /** Constructor
   *
   * Create a Weak Reference from a RefPtr.
   * If the RefPtr references nothing, an empty weak reference will be
   * constructed.
   */
  inline WeakRef(const RefPtr<T_CppObject>& src);

  /// Destructor
  inline ~WeakRef();

  /// Copy from another WeakRef
  inline WeakRef<T_CppObject>&
  operator=(const WeakRef<T_CppObject>& src);

  /// Copy from another RefPtr
  inline WeakRef<T_CppObject>&
  operator=(const RefPtr<T_CppObject>& src);

  /** Create a strong reference to the underlying object
   *
   * This is a thread-safe way to acquire a strong reference to the underlying
   * object.
   */
  inline RefPtr<T_CppObject>
  ref() const;

  /// Make this WeakRef empty
  inline void
  reset();

private:
  inline void
  set(const RefPtr<T_CppObject>& src);

  inline void
  set(T_CppObject* pCppObject);

  T_CppObject* pCppObject_;
  mutable GWeakRef gobject_;

};

template <class T_CppObject>
WeakRef<T_CppObject>::WeakRef()
{
  g_weak_ref_init(&gobject_, NULL);
  pCppObject_ = NULL;
}

template <class T_CppObject>
WeakRef<T_CppObject>::WeakRef(const WeakRef<T_CppObject>& src)
{
  g_weak_ref_init(&gobject_, NULL);
  pCppObject_ = NULL;

  *this = src;
}

template <class T_CppObject>
WeakRef<T_CppObject>::WeakRef(const RefPtr<T_CppObject>& src)
{
  g_weak_ref_init(&gobject_, NULL);
  pCppObject_ = NULL;

  *this = src;
}

template <class T_CppObject>
WeakRef<T_CppObject>::~WeakRef()
{
  reset();
}

template <class T_CppObject> void
WeakRef<T_CppObject>::reset()
{
  set(NULL);
}

template <class T_CppObject> WeakRef<T_CppObject>&
WeakRef<T_CppObject>::operator=(const WeakRef<T_CppObject>& src)
{
  set(src.ref());
  return *this;
}

template <class T_CppObject> WeakRef<T_CppObject>&
WeakRef<T_CppObject>::operator=(const RefPtr<T_CppObject>& src)
{
  set(src);
  return *this;
}

template <class T_CppObject> RefPtr<T_CppObject>
WeakRef<T_CppObject>::ref() const
{
  RefPtr<T_CppObject> ret;
  gpointer ptr;

  if (pCppObject_ == NULL) {
    goto fail;
  }

  ptr = g_weak_ref_get(&gobject_);
  if (ptr == NULL) {
    goto fail;
  }

  // A RefPtr constructed from pointer expects reference to be done externally
  pCppObject_->reference();
  ret = RefPtr<T_CppObject>(pCppObject_);

  g_object_unref(ptr);
fail:
  return ret;
}

template <class T_CppObject> void
WeakRef<T_CppObject>::set(const RefPtr<T_CppObject>& src)
{
  T_CppObject* pCppObject = src.operator->();
  set(pCppObject);
}

template <class T_CppObject> void
WeakRef<T_CppObject>::set(T_CppObject* pCppObject)
{
  g_weak_ref_set(&gobject_, NULL);
  pCppObject_ = pCppObject;

  if (pCppObject != NULL) {
    g_weak_ref_set(&gobject_, pCppObject->gobj());
  }
}

} // namespace Glib

#endif // _GLIBMM_WEAKREF_H
