#ifndef BLUECORE_REFERENCED_COUNTER_H
#define BLUECORE_REFERENCED_COUNTER_H

#include <list>

namespace BlueCore
{

class Referenced
{
public:

	class DestructionListener
	{
	public:
		virtual void onDestruction(Referenced *referenced) = 0;
	};

private:

	std::list<DestructionListener*> _DestructionListeners;

	unsigned int _ReferenceCount;

	virtual void destroyReferenced()
	{
		while (_DestructionListeners.begin() != _DestructionListeners.end())
		{
			if (_DestructionListeners.front() != 0)
				_DestructionListeners.front()->onDestruction(this);
			_DestructionListeners.pop_front();
		}

		delete this;
	}

public:

	inline Referenced() :
		_ReferenceCount(0)
	{
	}

	virtual inline ~Referenced()
	{
	}

	inline void addReference()
	{
		_ReferenceCount += 1;
	}

	inline void removeReference()
	{
		if (_ReferenceCount > 0)
		{
			_ReferenceCount -= 1;

			if ( 0 == _ReferenceCount)
			{
				destroyReferenced();
			}
		}
	}

	inline unsigned int getReferenceCount()
	{
		return _ReferenceCount;
	}

	inline void addDestructionListener(DestructionListener *listener)
	{
		if (listener != 0)
			_DestructionListeners.push_back(listener);
	}

	inline void removeDestructionListener(DestructionListener *listener)
	{
		_DestructionListeners.remove(listener);
	}
};

template<class T> class ref_ptr
{
public:
	ref_ptr() :
		_Referenced(0)
	{
	}

	ref_ptr(T* referenced) :
		_Referenced(referenced)
	{
		if (_Referenced)
			_Referenced->addReference();
	}

	ref_ptr(const ref_ptr& op) :
		_Referenced(op._Referenced)
	{
		if (_Referenced)
			_Referenced->addReference();
	}

	~ref_ptr()
	{
		if (_Referenced)
			_Referenced->removeReference();
	}

	T* operator ->() const
	{
		return _Referenced;
	}

	ref_ptr& operator =(T* op)
	{
		if (op != _Referenced)
		{
			if (_Referenced != 0)
				_Referenced->removeReference();

			_Referenced = op;

			if (_Referenced)
				_Referenced->addReference();
		}

		return *this;
	}

	bool operator ==(const ref_ptr& ref)
	{
		if (_Referenced == ref._Referenced)
			return true;
		else
			return false;
	}

	bool valid() const
	{
		return (_Referenced != 0);
	}

	operator T*() const
	{
		return _Referenced;
	}
	
	T* get() const
	{
		return _Referenced;
	}

private:

	T* _Referenced;
};

template<class T> class weak_ptr : public Referenced::DestructionListener
{
public:
	weak_ptr() :
		_Referenced(0)
	{
	}

	weak_ptr(T* referenced) :
		_Referenced(referenced)
	{
		if (_Referenced)
			_Referenced->addDestructionListener(this);
	}

	weak_ptr(const weak_ptr& op) :
		_Referenced(op._Referenced)
	{
		if (_Referenced)
			_Referenced->addDestructionListener(this);
	}

	virtual ~weak_ptr()
	{
		if (_Referenced)
			_Referenced->removeDestructionListener(this);
	}

	T* operator ->() const
	{
		return _Referenced;
	}

	weak_ptr& operator =(T* op)
	{
		if (op != _Referenced && _Referenced != 0)
			_Referenced->removeDestructionListener(this);

		_Referenced = op;
		_Referenced->addDestructionListener(this);

		return *this;
	}

	bool operator ==(const weak_ptr& ref)
	{
		if (_Referenced == ref._Referenced)
			return true;
		else
			return false;
	}

	bool valid() const
	{
		return (_Referenced != 0);
	}

	
	T* get() const
	{
		return _Referenced;
	}

	
	void onDestruction(Referenced *referenced)
	{
		_Referenced = 0;
	}

private:

	T* _Referenced;
};

} // namespace BlueCore

#endif
