/* Lecture code 14.0
 *
 * SharedPointer implementation from class.  Comments have been added to the requisite sections.
 */

#include <iostream>
#include <cstdlib> // For NULL
using namespace std;

/* A reference-counted shared pointer class that allows several pointers
 * to each access the same resource.  When the final pointer is deallocated,
 * the resource is cleaned up.
 */
template <typename ElemType> class SharedPointer
{
public:
	/* Default constructor creates a NULL SmartPointer. */
	SharedPointer();

	/* Primary constructor wraps the resource, which is expected to come
	 * from a call to new.
	 */
	explicit SharedPointer(ElemType* elem);

	/* Copy ctor, assignment operator, and destructor properly manage the
	 * resources.
	 */
	SharedPointer(const SharedPointer &other);
	SharedPointer& operator = (const SharedPointer& other);
	~SharedPointer();

	/* Arrow and star operator allow us to use the pointer like a regular
	 * C++ pointer.  As an exercise, consider adding == and the other relational
	 * operators.
	 */
	ElemType& operator *() const { return *ptr; }
	ElemType* operator ->() const { return ptr;}

	/* Function to return the raw stored pointer. */
	ElemType* get() const { return ptr; }

	/* Helper function to return the number of pointers sharing this resource. */
	int getSharedCount() const { return *referenceCount; }

	/* This function was not covered in class, but is a useful addition.
	 * reset causes the pointer to stop referring to its old resource and to
	 * instead use the new resource, deallocating memory as necessary.
	 */
	void reset(ElemType* newElem);
private:
	ElemType* ptr;       // The actual resource
	int *referenceCount; // The number of pointers referencing it.

	/* Utility functions to change where the pointer is pointing. */
	void setupStructure(ElemType* resource);
	void detach();
	void attachTo(const SharedPointer &other);
};

/* Initializes the pointer such that it points to the specified resource and has
 * a nonzero reference count.
 */
template <typename ElemType>
void SharedPointer<ElemType>::setupStructure(ElemType *resource)
{
	ptr = resource;
	referenceCount = new int(1);
}

/* Basic constructor just creates a SharedPointer referencing the specified
 * resource.
 */
template <typename ElemType>
SharedPointer<ElemType>::SharedPointer(ElemType *resource)
{
	setupStructure(resource);
}

/* attachTo attaches the pointer to the specified resource and updates the reference
 * count.
 */
template <typename ElemType>
void SharedPointer<ElemType>::attachTo(const SharedPointer &other)
{
	ptr = other.ptr;
	referenceCount = other.referenceCount;
	++(*referenceCount);
}

/* Copy constructor causes this pointer to tag onto the second pointer. */
template <typename ElemType>
SharedPointer<ElemType>::SharedPointer(const SharedPointer &other)
{
	attachTo(other);
}

/* detach drops the reference count and cleans up the specified resource if the
 * count drops to zero.
 */
template <typename ElemType> void SharedPointer<ElemType>::detach()
{
	--(*referenceCount);
	if(*referenceCount == 0)
	{
		delete referenceCount;
		delete ptr;
	}
}

/* Destructor just calls detach. */
template <typename ElemType> SharedPointer<ElemType>::~SharedPointer()
{
	detach();
}

/* Assignment operator attaches the pointer to the specified resource after
 * first dropping existing resource.
 */
template <typename ElemType>
SharedPointer<ElemType>& SharedPointer<ElemType>::operator =(const SharedPointer& other)
{
	if(this != &other) // Don't forget self-assignment!
	{
		detach();
		attachTo(other);
	}
	return *this;
}

/* Default constructor has the pointer manage an empty resource. */
template <typename ElemType> SharedPointer<ElemType>::SharedPointer()
{
	setupStructure(NULL);
}

/* Reset detaches the pointer and makes it point to a new resource. */
template <typename ElemType> void SharedPointer<ElemType>::reset(ElemType* newElem)
{
	detach();
	setupStructure(newElem);
}
