#ifndef H_CORAL_REF
#define H_CORAL_REF
// This file is part of the Coral project.
// Please read the copyright notice in the LICENSE file.

#include <cstdio>
#include <coral/coral.h>
#include <coral/lib/assert.h>
#include <coral/lib/noncopy.h>

namespace coral {

     class EXPORT_coral ref_counted {
     private:
	  /** Reference count for garbage collection.
	      See REF_COUNTING.TXT for a description. */
	  mutable std::size_t refcount;
     public:
	  ref_counted() : refcount(1) { ; }
	  virtual ~ref_counted() throw () {
	    ;
	  }
	  void ref() const throw () { ++this->refcount; }
	  void unref() const throw ();
	  int get_ref_count() const throw () { return this->refcount; }
	  long get_id() const throw () { return reinterpret_cast<long>(this); }
	  /* Strange but needed, because of downcast/upcast issues in SWIG */
	  bool __eq__(const ref_counted* p) const throw () {
	       return this == p;
	  }
	  bool __ne__(const ref_counted* p) const throw () {
	       return this != p;
	  }
	  int __hash__() const throw () {
	       return reinterpret_cast<long>(this);
	  }
     };

#ifndef SWIG

	/* Keeps a ref() on an element, and drops it at destructor. */ 
	class EXPORT_coral temp_ref : public noncopy {
	private:
		const ref_counted* element;
		temp_ref(const temp_ref&);
		temp_ref& operator=(const temp_ref&);
	public:
		temp_ref(const ref_counted* e) : element(e) { if (e) e->ref(); }
		~temp_ref() throw () { if (this->element) this->element->unref(); }
		void release() throw () { this->element = NULL; }
	};

	/*
	 * Drops a ref upon destruction, unless told to release (forget) the
	 * pointer.
	 */
     template<class T>
	class EXPORT_coral autounref {
		T* ptr;
	public:
		autounref() throw () { ptr = NULL; }
		explicit autounref(T* e) throw () : ptr(e) { ; }
		T* release() throw () { T* t = ptr; ptr = NULL; return t; }
		T* get() const throw () { return ptr; }
		T* operator->() const throw () { return ptr; }
		autounref<T>& operator=(const autounref<T>& e) throw () {
		     if (&e == this) return *this;
		     if (ptr) {
			  if (ptr == e.ptr) return *this;
			  ptr->unref();
		     }
		     ptr = e.ptr;
		     const_cast<autounref<T>&>(e).ptr = NULL;
		     return *this;
		}
		autounref<T>& operator=(const T* e) throw () {
			if (e == ptr) return *this;
			if (ptr) {
			     if (ptr == e) return *this;
			     ptr->unref();
			}
			ptr = const_cast<T*>(e);
			return *this;
		}

		bool operator!() const throw () {
		     return ptr == NULL;
		}

		~autounref() throw () {
			if (ptr)
				ptr->unref();
		}
		
		autounref(const autounref<T>& a) throw () {
			if (&a != this) {
				ptr = a.ptr;
				const_cast<autounref<T>&>(a).ptr = NULL;
			}
		}
	template <class T2>
	     autounref<T>& operator=(const autounref<T2>& e) throw () {
	     if (ptr) {
		  if (ptr == e.get()) return *this;
		  ptr->unref();
	     }
	     ptr = e.get();
	     const_cast<autounref<T2>&>(e).release();
	     return *this;
	}
	};


#endif

}

#endif
