#ifndef _CREFPTR_H_
#define _CREFPTR_H_

#include <config.h>
#if FF_DEBUG
#include <map>
#endif

template <typename T>
class CRefPtr
{
	//template <typename Y> friend class CRefPtr;
	
public:
	typedef T element_type;
	typedef T value_type;
	
	
private:
	unsigned int* m_pn;
	T* m_px;

#if FF_DEBUG
	static std::map <const void*, bool> m_map;
	std::map <const void*, bool>::iterator m_it;
#endif


public:
	CRefPtr()
		: m_pn(NULL)
		, m_px(NULL)
	{
	}
	
	CRefPtr(const CRefPtr& other)
		: m_pn(NULL)
		, m_px(NULL)
	{
		init(other);
	}
	
	template <typename Y>
	CRefPtr(const CRefPtr <Y>& other)
		: m_pn(NULL)
		, m_px(NULL)
	{
		init(other);
	}
	
	CRefPtr(T* px)
		: m_pn(NULL)
		, m_px(NULL)
	{
		init(px);
	}
	
	template <typename Y>
	CRefPtr(Y* px)
		: m_pn(NULL)
		, m_px(NULL)
	{
		init(px);
	}
	
	~CRefPtr()
	{
		release();
	}
	

protected:
	void init(const CRefPtr& other)
	{
		if (m_px != other.m_px)
		{
			release();
			m_pn = other.m_pn;
			m_px = other.m_px;
#if FF_DEBUG
			m_it = other.m_it;
#endif
			add_ref();
		}
	}
	
	template <typename Y>
	void init(const CRefPtr <Y>& other)
	{
		if (m_px != other.m_px)
		{
			release();
			m_pn = other.m_pn;
			m_px = other.m_px;
			if (FF_DEBUG)
				this->m_it = other.m_it;
			add_ref();
		}
	}

	void init(T* px)
	{
		if (m_px != px)
		{
#if FF_DEBUG
			std::pair <std::map <const void*, bool>::iterator, bool> result = m_map.insert(std::pair <const void*, bool> (px, true));
			if (result.second)
				m_it = result.first;
			else
				throw -1;
#endif
			release();
			m_pn = new unsigned int(0);
			m_px = px;
			add_ref();
		}
	}
	
	template <typename Y>
	void init(Y* px)
	{
		if (m_px != px)
		{
#if FF_DEBUG
			std::pair <std::map <const void*, bool>::iterator, bool> result = m_map.insert(std::pair <const void*, bool> (px, true));
			if (result.second)
				m_it = result.first;
			else
				throw -1;
#endif
			release();
			m_pn = new unsigned int(0);
			m_px = px;
			add_ref();
		}
	}
	
	void add_ref()
	{
		if (m_px)
			++*m_pn;
	}
	
	void release()
	{
		if (m_px && !--*m_pn)
		{
#if FF_DEBUG
			m_map.erase(m_it);
#endif
			delete m_px;
			delete m_pn;
			m_px = NULL; // important! the next release() relies on it
			m_pn = NULL;
		}
	}
	
public:
	CRefPtr& operator =(const CRefPtr& other)
	{
		init(other);
		return *this;
	}
	
	template <typename Y>
	CRefPtr& operator =(const CRefPtr <Y>& other)
	{
		init(other);
		return *this;
	}
	
	CRefPtr& operator =(T* px)
	{
		init(px);
		return *this;
	}
	
	template <typename Y>
	CRefPtr& operator =(Y* px)
	{
		init(px);
		return *this;
	}
	
	T* operator ->()
	{
		return m_px;
	}
	
	const T* operator ->() const
	{
		return m_px;
	}
	
/*	T& operator *()
	{
		return *m_px;
	}*/
	
	const T& operator *() const
	{
		return *m_px;
	}
	
/*	T* operator &() const
	{
		return &*m_px;
	}*/
	
	void operator =(const T& rx)
	{
		*m_px = rx;
	}
	
	bool operator !()
	{
		return m_px == NULL;
	}

	operator bool()
	{
		return m_px != NULL;
	}
	
	bool operator ==(const CRefPtr& other)
	{
		return m_px == other.m_px;
	}
	
	template <typename Y>
	bool operator ==(const CRefPtr <Y>& other)
	{
		return m_px == other.m_px;
	}
	
	bool operator !=(const CRefPtr& other)
	{
		return m_px != other.m_px;
	}
	
	template <typename Y>
	bool operator !=(const CRefPtr <Y>& other)
	{
		return m_px != other.m_px;
	}
};

#if FF_DEBUG
template <typename T>
std::map <const void*, bool> CRefPtr <T>::m_map;
#endif

#endif
