/* crefptr - cyclic-protected reference counting smart pointers.
 * Copyright 2009 Bas Wijnen <wijnen@debian.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef SHEVEK_CREFPTR_HH
#define SHEVEK_CREFPTR_HH

#include <list>
#include <iostream>
#include "error.hh"

namespace shevek
{
	// Structure of the refptr:
	// The object with the data derives from crefbase.  This cannot be copied.
	// Pointers to it are of type crefptr <_T>, where _T is the deriving class.
	// crefbase contains a list of _ptrdata with pointers to the object.
	// _ptrdata contains the target object, the pointer which references it and the owner of the reference.
	// crefptr contains (through _ptr) a list iterator, pointing to its _ptrdata.
	// crefptr->_data->_ref is always crefptr, and object->_refs[]->target is always object.

	/// Circular-dependancy-protected reference-counting object base class.
	/** Objects which derive from this class are reference-counted in a way that detects dependency loops and destroys the entire loop when it is no longer externally referenced.
	 *  For this, it is important that objects which contain pointers register them appropriately.
	 */
	class crefbase
	{
		class _ptr;
		class _ptrptr;
		class _objptr;
		template <typename _T> friend class crefptr;

		// Internal pointers to crefptrs.
		class _ptrptr
		{
			_ptr *_data;
		public:
			_ptr &operator* () { return *_data; }
			_ptr *operator-> () { return _data; }
			_ptrptr (_ptr *p) { _data = p; }
		};

		// Internal pointers to crefbase objects.
		class _objptr
		{
			crefbase *_data;
		public:
			_objptr (crefbase *d) { _data = d; }
			crefbase &operator* () { return *_data; }
			crefbase *operator-> () const { return _data; }
		};

		// This struct is used internally for storing back references to a pointer.
		struct _ptrdata
		{
			_objptr target;	// Link to self, so _ptr only needs to store an iterator.
			_ptrptr ref;	// The referencing pointer.
			_objptr owner;	// The owner of ref.
			_ptrdata (_objptr t, _ptrptr r, _objptr o) : target (t), ref (r), owner (o) {}
		};

		// Base class for crefptr.
		class _ptr
		{
			friend class crefbase;
			template <typename _T> friend class crefptr;
			std::list <_ptrdata>::iterator _data;
			_objptr _target () const { return _data->target; }
			_objptr &_owner () const { return _data->owner; }
			// The reference counting machinery.
		protected:
			inline _ptr (_objptr owner, _objptr target);
			inline ~_ptr ();
		public:
			inline _ptr (_ptr const &that);
			inline _ptr &operator= (_ptr const &that);
			inline void reset ();
			inline void set_owner (crefbase *owner);
		};

		// Object to which null-pointers point.  This is needed to store their owners.  This object is never deleted because init_done () is never called.
		static crefbase no_target;
		// This is set at creation; the object cannot be destroyed until it is reset using init_done ().
		int _init;
		// List of pointers referencing this object, with their owner.
		// This costs more memory than storing the data in _ptr, but _ptr must be as small as possible,
		// because it is passed around and thus copied a lot.
		std::list <_ptrdata> _refs;
		// Use _refs.
		void _check ();
		bool _checking;
		void _add (_ptrptr p, _objptr owner);
		void _remove (_ptrptr p);
		// Can't copy refcounted objects.
		crefbase &operator= (crefbase const &that);	// NI
		crefbase (crefbase const &that);		// NI
		static int dbg_tag;
#ifdef DEBUG_CREFBASE
		std::list <crefbase *>::iterator dbg_iterator;
		static std::list <crefbase *> dbg_check;
#endif
	protected:
		/// Constructor, which is called when an object is created.
		crefbase () : _init (0), _checking (false)
		{
#ifdef DEBUG_CREFBASE
			dbg_check.push_back (this);
			dbg_iterator = --dbg_check.end ();
#endif
		}
		/// Virtual destructor, which does nothing except allowing derived class to have a virtual destructor.
		virtual ~crefbase ()
		{
			if (this == &no_target)
				return;
			if (!_refs.empty ())
				shevek_error ("reference list is not empty");
#ifdef DEBUG_CREFBASE
			dbg_check.erase (dbg_iterator);
#endif
			if (_init == 0)
				shevek_error ("removing object before init_done was called");
			if (_init != 1)
				std::cerr << "Removing object " << this << " which is tagged " << _init << '\n';
		}
	public:
		/// Set the default tag for when init_done is called.
		/** If the tag is not set to 1, a message will be printed to standard error on destruction.
		 *  The previous tag is returned.
		 *  If tag is set to 0, the old value is not changed.
		 *  The initial default value is 1.
		 */
		static int set_default_tag (int tag)
		{
			int old = dbg_tag;
			if (tag)
				dbg_tag = tag;
			return old;
		}
		/// After calling this, the object is destroyed without references.
		/** On creation, an object does not have any references.  To prevent immediate destruction, it is first in an initialisation phase.
		 *  During that phase, it will not be destroyed, even if it has no references.
		 *  This function should be called immeiately after creating the object (normally through crefptr::init):
		 *  shevek::crefptr <foo> bar = foo::create ().init ();
		 *  If code is not given or 0, the default tag (set with set_default_tag) will be used.
		 */
		void init_done (int code = 0)
		{
			if (!code)
				code = dbg_tag;
			if (this == &no_target)
				shevek_error ("calling init_done on no_target");
			if (_init)
				shevek_error ("calling init_done more than once");
			_init = code;
			if (code != 1)
				std::cerr << "Tagging object " << this << " with code " << code << '\n';
			_check ();
		}
		/// Check if all objects have called init_done.
		/** When debugging is enabled, this function checks for all objects if they have called init_done.
		 *  When debugging is not enabled, no list of objects is kept, and this check does nothing.
		 */
		static void check (bool fatal = true)
		{
#ifdef DEBUG_CREFBASE
			unsigned count = 0;
			for (std::list <crefbase *>::iterator i = dbg_check.begin (); i != dbg_check.end (); ++i)
			{
				if (*i == &no_target)
					continue;
				if (!(*i)->_init)
				{
					shevek_warning (shevek::ostring ("init_done not called before check for %08x", (unsigned)*i));
					++count;
				}
				else if ((*i)->_init != 1)
					std::cerr << "Check: object " << *i << ", tagged " << (*i)->_init << " still lives (" << (*i)->_refs.size () << " references)\n";
			}
			std::cerr << "checked " << dbg_check.size () << " items\n";
			if (count && fatal)
			{
				shevek_error ("check failed");
				throw "check failed";
			}
#endif
		}
	};

	/// Keep a pointer to an object derived from crefbase.
	template <typename _T> class crefptr : public crefbase::_ptr
	{
	public:
		/// Create a new pointer.  If this pointer is stored inside a crefbase-derived object, make sure to set the owner.
		crefptr (crefbase *target = NULL, crefbase *owner = NULL) : _ptr (owner, target) {}
		/// Dereference the pointer.
		_T &operator* () const { if (_target ().operator-> () == &crefbase::no_target) shevek_error ("dereferencing NULL crefptr"); return reinterpret_cast <_T &> (*_target ()); }
		/// Dereference the pointer.
		_T *operator-> () const { if (_target ().operator-> () == &crefbase::no_target) return NULL; return reinterpret_cast <_T *> (_target ().operator-> ()); }
		/// Test if two pointers refer to the same object.
		bool operator== (crefptr <_T> const &that) const { return _target ().operator-> () == that._target ().operator-> (); }
		/// Test if two pointers don't refer to the same object.
		bool operator!= (crefptr <_T> const &that) const { return _target ().operator-> () != that._target ().operator-> (); }
		/// Create a new pointer from this one, up- or downcast.  Normally, this is used to fill a new crefptr.
		template <typename _R> _R *cast_dynamic () const { return dynamic_cast <_R *> (_target ().operator-> ()); }
		/// Implicit pointer conversion.
		operator _T * () const { _T *ret = reinterpret_cast <_T *> (_target ().operator-> ()); if (ret == &crefbase::no_target) return NULL; return ret; }
		/// Allow the pointer to be destroyed. See crefbase::init_done for details.
		crefptr <_T> init (int code = 0)
		{
			_target ()->init_done (code);
			return *this;
		}
	};

	crefbase::_ptr::_ptr (_objptr owner, _objptr target)
	{
		if (target.operator-> () == NULL)
			target = &crefbase::no_target;
		target->_add (this, owner);
	}

	crefbase::_ptr::~_ptr ()
	{
		_target ()->_remove (this);
	}

	// Using the copy constructor is a problem, because the owner is not given.
	// So it really shouldn't be used, but it's unacceptable to forbid passing
	// crefptrs as function arguments.  So we assume that the copy constructor
	// will not be used in other places, and set the owner to NULL.
	crefbase::_ptr::_ptr (_ptr const &that)
	{
		_objptr target = that._target ();
		target->_add (this, NULL);
	}

	crefbase::_ptr &crefbase::_ptr::operator= (_ptr const &that)
	{
		_objptr target = that._target ();
		if (target.operator-> () == _target ().operator-> ())
			return *this;
		_objptr owner = _owner ();
		_target ()->_remove (this);
		target->_add (this, owner);
		return *this;
	}

	void crefbase::_ptr::reset ()
	{
		if (_target ().operator-> () == &crefbase::no_target)
			return;
		_objptr owner = _owner ();
		_target ()->_remove (this);
		crefbase::no_target._add (this, owner);
	}

	void crefbase::_ptr::set_owner (crefbase *owner)
	{
		_owner () = owner;
		_target ()->_check ();
	}
}

#endif
