/**
 * \file BGDNetShare.h
 * \brief Объявление класса Share и шаблона SharePtr
 */

#ifndef __BGDNetShare_h__
#define __BGDNetShare_h__

#include "BGDNetConfig.h"

#include <assert.h>

namespace BGDNet
{

/**
 * \class Share
 * \brief Класс "Разделяемый ресурс"
 *
 * Базовый класс для различных разделяемых ресурсов. Ведет
 * счетчик ссылок, объект удаляется при достижении счетчиком
 * ссылок нулевого значения.
 */
class BGDNET_API Share
{
public:

  Share( void );
  /**<
   * \brief Конструктор
   * Устанавливает счетчик ссылок равным 1
   */
  
  Share( const Share& );
  /**<
   * \brief Инициализатор копии
   * Устанавливает счетчик ссылок равным 1
   */

  Share& operator =( const Share& );
  /**<
   * \brief Перегруженный оператор присваивания
   * Не допускает копирования значения счетчика ссылок,
   * которое имело бы место по умолчанию.
   */

  void addRef( void );
  /**<
   * \brief Увеличивает счетчик ссылок
   */
  
  void release( void );
  /**<
   * \brief Уменьшает счетчик ссылок и удаляет объект,
   * если счетчик достиг нуля.
   */

  bool shared( void ) const;
  /**<
   * \brief Определяет, разделяется ли объект несколькими владельцами
   * @return true, если счетчик ссылок равен 1; false, если счетчик
   * ссылок больше единицы.
   */

protected:

  virtual ~Share( void ) {};
  /**<
   * \brief Деструктор
   */

private:

  unsigned long m_refCnt;
  /**<
   * \brief Счетчик cсылок
   */
};

inline
Share::Share( void ) : m_refCnt(1)
{
}

inline
Share::Share( const Share& ) : m_refCnt(1)
{
}

inline
Share& Share::operator =( const Share& )
{
  return *this;
}

inline
void Share::addRef( void )
{
  ++m_refCnt;
}

inline
void Share::release( void )
{
  if (!--m_refCnt)
    delete this;
}

inline
bool Share::shared( void ) const
{
  return m_refCnt > 0;
}




/**
 * \class SharePtr
 * \brief Шаблон "Указатель на разделяемый ресурс"
 *
 * Интеллектуальный указатель, автоматически увеличивающий и уменьшающий
 * счетчик ссылок ресурса по мере необходимости.
 */
template <class S>
class BGDNET_API SharePtr
{
public:

  SharePtr( S* p = 0 );
  /**<
   * \brief Конструктор
   * Если конструктору передается указатель на разделяемый ресурс,
   * счетчик ссылок этого ресурса конструктором НЕ меняется.
   * @param p Необязательный указатель на разделяемый ресурс
   */

  SharePtr( const SharePtr& p );
  /**<
   * \brief Инициализатор копии
   */

  ~SharePtr( void );
  /**<
   * \brief Деструктор
   * Автоматически уменьшает счетчик ссылок разделяемого объекта
   */

  SharePtr& operator =( S* p );
  /**<
   * \brief Оператор присваивания
   */

  SharePtr& operator =( const SharePtr& p );
  /**<
   * \brief Оператор присваивания
   */

  const S* operator ->( void ) const;
  /**<
   * \brief Перегруженный оператор обращения по указателю
   */

  S* operator ->( void );
  /**<
   * \brief Перегруженный оператор обращения по указателю
   */

  const S& operator *( void ) const;
  /**<
   * \brief Перегруженный оператор разыменования указателя
   */

  S& operator *( void );
  /**<
   * \brief Перегруженный оператор разыменования указателя
   */

  operator const S*( void ) const;
  /**<
   * \brief Оператор преобразования к типу реального указателя
   */

  operator S*( void );
  /**<
   * \brief Оператор преобразования к типу реального указателя
   */

private:

  S* m_pShare;
  /**<
   * \brief Реальный указатель на разделяемый ресурс
   */
};

template <class S> inline
SharePtr<S>::SharePtr( S* p ) : m_pShare(p)
{
}

template <class S> inline
SharePtr<S>::SharePtr( const SharePtr& p ) : m_pShare(p.m_pShare)
{
  if (m_pShare)
    m_pShare->addRef();
}

template <class S> inline
SharePtr<S>::~SharePtr( void )
{
  if (m_pShare)
    m_pShare->release();
}

template <class S>
SharePtr<S>& SharePtr<S>::operator =( S* p )
{
  if (m_pShare != p) // не допускаем присваивания самому себе!
  {
    if (m_pShare)
      m_pShare->release();
    m_pShare = p;
  }
  return *this;
}

template <class S>
SharePtr<S>& SharePtr<S>::operator =( const SharePtr& p )
{
  if (this != &p)
  {
    // "Отпустить" текущий ресурс:
    if (m_pShare)
      m_pShare->release();
    
    // Привязаться к новому ресурсу:
    m_pShare = p.m_pShare;

    // Увеличить счетчик ссылок на новый ресурс:
    if (m_pShare)
      m_pShare->addRef();
  }
  return *this;
}

template <class S> inline
const S* SharePtr<S>::operator ->( void ) const
{
  return m_pShare;
}

template <class S> inline
S* SharePtr<S>::operator ->( void )
{
  return m_pShare;
}

template <class S> inline
const S& SharePtr<S>::operator *( void ) const
{
  assert(m_pShare);
  return *m_pShare;
}

template <class S> inline
S& SharePtr<S>::operator *( void )
{
  assert(m_pShare);
  return *m_pShare;
}

template <class S> inline
SharePtr<S>::operator const S*( void ) const
{
  return m_pShare;
}

template <class S> inline
SharePtr<S>::operator S*( void )
{
  return m_pShare;
}

} // namespace BGDNet

#endif //__BGDNetShare_h__
