
#ifndef AT_OBJPTR_H
#define AT_OBJPTR_H

#include "at_objptrconst.h"

// ================================== PTRBASE =================

template <class T> inline
PtrBase<T>::PtrBase
  (Obj<T>& o) :
    PtrConst<T>(o)
{
}

template <class T> inline
PtrBase<T>::PtrBase
  () :
    PtrConst<T>()
{
}

template <class T> inline
PtrBase<T>& PtrBase<T>::operator=
  (const ConstObj<T>& rhs)
{
  if (rb)
  {
    T *t = (T*) rb->unlock();
    if (t)
    {
      t->~T();
      delete rb;
    }
  }

  if (rb = getObjRB(rhs))
    ptr = (T*) rb->lock();
  return *this;
}

template <class T> inline
PtrBase<T>::operator T*
  ()
  const
{
  return ptr;
}

template <class T> inline
T& PtrBase<T>::operator*
  ()
  const
{
  return *ptr;
}

template <class T> inline
T* PtrBase<T>::operator->
  ()
  const
{
  return ptr;
}

//=================================== SPTR, PTR ===============

template <class T> inline
SPtr<T>::SPtr
  (Obj<T>& o) :
    PtrBase<T>(o)
{
  if (rb)
  {
    if (rb->shared())
    {
      // COW: must make new copy
      // make new rb for copy; obj keeps old for now
      rb = new (getObjRB(o)) refBuf();
      // copy object(s)
      const byte *const p_old = (const byte *const) ptr;
      memcpy(ptr = (T*) rb->lock(), p_old, rb->getTSize() * rb->getDim());
      // unlock, unref old rb
      getObjRB(o)->unlock();
      getObjRB(o)->unref();
      // save new rb
      setObjRB(o, rb);
    }
  }
}

template <class T> inline
SPtr<T>::SPtr
  () :
    PtrBase<T>()
{
}

template <class T>
Ptr<T>::Ptr
  (Obj<T>& o) :
    PtrBase<T>(o)
{
  if (rb)
  {
    if (rb->shared())
    {
      PtrConst<T> old_ptr(o);
      // make new rb for clone(s); obj keeps old for now
      rb = new (getObjRB(o)) refBuf();

      // do actual cloning
      const byte *p_old = (byte*) ptr;
      byte *p_new = (byte *) (ptr = (T*) rb->lock());
      for (uint i = 0; i < rb->getDim();
           i++, p_new += rb->getTSize(), p_old += getObjRB(o)->getTSize())   // use actual obj size
      {
        const T* t = (const T*) p_old;
        t->placeClone(p_new);
      }

      // unlock, unref old rb
      getObjRB(o)->unlock();
      getObjRB(o)->unref();
      // save new rb
      setObjRB(o, rb);
    }
  }
}

template <class T> inline
Ptr<T>::Ptr
  () :
    PtrBase<T>()
{
}

#endif
