#ifndef _RSTL_RC_PTR
#define _RSTL_RC_PTR

#include "types.h"
#include "rstl/rmemory_allocator.hpp"

namespace rstl {
class CRefData {
public:
  CRefData(const void* ptr) : x0_ptr(ptr), x4_refCount(1) {}
  CRefData(const void* ptr, int refCount) : x0_ptr(ptr), x4_refCount(refCount) {}
  ~CRefData() {}

  void* GetPtr() const { return const_cast< void* >(x0_ptr); }
  int GetRefCount() const { return x4_refCount; }
  int AddRef() { return ++x4_refCount; }
  int DelRef() { return --x4_refCount; }

  const void* x0_ptr;
  int x4_refCount;

  static CRefData sNull;
};

template < typename T >
class rc_ptr {
public:
  rc_ptr() : x0_refData(&CRefData::sNull) { x0_refData->AddRef(); }
  rc_ptr(const T* ptr) : x0_refData(rs_new CRefData(ptr)) {}
  rc_ptr(const rc_ptr& other) : x0_refData(other.x0_refData) { x0_refData->AddRef(); }
  ~rc_ptr() { ReleaseData(); }
  T* GetPtr() const { return static_cast< T* >(x0_refData->GetPtr()); }
  void ReleaseData();
  T* operator->() const { return GetPtr(); }
  T& operator*() const { return *GetPtr(); }
  operator bool() const { return GetPtr() != nullptr; }

private:
  CRefData* x0_refData;
};

template < typename T >
void rc_ptr< T >::ReleaseData() {
  if (x0_refData->DelRef() <= 0) {
    delete GetPtr();
    delete x0_refData;
  }
}

template < typename T >
class ncrc_ptr : public rc_ptr< T > {
public:
  ncrc_ptr() {}
  ncrc_ptr(T* ptr) : rc_ptr< T >(ptr) {}
  ncrc_ptr(const rc_ptr< T >& other) : rc_ptr< T >(other) {}
};

template < typename T >
bool operator==(const rc_ptr< T >& left, const rc_ptr< T >& right) {
  return left.GetPtr() == right.GetPtr();
}

} // namespace rstl

#endif // _RSTL_RC_PTR
