/* -*- C++ -*-
 */
/** @file "utils/SingletonUse.hh"
 * @brief Definition of SingletonUse template class
 *
 * @author Frederic Py <fpy@mbari.org>
 */
#ifndef CLUSTER_SINGLETON_USE_HH
# define CLUSTER_SINGLETON_USE_HH

# include "dmem/dnew.hh"

/** @brief Phoenix singleton entry point.
 *
 * This class may be viewed as a smart pointer (reference counting) to a singleton instance.
 * The singleton instance is managed using the phoenix design pattern. That means that
 * we guarantee that the singlton will exist each time it is requested and will be a uniaue
 * instance, still it will be destroyed if no more clients  are connected to it. An esay way
 * to ensure that it will be a pure singleton existsing during the whole program life is to declare
 * a static instance of SingletonUse.
 *
 * @param Ty type of the singleton 
 *
 * @note Another thing to note about this class is that it does not ensure that @e Ty cannot
 * be created by othger means. To have this guarantee the best thing to do is to declare the
 * constructors/destructor of @e Ty has private and SingletonUse<Ty> as a fired of this class.
 *
 * @bug As this class is based on the simple reference counting paradigm it does not support cyclic
 * references. If you wan to do singlton that are autoreferencing themselves using this class the
 * two singletons will not be destroyed properly.  
 *
 * @author Frederic Py <fpy@mbari.org>
 */
template<class Ty>
class SingletonUse {
public:
  /** @brief Constructor.
   *
   * Create a new entry point to the singleton. If the singleton instance did not
   * exist it creates it. On any case it incrment the singleton reference counter to indicate
   * that a new client is connected to the singleton.
   *
   * @sa void incrRef()
   */
  SingletonUse();
  /** @brief Copy constructor.
   *
   * This operator does nothing special compared to the default constructor.
   * It is just defined to avoid automatic definition of this constructor by
   * the compiler. 
   * 
   * @param other another SingletonUse instance.
   *
   * @sa SingletonUse()
   */
  SingletonUse(SingletonUse const &other);
  /** @brief Destructor.
   *
   * Decrement the reference counter to indicate that this client is not existing anymore.
   * If the counter reaches the 0 value then destrfoy the singleton instance.
   *
   * @sa void decrRef()
   */
  ~SingletonUse();

  /** @brief Copy operator.
   *
   * This opêrator is redefined here to avoid the automatic generation
   * of it by the compiler. It does nothing as the instance already
   * points correctly to the singleton.
   *
   * @param other another instance.
   * @return *this
   */
  SingletonUse &operator= (SingletonUse const &other);
  /** @brief Singleton access.
   *
   * @return The singleton instance.
   *
   * @sa Ty *operator->() const
   * @sa Ty &operator* () const
   */
  Ty &instance() const;

  /** @brief Singleton access.
   *
   * This operator allows to access directly to singleton
   * attributes and methods.
   *
   * @return A pointer to the singleton
   *
   * @sa Ty &instance() const
   * @sa Ty &operator* () const
   */
  Ty *operator->() const;
  /** @brief Singleton access.
   *
   * @return The singleton instance.
   *
   * @sa Ty *operator->() const
   * @sa Ty &instance() const
   */
  Ty &operator* () const;

private:
  /** @brief Singleton instance.
   *
   * Stores the pointer to The singleton instance.
   */
  static Ty    *s_singleton;
  /** @brief Reference counter
   *
   * This variable maintains the number of clients
   * connected to the singleton.
   *
   * @sa void incrRef()
   * @sa void decrRef()
   */
  static size_t s_refCount;
  
  /** @brief Increment reference counter.
   *
   * This method is called during construction to
   * increment the reference counter and possibly
   * creates the singleton instance if it did not exist.
   *
   * @sa void decrRef() 
   */
  static void incrRef();
  /** @brief Decrement reference counter.
   *
   * This method is called during destruyction to
   * decrement the reference counter and possibly
   * destroy the singleton instance if the counter
   * reached 0.
   *
   * @sa void incrRef() 
   */
  static void decrRef();
}; // SingletonUse<>

/*
 * class SingletonUse<>
 */
// statics :

template<class Ty>
Ty *SingletonUse<Ty>::s_singleton = NULL;

template<class Ty>
size_t SingletonUse<Ty>::s_refCount = 0;

template<class Ty>
void SingletonUse<Ty>::incrRef() {
  if( NULL==s_singleton ) {
    s_singleton = new Ty;
    s_refCount = 0;
  }
  ++s_refCount;
}

template<class Ty>
void SingletonUse<Ty>::decrRef() {
  if( NULL!=s_singleton && (s_refCount--)<=1 ) {
    Ty *to_del = s_singleton;
    s_singleton = NULL;
    delete to_del;
  }
}

// structors :

template<class Ty>
SingletonUse<Ty>::SingletonUse() {
  incrRef();
}

template<class Ty>
SingletonUse<Ty>::SingletonUse(SingletonUse<Ty> const &) {
  incrRef();
}

template<class Ty>
SingletonUse<Ty>::~SingletonUse() {
  decrRef();
}

// modifiers:

template<class Ty>
SingletonUse<Ty> &SingletonUse<Ty>::operator= (SingletonUse<Ty> const &) {
  return *this;
}

// observers :

template<class Ty>
Ty &SingletonUse<Ty>::instance() const {
  return *operator->();
}

template<class Ty>
Ty *SingletonUse<Ty>::operator->() const {
  return s_singleton;
}

template<class Ty>
Ty &SingletonUse<Ty>::operator* () const {
  return instance();
}

#endif // CLUSTER_SINGLETON_USE_HH 
