/*
   Copyright (C) 2004 by James Gregory
   Part of the GalaxyHack project
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.
 
   See the COPYING file for more details.
*/

#ifndef GUARD_GenericHandle
#define GUARD_GenericHandle

#include "Inlines.h"

#include <cstddef>
#include <stdexcept>

template <class T> class GenericHandle {
public:
	GenericHandle(): object(0), ref(new size_t(1)) {}
	GenericHandle(T* initObject): object(initObject), ref(new size_t(1)) {}
	GenericHandle(const GenericHandle& cGH);
	~GenericHandle();

	GenericHandle& operator=(const GenericHandle& cGH);

	operator bool() const {return object;}
	T& operator*() const;
	T* operator->() const;

	void MakeUnique();

	void clear();
	T* GetPointer();

private:
	T* object;
	std::size_t* ref;
};

template <class T>
GenericHandle<T>::GenericHandle(const GenericHandle& cGH):
object(cGH.object), ref(cGH.ref) {
	++*ref;
}

template <class T>
GenericHandle<T>::~GenericHandle() {
	if (--*ref == 0) {
		delete ref;
		delete object;
	}
}

template <class T>
GenericHandle<T>& GenericHandle<T>::operator=(const GenericHandle& cGH) {
	++*cGH.ref;

	if (--*ref == 0) {
		delete ref;
		delete object;
	}

	ref = cGH.ref;
	object = cGH.object;
	return *this;
}

template <class T>
T& GenericHandle<T>::operator*() const {
	if (object)
		return *object;
	throw std::runtime_error("Attempt to dereference unbound handle");
}

template <class T>
T* GenericHandle<T>::operator->() const {
	if (object)
		return object;
	throw std::runtime_error("Attempt to -> unbound handle");
}

template <class T>
void GenericHandle<T>::MakeUnique() {
	if (*ref != 1) {
		--*ref;
		ref = new size_t(1);
		object = object ? object->Clone() : 0;
	}
}

template <class T>
void GenericHandle<T>::clear() {
	if (--*ref == 0) {
		SafeDelete(object);
		*ref = 1;
	}
}

template <class T>
T* GenericHandle<T>::GetPointer() {
	return object;
}

#endif


