#ifndef __COUNTED_PTR_H
#define __COUNTED_PTR_H


#include "mutex_lock.h"

template <class X> class CountedPtr
{
public:
    typedef X element_type;

    CountedPtr(X* p = 0) // allocate a new counter
        : itsCounter(0) {if (p) itsCounter = new counter(p);}
    ~CountedPtr()
        {release();}
    CountedPtr(const CountedPtr& r) throw()
        {acquire(r.itsCounter);}
    CountedPtr& operator=(const CountedPtr& r)
    {
        if (this != &r) {
            release();
            acquire(r.itsCounter);
        }
        return *this;
    }

	/**
	Pablo: I want to assign a pointer after creating a null CountedPtr or set it null.
	*/
    CountedPtr& operator=(X* p)
    {
        release();
		if(p) {
			itsCounter = new counter(p);
		}
        return *this;
    }

    X& operator*()  const throw()   {return *itsCounter->ptr;}
    X* operator->() const throw()   {return itsCounter->ptr;}
	bool operator==(int null) const throw()   { return (null == (int) get()); }
	bool operator==(const CountedPtr& r) const throw()   { return (r.itsCounter->ptr == itsCounter->ptr); }
	bool operator==(CountedPtr& r) const throw()   { return (r.itsCounter->ptr == itsCounter->ptr); }
	bool operator!=(int null) const throw()   { return (null != (int) get()); }
	bool operator!=(const CountedPtr& r) const throw()   { return (r.itsCounter->ptr != itsCounter->ptr); }
	bool operator!=(CountedPtr& r) const throw()   { return (r.itsCounter->ptr != itsCounter->ptr); }
	bool operator!() const throw()
	{ return is_null(); }
	
	class bool_tester
	{ void operator delete(void*); };

	operator bool_tester*() const throw()
	{ if (is_null()) return 0; static bool_tester test;	return &test;; }

    X* get()        const throw()   {return itsCounter ? itsCounter->ptr : 0;}
    bool unique()   const throw()
        {return (itsCounter ? itsCounter->count == 1 : true);}
	bool is_null() const { return (get() == NULL); }


private:

    struct counter {
        counter(X* p = 0, unsigned c = 1) : ptr(p), count(c) {}
        X*          ptr;
        unsigned    count;
		MutexLock lock;
    }* itsCounter;

    void acquire(counter* c) throw()
    { // increment the count
        itsCounter = c;
		if(c)
		{
			AutoMutextLock lock(c->lock);
			++c->count;
		}
    }

    unsigned int release()
    { // decrement the count, delete if it is 0
		unsigned int ret = -1;
        if (itsCounter) {
			{
				AutoMutextLock lock(itsCounter->lock);
				ret = --itsCounter->count;

			}
            if (ret == 0) {
				struct counter* itsBkp = itsCounter;
                delete itsBkp->ptr;
                delete itsBkp;
            }
            itsCounter = 0;
        }
		return ret;
    }
};


#endif // __COUNTED_PTR_H
