/* crefptr - cyclic-protected reference counting smart pointers.
 * Copyright 2009-2010 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/>.
 */

#include "crefptr.hh"
#include "debug.hh"

namespace shevek
{
#ifdef DEBUG_CREFBASE
	std::list <crefbase *> crefbase::dbg_check;
#endif
	int crefbase::dbg_tag = 1;
	void crefbase::_check ()
	{
		if (!_init || _checking)
			return;
		_checking = true;
		if (!_refs.empty ())
		{
			std::list <_objptr> queue, cleaner;
			queue.push_back (this);
			bool clean = false;
			while (!clean && !queue.empty ())
			{
				_objptr current = queue.front ();
				queue.pop_front ();
				//dbg ("checking references of " << current.operator-> ());
				for (std::list <_ptrdata>::iterator i = current->_refs.begin (); i != current->_refs.end (); ++i)
				{
					//dbg (" for " << current.operator-> () << ", checking " << i->ref.operator-> () << "; owner: " << i->owner.operator-> ());
					if (i->owner.operator-> () == NULL || !i->owner->_init)
					{
						clean = true;
						break;
					}
					if (i->owner->_checking)
						continue;
					queue.push_back (i->owner);
					i->owner->_checking = true;
					cleaner.push_back (i->owner);
				}
				//dbg ("done checking references of " << current.operator-> ());
			}
			for (std::list <_objptr>::iterator i = cleaner.begin (); i != cleaner.end (); ++i)
				(*i)->_checking = false;
			if (clean)
			{
				_checking = false;
				return;
			}
			dbg ("unlinking " << this << " with " << _refs.size () << " reference(s)");
			//for (std::list <_ptrdata>::iterator i = _refs.begin (); i != _refs.end (); ++i)
				//dbg ("ref: " << i->target.operator-> () << "; " << i->owner.operator-> ());
			while (!_refs.empty ())
				_refs.front ().ref->reset ();
		}
		delete this;
	}

	crefbase crefbase::no_target;

	void crefbase::_add (_ptrptr p, _objptr owner)
	{
		_refs.push_back (_ptrdata (this, p, owner));
		p->_data = --_refs.end ();
		//dbg ("added reference " << p.operator-> () << " (target = " << p->_target ().operator-> () << ") to " << this << " with owner " << owner.operator-> ());
	}

	void crefbase::_remove (_ptrptr p)
	{
		//dbg ("removing reference " << p.operator-> () << " (target = " << p->_target ().operator-> () << ") to " << this << " with owner " << p->_owner ().operator-> ());
		_refs.erase (p->_data);
		_check ();
	}
}
