/*
 *  Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
 *
 * 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, or (at your option) any later version of the License.
 *
 * 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#ifndef _CAUCHY_SHAREDPOINTER_P_H_
#define _CAUCHY_SHAREDPOINTER_P_H_

#include "StdTypes.h"

#define CAUCHY_SHARED_DATA_CLASS(_NAME_, _CLASS_NAME_) \
  _NAME_::_CLASS_NAME_(const _NAME_& str) : d(str.d) \
  { \
    d->ref(); \
  } \
  _NAME_& _NAME_::operator=(const _NAME_& _rhs) \
  { \
    _rhs.d->ref(); \
    if(d->deref() == 0) \
    { \
      delete d; \
    } else { \
      CAUCHY_ASSERT(d->count() > 0); \
    } \
    d = _rhs.d; \
    return *this; \
  } \
  _NAME_::~_CLASS_NAME_() \
  { \
    if(d->deref() == 0) \
    { \
      delete d; \
    } else { \
      CAUCHY_ASSERT(d->count() > 0); \
    } \
  } \
  void _NAME_::deref() \
  { \
    if(d->count() == 1 ) return; \
    CAUCHY_ASSERT(d->count() > 1); \
    d->deref(); \
    d = new Private(*d); \
    d->ref(); \
  }

#define CAUCHY_SHARED_DATA(_NAME_) \
  CAUCHY_SHARED_DATA_CLASS(_NAME_, _NAME_ )

namespace Cauchy {
  class SharedPointerData {
      SharedPointerData(const SharedPointerData&);
      const SharedPointerData& operator=(const SharedPointerData&);
    protected:
      SharedPointerData();
      ~SharedPointerData();
    public:
      void ref();
      int deref();
      int count() const;
    private:
#ifndef _MSC_VER
	  cauchy_uint32 m_count;
#else
	  unsigned int m_count;
#endif
  };
  template<typename T>
  class SharedPointer {
    public:
      SharedPointer(T* _t) : t(_t)
      {
        ref();
      }
      SharedPointer<T>& operator=(const SharedPointer<T>& _rhs)
      {
        deref();
        t = _rhs.t;
        ref();
        return *this;
      }
      SharedPointer( const SharedPointer<T>& _rhs ) : t(_rhs.t) {
        ref();
      }
      ~SharedPointer()
      {
        deref();
      }
      const T* operator->() const {
        return t;
      }
      T* operator->() {
        return t;
      }
      const T& operator*() const {
        return *t;
      }
      T& operator*() {
        return *t;
      }
      template<typename T2>
      SharedPointer<T2> scast() {
        return SharedPointer<T2>( static_cast<T2*>(t) );
      }
      template<typename T2>
      SharedPointer<T2> scast() const {
        return SharedPointer<T2>( static_cast<T2*>(t) );
      }
      template<typename T2>
      SharedPointer<T2> dcast() {
        return SharedPointer<T2>( dynamic_cast<T2*>(t) );
      }
      template<typename T2>
      SharedPointer<T2> dcast() const {
        return SharedPointer<T2>( dynamic_cast<T2*>(t) );
      }
      bool isNull() const {
        return t == 0;
      }
    private:
      void ref()
      {
        if(t)
        {
          t->ref();
        }
      }
      void deref()
      {
        if(t && t->deref() == 0)
        {
          delete t;
        }
        t = 0;
      }
      T* t;
  };
};

#endif
