#ifndef K3DSDK_OBJECT_DATA_H
#define K3DSDK_OBJECT_DATA_H

// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "channel.h"
#include "data.h"
#include "iobject.h"
#include "iplugin_factory.h"
#include "objects.h"
#include "plugins.h"
#include "result.h"
#include "vectors.h"

#include <vector>

#ifdef	interface
#undef	interface
#endif	// interface

namespace k3d
{

/////////////////////////////////////////////////////////////////////////////
// object_adaptor

/// Wraps an instance of k3d::data<iobject::id_type>, keeping it current in the face of object deletions, etc.
template<typename interface_t, typename data_t>
class object_adaptor :
	public data_t,
	public virtual SigC::Object
{
public:
	template<typename init_t>
	object_adaptor(const init_t& Init) :
		data_t(Init),
		m_object(Init.object()),
		m_interface(0),
		m_object_collection(Init.document().objects())
	{
		initialize();
	}

	typename data_t::changed_signal_t& changed_signal()
	{
		return m_changed_signal;
	}

	iobject* object() const
	{
		return m_object;
	}

	void set_object(iobject* const Object)
	{
		set_value(Object ? Object->id() : 0);
	}

	interface_t* interface() const
	{
		return m_interface;
	}

	interface_t* operator->() const
	{
		return m_interface;
	}

	k3d::iobject_collection& object_collection() const
	{
		return m_object_collection;
	}

	void load_complete()
	{
		if(value())
			{
				m_object = k3d::find_object(m_object_collection, value());
				m_interface = dynamic_cast<interface_t*>(m_object);
				return_if_fail(m_object);

				m_object_deleted_connection = m_object->deleted_signal().connect(SigC::slot(*this, &object_adaptor::on_object_deleted));
			}
	}

	bool allow_none()
	{
		return true;
	}
	
	bool allow(iplugin_factory& Factory)
	{
		return Factory.implements(typeid(interface_t));
	}
	
	bool allow(iobject& Object)
	{
		return dynamic_cast<interface_t*>(&Object) ? true : false;
	}

private:
	void initialize()
	{
		data_t::changed_signal().connect(SigC::slot(*this, &object_adaptor::on_object_changed));

		if(m_object)
			{
				m_interface = dynamic_cast<interface_t*>(m_object);
				m_object_deleted_connection = m_object->deleted_signal().connect(SigC::slot(*this, &object_adaptor::on_object_deleted));
			}
	}

	void on_object_changed()
	{
		if(m_object)
			m_object_deleted_connection.disconnect();

		m_object = 0;
		m_interface = 0;

		if(value())
			{
				m_object = k3d::find_object(m_object_collection, value());
				m_interface = dynamic_cast<interface_t*>(m_object);

				return_if_fail(m_object);

				m_object_deleted_connection = m_object->deleted_signal().connect(SigC::slot(*this, &object_adaptor::on_object_deleted));
			}

		m_changed_signal.emit();
	}

	void on_object_deleted()
	{
		set_value(0);
	}

	typename data_t::changed_signal_t m_changed_signal;
	iobject* m_object;
	interface_t* m_interface;
	SigC::Connection m_object_deleted_connection;
	k3d::iobject_collection& m_object_collection;
};

/// Convenience macro so we can declare k3d::object_adapter objects almost as if we had template template arguments
#define k3d_object_adaptor(interface_type, name_policy, undo_policy, storage_policy) \
	k3d::object_adaptor<interface_type, k3d_data(k3d::iobject::id_type, name_policy, k3d::change_signal, undo_policy, storage_policy, k3d::no_constraint) >

class object_value_t
{
public:
	explicit object_value_t(iobject* const Object) :
		m_object(Object),
		m_value(Object ? Object->id() : 0)
	{
	}
	
	iobject* const object() const
	{
		return m_object;
	}
	
	const iobject::id_type value() const
	{
		return m_value;
	}

private:
	iobject* const m_object;
	const iobject::id_type m_value;
};

inline const initializer_t<object_value_t> init_object_value(iobject* const Object)
{
	return initializer_t<object_value_t>(object_value_t(Object));
}

} // namespace k3d

#endif // !K3DSDK_OBJECT_DATA_H


