#ifndef __MEM__MEM__H__
#define __MEM__MEM__H__

#include <stddef.h> // size_t

namespace mem {

// class to handle the object and ptr_ref counter (holder and observer).
//
// note: this class is not thread safe!
template <typename T>
class ptr_ref
{
		template <typename U> friend class shared_ptr;
		template <typename U> friend class weak_ptr;
	private:
		// pointer to the actual object
		T * ptr;

		// ptr_ref counter.
		size_t n;

		// number of observers (including ptr_ref holders).
		size_t m;
	private:
		// initializes the ptr_ref.
		explicit ptr_ref(T * ptr)
			: ptr(ptr), n(1), m(1)
		{}

		// destroys the object.
		~ptr_ref()
		{
			if (ptr) {
				delete ptr;
				ptr = 0;
			}
			n = 0;
			m = 0;
		}

		// decreases the number of holders. the object gets destroyed
		// if no more holders are present.
		inline void dec_shared(void)
		{
			if (n > 0) --n;
			if (m > 0) --m;
			destroy();
		}

		// decreases the number of observers.
		inline void dec_observer(void)
		{
			if (m > 0) --m;
		}

		// increases the holder and observer coutners.
		//
		// note: upper boundary of size_t is not checked!
		inline void inc_shared(void)
		{
			++n;
			++m;
		}

		// increases the observer counter.
		//
		// note: upper boundary of size_t is not checked!
		inline void inc_observer(void)
		{
			++m;
		}

		// destroys the object if there are no more holders, not
		// regarding the observers.
		inline void destroy(void)
		{
			if (n == 0) {
				if (ptr) delete ptr;
				ptr = 0;
			}
		}

		// returns the number of holders.
		inline size_t count(void) const
		{
			return n;
		}

		// returns the validity of the object. this is useful
		// for weak pointers to check for the validity.
		inline bool valid(void) const
		{
			return ptr != 0;
		}

		// returns true if the ptr_ref is to be destroyed.
		inline bool to_destroy(void) const
		{
			return ((n == 0) && (m == 0));
		}
};

// light-weight ptr_ref counting shared pointer class.
//
// note: this class is not thread safe!
// note: upper limits of size_t are not checked, there will be an error
//       if you allocate too many objects (see ptr_ref::n and ptr_ref::m).
template <typename T>
class shared_ptr
{
		template <typename U> friend class weak_ptr;
		template <typename U> friend class shared_ptr;
	private:
		// the ptr_ref
		ptr_ref<T> * ref;
	private:
		// decreases the ptr_ref counter and destroys the object
		// if the counter reaches zero.
		inline void decrease(void)
		{
			if (!ref) return;
			ref->dec_shared();
			if (ref->to_destroy()) delete ref;
			ref = 0;
		}

		// assigns the specified pointer (other) to this one.
		// the ptr_ref counter is increased.
		void assign(ptr_ref<T> * ref)
		{
			this->ref = ref;
			if (this->ref) this->ref->inc_shared();
		}
	public:
		// constructor to initialize the shared pointer. a ptr_ref
		// is created only if the specified pointer is not NULL.
		//
		// note: do not share object between shared pointer class and
		//       free floating pointer.
		// note: do not specifiy a pointer from a static object.
		//       example:
		//         void foo(void)
		//         {
		//           ClassA object;
		//           shared_ptr<ClassA> ptr(&object); // do not do this!
		//         }
		explicit shared_ptr(T * ptr = 0) throw()
			: ref(0)
		{
			if (ptr) ref = new ptr_ref<T>(ptr);
		}

		/// constructor to construct a shared pointer from a ptr_ref.
		explicit shared_ptr(ptr_ref<T> * ref) throw()
			: ref(0)
		{
			assign(ref);
		}

		// copy constructor to copy another shared pointer
		// (of the same type).
		shared_ptr(const shared_ptr<T> & other) throw()
			: ref(0)
		{
			assign(other.ref);
		}

		// destructor. if the last ptr_ref dies the object
		// will be destroyed.
		~shared_ptr() throw()
		{
			decrease();
		}

		// returns the number of references.
		inline size_t ref_count(void) const
		{
			if (ref) return ref->count();
			return 0;
		}

		// resets the shared pointer instance. if the last ptr_ref dies,
		// the object will be destroyed.
		inline void reset(void) throw()
		{
			decrease();
		}

		// operator to access the object.
		inline T * operator->(void) throw()
		{
			if (ref) return ref->ptr;
			return 0;
		}

		// operator to access the object (constant version).
		inline const T * operator->(void) const throw()
		{
			if (ref) return ref->ptr;
			return 0;
		}

		// assignment operator to assign a shared pointer to
		// another of the same type.
		inline shared_ptr<T> & operator=(const shared_ptr<T> & other) throw()
		{
			reset();
			assign(other.ref);
			return *this;
		}

		// returns the object without affecting the ptr_ref counter.
		inline T * get(void) throw()
		{
			if (ref) return ref->ptr;
			return 0;
		}

		// returns the object without affecting the ptr_ref
		// counter (constant version).
		inline const T * get(void) const throw()
		{
			if (ref) return ref->ptr;
			return 0;
		}

		// returns a ptr_ref of the object. do not call this
		// operator if the object is not valid.
		inline T & operator*(void) throw()
		{
			return *ref->ptr;
		}

		// returns a ptr_ref of the object. do not call this
		// operator if the object is not valid (constant version).
		inline const T & operator*(void) const throw()
		{
			return *ref->ptr;
		}

		// return true if the object is valid, otherwise the
		// method returns false.
		inline operator bool(void) const
		{
			return ref;
		}

		// tests the equality of the two pointers.
		inline bool operator==(const shared_ptr<T> & other) const
		{
			return ref == other.ref;
		}

		// tests the equality of the two pointers.
		template <typename U>
		inline bool operator==(const shared_ptr<U> & other) const
		{
			return (void *)ref == (void *)other.ref;
		}

		// tests the equality of the two pointers.
		inline bool operator!=(const shared_ptr<T> & other) const
		{
			return ref != other.ref;
		}

		// tests the equality of the two pointers.
		template <typename U>
		inline bool operator!=(const shared_ptr<U> & other) const
		{
			return (void *)ref != (void *)other.ref;
		}
};

// light-weight ptr_ref counting weak pointer class.
//
// note: this class is not thread safe!
// note: upper limits of size_t are not checked, there will be an error
//       if you allocate too many objects (see ptr_ref::n and ptr_ref::m).
template <typename T>
class weak_ptr
{
		template <typename U> friend class weak_ptr;
	private:
		// the ptr_ref
		ptr_ref<T> * ref;
	private:
		// decreases the number of observers. it destroys the object
		// if the no other holds the object.
		inline void decrease(void)
		{
			if (!ref) return;
			ref->dec_observer();
			if (ref->to_destroy()) delete ref;
			ref = 0;
		}

		// assigns the specified pointer (other) to this one.
		// the observer counter is increased.
		inline void assign(ptr_ref<T> * ref)
		{
			this->ref = ref;
			if (this->ref) this->ref->inc_observer();
		}

		template <typename U>
		inline bool same_ref_as(const shared_ptr<U> & a) const
		{
			return (void *)ref == (void *)a.ref;
		}
	public:
		// constructor to create a weak pointer out of a shared pointer.
		template <typename U>
		weak_ptr(const shared_ptr<U> & other) throw()
			: ref(0)
		{
			assign(other.ref);
		}

		// copy constructor for weak pointers.
		weak_ptr(const weak_ptr & other) throw()
			: ref(0)
		{
			assign(other.ref);
		}

		// destructor.
		~weak_ptr() throw()
		{
			decrease();
		}

		// returns a shared pointer out of this weak pointer. the
		// ptr_ref counter is increased acordingly.
		// if the object is not valid, a null pointer is returned.
		shared_ptr<T> lock(void)
		{
			if (ref && ref->valid()) return shared_ptr<T>(ref);
			return shared_ptr<T>();
		}

		// resets the shared pointer instance. if the last ptr_ref dies,
		// the object will be destroyed.
		inline void reset(void) throw()
		{
			decrease();
		}

		// operator to access the object.
		inline T * operator->(void) throw()
		{
			if (ref) return ref->ptr;
			return 0;
		}

		// operator to access the object (constant version).
		inline const T * operator->(void) const throw()
		{
			if (ref) return ref->ptr;
			return 0;
		}

		// assignment operator to assign a shared pointer to
		// another of the same type.
		inline weak_ptr<T> & operator=(const weak_ptr<T> & other) throw()
		{
			reset();
			assign(other.ref);
			return *this;
		}

		// returns the object without affecting the ptr_ref counter.
		inline T * get(void) throw()
		{
			if (ref) return ref->ptr;
			return 0;
		}

		// returns the object without affecting the ptr_ref
		// counter (constant version).
		inline const T * get(void) const throw()
		{
			if (ref) return ref->ptr;
			return 0;
		}

		// returns a ptr_ref of the object. do not call this
		// operator if the object is not valid.
		inline T & operator*(void) throw()
		{
			return *ref->ptr;
		}

		// returns a ptr_ref of the object. do not call this
		// operator if the object is not valid (constant version).
		inline const T & operator*(void) const throw()
		{
			return *ref->ptr;
		}

		// return true if the object is valid, otherwise the
		// method returns false.
		inline operator bool(void) const
		{
			return ref;
		}

		template <typename S, typename U>
		friend bool operator==(const weak_ptr<S> &, const shared_ptr<U> &);

		template <typename S, typename U>
		friend bool operator==(const shared_ptr<S> &, const weak_ptr<U> &);

		template <typename S, typename U>
		friend bool operator!=(const weak_ptr<S> &, const shared_ptr<U> &);

		template <typename S, typename U>
		friend bool operator!=(const shared_ptr<S> &, const weak_ptr<U> &);

		// tests the equality of the two pointers.
		inline bool operator==(const weak_ptr<T> & other) const
		{
			return ref == other.ref;
		}

		// tests the equality of the two pointers.
		template <typename U>
		inline bool operator==(const weak_ptr<U> & other) const
		{
			return (void *)ref == (void *)other.ref;
		}

		// tests the equality of the two pointers.
		inline bool operator!=(const weak_ptr<T> & other) const
		{
			return ref != other.ref;
		}

		// tests the equality of the two pointers.
		template <typename U>
		inline bool operator!=(const weak_ptr<U> & other) const
		{
			return (void *)ref != (void *)other.ref;
		}
};

template <typename S, typename U>
bool operator==(const weak_ptr<S> & a, const shared_ptr<U> & b)
{
	return a.same_ref_as(b);
}

template <typename S, typename U>
bool operator==(const shared_ptr<S> & a, const weak_ptr<U> & b)
{
	return b.same_ref_as(a);
}

template <typename S, typename U>
bool operator!=(const weak_ptr<S> & a, const shared_ptr<U> & b)
{
	return !a.same_ref_as(b);
}

template <typename S, typename U>
bool operator!=(const shared_ptr<S> & a, const weak_ptr<U> & b)
{
	return !b.same_ref_as(a);
}

}

#endif
