// smartp.tcc - Copyright 2005, Matteo Settenvini
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#ifndef SMARTP_TCC
#define SMARTP_TCC 1

#include <sgpemv2/templates/smartp.hh>

namespace memory
{

  template<typename T, bool isArray>
  const smart_ptr<T, isArray> smart_ptr<T, isArray>::null = 0;

  // ------------------------------

  template<typename T, bool isArray>
  smart_ptr<T, isArray>::smart_ptr( T* ptr ) throw(std::bad_alloc)
      : _contents(new contents_type())
  {
    _contents->rc  = 1;
    _contents->ptr = ptr;
  }


  template<typename T, bool isArray>
  smart_ptr<T, isArray>::smart_ptr(const smart_ptr& sptr) throw()
      : _contents(sptr._contents)
  {
    (_contents->rc)++;
  }


  template<typename T, bool isArray>
  smart_ptr<T, isArray>::~smart_ptr() throw()
  {
    if (--(_contents->rc) == 0)
    {
      if (_contents->ptr != 0)
        !isArray ? delete _contents->ptr : delete [] _contents->ptr;
      delete _contents;
    }
  }

  // ------------------------------

  template<typename T, bool isArray>
  smart_ptr<T, isArray>&
  smart_ptr<T, isArray>::operator=(const smart_ptr& sptr) throw()
  {
    if (this != &sptr && _contents != sptr._contents)
    {
      if (--(_contents->rc) == 0)
      {
        if (_contents->ptr != 0)
          !isArray ? delete _contents->ptr : delete [] _contents->ptr;
        delete _contents;
      }

      _contents = sptr._contents;
      (_contents->rc)++;
    }

    return *this;
  }


  template<typename T, bool isArray>
  bool
  smart_ptr<T, isArray>::operator==( const smart_ptr& sptr ) const throw()
  {
    return _contents->ptr == sptr._contents->ptr;
  }


  template<typename T, bool isArray>
  bool
  smart_ptr<T, isArray>::operator!=( const smart_ptr& sptr ) const throw()
  {
    return _contents->ptr != sptr._contents->ptr;
  }


  template<typename T, bool isArray>
  T&
  smart_ptr<T, isArray>::operator*() throw()
  {
    return *(_contents->ptr);
  }


  template<typename T, bool isArray>
  T*
  smart_ptr<T, isArray>::operator->() throw()
  {
    return _contents->ptr;
  }


  template<typename T, bool isArray>
  const T&
  smart_ptr<T, isArray>::operator*() const throw()
  {
    return *(_contents->ptr);
  }


  template<typename T, bool isArray>
  const T*
  smart_ptr<T, isArray>::operator->() const throw()
  {
    return _contents->ptr;
  }


  template<typename T, bool isArray>
  smart_ptr<T, isArray>::operator bool() const throw()
  {
    return _contents->ptr != 0;
  }

  // ------------------------------

  template<typename T, bool isArray>
  unsigned int
  smart_ptr<T, isArray>::alive_refs() const throw()
  {
    return _contents->ptr != 0 ? _contents->rc : 0;
  }


  template<typename T, bool isArray>
  template<typename U>
  smart_ptr<U, isArray>
  smart_ptr<T, isArray>::cast_to() throw(std::bad_cast)
  {
    return smart_ptr<U, isArray>(*this);
  }


  template<typename T, bool isArray>
  template<typename U>
  const smart_ptr<U, isArray>
  smart_ptr<T, isArray>::cast_to() const throw(std::bad_cast)
  {
    return smart_ptr<U, isArray>(*this);
  }


  template<typename T, bool isArray>
  template<typename U>
  smart_ptr<T, isArray>::smart_ptr(const smart_ptr<U, isArray>& sptr)
  throw(std::bad_cast)
  {
    if (!sptr._contents->ptr || dynamic_cast<T*>(sptr._contents->ptr) == 0)
      throw std::bad_cast();

    // I know, I know... this is Evil(TM):
    _contents = reinterpret_cast<typename smart_ptr<T, isArray>::contents_type* > (sptr._contents);
    (_contents->rc)++;
  }

} //~ namespace memory

#endif
