#ifndef K3DSDK_PROPERTY_COLLECTION_H
#define K3DSDK_PROPERTY_COLLECTION_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

/** \file
		\brief Declares commandnode, which provides a default implementation of icommand_node
		\author Tim Shead (tshead@k-3d.com)
*/

#include "data.h"
#include "idag.h"
#include "ienumeration_property.h"
#include "ilist_property.h"
#include "imeasurement_property.h"
#include "iobject.h"
#include "iobject_property.h"
#include "iproperty.h"
#include "iproperty_collection.h"
#include "iscript_property.h"
#include "iwritable_property.h"

#include <boost/lexical_cast.hpp>

namespace k3d
{

namespace property
{

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// data_proxy

/// Implementation of iproperty that can front for k3d::data objects
template<typename data_t>
class data_proxy :
	public data_t
{
public:
	typedef typename data_t::value_t value_t;
	
	template<typename init_t>
	data_proxy(const init_t& Init) :
		data_t(Init),
		m_proxy(*this, Init.description()),
		m_dag(Init.document().dag())
	{
	}

	iproperty& property()
	{
		return m_proxy;
	}

	operator iproperty&()
	{
		return m_proxy;
	}

	value_t property_value()
	{
		iproperty* source = &m_proxy;
		for(iproperty* dependency = m_dag.dependency(*source); dependency; dependency = m_dag.dependency(*dependency))
			source = dependency;
			
		if(source != &m_proxy)
			return boost::any_cast<value_t>(source->value());

		return data_t::value();
	}	
	
private:
	class proxy_t :
		public iproperty,
		public iwritable_property
	{
	public:
		proxy_t(data_t& Data, const char* const Description) :
			m_data(Data),
			m_description(Description)
		{
		}
		
		~proxy_t()
		{
			m_deleted_signal.emit();
		}

		const std::string name()
		{
			return m_data.name();
		}

		const std::string description()
		{
			return m_description;
		}

		const std::type_info& type()
		{
			return typeid(typename data_t::value_t);
		}

		const boost::any value()
		{
			return boost::any(m_data.value());
		}

		bool set_value(const boost::any Value)
		{
			const typename data_t::value_t* const new_value = boost::any_cast<typename data_t::value_t>(&Value);
			if(!new_value)
				return false;

			m_data.set_value(*new_value);
			return true;
		}

		changed_signal_t& changed_signal()
		{
			return m_data.changed_signal();
		}
		
		deleted_signal_t& deleted_signal()
		{
			return m_deleted_signal;
		}

	private:
		data_t& m_data;
		const char* const m_description;
		deleted_signal_t m_deleted_signal;
	};
	
	proxy_t m_proxy;
	k3d::idag& m_dag;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// read_only_data_proxy

/// Implementation of iproperty that can front for k3d::data objects
template<typename data_t>
class read_only_data_proxy :
	public data_t
{
public:
	typedef typename data_t::value_t value_t;
	
	template<typename init_t>
	read_only_data_proxy(const init_t& Init) :
		data_t(Init),
		m_proxy(*this, Init.description()),
		m_dag(Init.document().dag())
	{
	}

	iproperty& property()
	{
		return m_proxy;
	}

	operator iproperty&()
	{
		return m_proxy;
	}

	value_t property_value()
	{
		iproperty* source = &m_proxy;
		for(iproperty* dependency = m_dag.dependency(*source); dependency; dependency = m_dag.dependency(*dependency))
			source = dependency;
			
		if(source != &m_proxy)
			return boost::any_cast<value_t>(source->value());

		return data_t::value();
	}	
	
private:
	class proxy_t :
		public iproperty
	{
	public:
		proxy_t(data_t& Data, const char* const Description) :
			m_data(Data),
			m_description(Description)
		{
		}
		
		~proxy_t()
		{
			m_deleted_signal.emit();
		}

		const std::string name()
		{
			return m_data.name();
		}

		const std::string description()
		{
			return m_description;
		}

		const std::type_info& type()
		{
			return typeid(typename data_t::value_t);
		}

		const boost::any value()
		{
			return boost::any(m_data.value());
		}

		changed_signal_t& changed_signal()
		{
			return m_data.changed_signal();
		}
		
		deleted_signal_t& deleted_signal()
		{
			return m_deleted_signal;
		}

	private:
		data_t& m_data;
		const char* const m_description;
		deleted_signal_t m_deleted_signal;
	};
	
	proxy_t m_proxy;
	k3d::idag& m_dag;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// measurement_proxy

/// Implementation of iproperty that can front for k3d::data objects
template<typename data_t>
class measurement_proxy :
	public data_t
{
public:
	typedef typename data_t::value_t value_t;

	template<typename init_t>	
	measurement_proxy(const init_t& Init) :
		data_t(Init),
		m_proxy(*this, Init.description(), Init.precision(), Init.step_increment(), Init.units()),
		m_dag(Init.document().dag())
	{
	}

	iproperty& property()
	{
		return m_proxy;
	}

	operator iproperty&()
	{
		return m_proxy;
	}

	value_t property_value()
	{
		iproperty* source = &m_proxy;
		for(iproperty* dependency = m_dag.dependency(*source); dependency; dependency = m_dag.dependency(*dependency))
			source = dependency;
			
		if(source != &m_proxy)
			return boost::any_cast<value_t>(source->value());

		return data_t::value();
	}	
	
private:
	class proxy_t :
		public iproperty,
		public iwritable_property,
		public imeasurement_property
	{
	public:
		explicit proxy_t(data_t& Data, const char* const Description, const unsigned long Precision, const double StepIncrement, const std::type_info& Units) :
			m_data(Data),
			m_description(Description),
			m_precision(Precision),
			m_step_increment(StepIncrement),
			m_units(Units)
		{
		}

		~proxy_t()
		{
			m_deleted_signal.emit();
		}

		const std::string name()
		{
			return m_data.name();
		}

		const std::string description()
		{
			return m_description;
		}

		const std::type_info& type()
		{
			return typeid(typename data_t::value_t);
		}

		const boost::any value()
		{
			return boost::any(m_data.value());
		}

		bool set_value(const boost::any Value)
		{
			const typename data_t::value_t* const new_value = boost::any_cast<typename data_t::value_t>(&Value);
			if(!new_value)
				return false;

			m_data.set_value(*new_value);
			return true;
		}

		changed_signal_t& changed_signal()
		{
			return m_data.changed_signal();
		}

		deleted_signal_t& deleted_signal()
		{
			return m_deleted_signal;
		}

		const unsigned long precision()
		{
			return m_precision;
		}
		
		const double step_increment()
		{
			return m_step_increment;
		}
		
		const std::type_info& units()
		{
			return m_units;
		}

	private:
		data_t& m_data;
		const char* const m_description;
		const unsigned long m_precision;
		const double m_step_increment;
		const std::type_info& m_units;
		deleted_signal_t m_deleted_signal;
	};
	
	proxy_t m_proxy;
	k3d::idag& m_dag;
};

/////////////////////////////////////////////////////////////////////////////
// string_proxy

/// Implementation of iproperty that can front for k3d::data objects, exposing the underlying type as a string property
template<typename data_t>
class string_proxy :
	public data_t
{
public:
	typedef typename data_t::value_t value_t;

	template<typename init_t>	
	string_proxy(const init_t& Init) :
		data_t(Init),
		m_proxy(*this, Init.description()),
		m_dag(Init.document().dag())
	{
	}

	iproperty& property()
	{
		return m_proxy;
	}
	
	operator iproperty&()
	{
		return m_proxy;
	}

	const value_t property_value()
	{
		iproperty* source = &m_proxy;
		for(iproperty* dependency = m_dag.dependency(*source); dependency; dependency = m_dag.dependency(*dependency))
			source = dependency;
			
		if(source != &m_proxy)
			return boost::any_cast<value_t>(source->value());

		return data_t::value();
	}	
	
private:
	class proxy_t :
		public iproperty,
		public iwritable_property
	{
	public:
		proxy_t(data_t& Data, const char* const Description) :
			m_data(Data),
			m_description(Description)
		{
		}

		~proxy_t()
		{
			m_deleted_signal.emit();
		}

		const std::string name()
		{
			return m_data.name();
		}

		const std::string description()
		{
			return m_description;
		}

		const std::type_info& type()
		{
			return typeid(std::string);
		}

		const boost::any value()
		{
			try
				{
					return boost::any(boost::lexical_cast<std::string>(m_data.value()));
				}
			catch(...)
				{
				}

			return boost::any();
		}

		bool set_value(const boost::any Value)
		{
			const std::string* const new_value = boost::any_cast<std::string>(&Value);
			if(!new_value)
				return false;

			try
				{
					m_data.set_value(boost::lexical_cast<typename data_t::value_t>(*new_value));
					return true;
				}
			catch(...)
				{
				}

			return false;
		}

		changed_signal_t& changed_signal()
		{
			return m_data.changed_signal();
		}

		deleted_signal_t& deleted_signal()
		{
			return m_deleted_signal;
		}

	private:
		data_t& m_data;
		const char* const m_description;
		deleted_signal_t m_deleted_signal;
	};
	
	proxy_t m_proxy;
	k3d::idag& m_dag;
};

/////////////////////////////////////////////////////////////////////////////
// script_proxy

/// Implementation of iproperty that can front for k3d::data objects, exposing the underlying script as a string property
template<typename data_t>
class script_proxy :
	public data_t
{
public:
	typedef typename data_t::value_t value_t;

	template<typename init_t>	
	script_proxy(const init_t& Init) :
		data_t(Init),
		m_proxy(*this, Init.description()),
		m_dag(Init.document().dag())
	{
	}

	iproperty& property()
	{
		return m_proxy;
	}
	
	operator iproperty&()
	{
		return m_proxy;
	}

	const value_t property_value()
	{
		iproperty* source = &m_proxy;
		for(iproperty* dependency = m_dag.dependency(*source); dependency; dependency = m_dag.dependency(*dependency))
			source = dependency;
			
		if(source != &m_proxy)
			return boost::any_cast<value_t>(source->value());

		return data_t::value();
	}	
	
private:
	class proxy_t :
		public iproperty,
		public iwritable_property,
		public iscript_property
	{
	public:
		proxy_t(data_t& Data, const char* const Description) :
			m_data(Data),
			m_description(Description)
		{
		}

		~proxy_t()
		{
			m_deleted_signal.emit();
		}

		const std::string name()
		{
			return m_data.name();
		}

		const std::string description()
		{
			return m_description;
		}

		const std::type_info& type()
		{
			return typeid(std::string);
		}

		const boost::any value()
		{
			try
				{
					return boost::any(boost::lexical_cast<std::string>(m_data.value()));
				}
			catch(...)
				{
				}

			return boost::any();
		}

		bool set_value(const boost::any Value)
		{
			const std::string* const new_value = boost::any_cast<std::string>(&Value);
			if(!new_value)
				return false;

			try
				{
					m_data.set_value(boost::lexical_cast<typename data_t::value_t>(*new_value));
					return true;
				}
			catch(...)
				{
				}

			return false;
		}

		changed_signal_t& changed_signal()
		{
			return m_data.changed_signal();
		}

		deleted_signal_t& deleted_signal()
		{
			return m_deleted_signal;
		}

	private:
		data_t& m_data;
		const char* const m_description;
		deleted_signal_t m_deleted_signal;
	};
	
	proxy_t m_proxy;
	k3d::idag& m_dag;
};

/////////////////////////////////////////////////////////////////////////////
// enumeration_proxy

/// Implementation of iproperty that can front for k3d::data objects, exposing the underlying type as an enumeration (string) property
template<typename data_t>
class enumeration_proxy :
	public data_t
{
public:
	typedef typename data_t::value_t value_t;
	
	template<typename init_t>
	enumeration_proxy(const init_t& Init) :
		data_t(Init),
		m_proxy(*this, Init.description(), Init.values()),
		m_dag(Init.document().dag())
	{
	}

	iproperty& property()
	{
		return m_proxy;
	}
	
	operator iproperty&()
	{
		return m_proxy;
	}

	const value_t property_value()
	{
		iproperty* source = &m_proxy;
		for(iproperty* dependency = m_dag.dependency(*source); dependency; dependency = m_dag.dependency(*dependency))
			source = dependency;
			
		if(source != &m_proxy)
			return boost::any_cast<value_t>(source->value());

		return data_t::value();
	}	
	
private:
	class proxy_t :
		public iproperty,
		public iwritable_property,
		public ienumeration_property
	{
	public:
		proxy_t(data_t& Data, const char* const Description, const ienumeration_property::values_t& Values) :
			m_data(Data),
			m_description(Description),
			m_values(Values)
		{
		}

		~proxy_t()
		{
			m_deleted_signal.emit();
		}

		const std::string name()
		{
			return m_data.name();
		}

		const std::string description()
		{
			return m_description;
		}

		const std::type_info& type()
		{
			return typeid(std::string);
		}

		const boost::any value()
		{
			try
				{
					return boost::any(boost::lexical_cast<std::string>(m_data.value()));
				}
			catch(...)
				{
				}

			return boost::any();
		}

		bool set_value(const boost::any Value)
		{
			const std::string* const new_value = boost::any_cast<std::string>(&Value);
			if(!new_value)
				return false;

			try
				{
					m_data.set_value(boost::lexical_cast<typename data_t::value_t>(*new_value));
					return true;
				}
			catch(...)
				{
				}

			return false;
		}

		changed_signal_t& changed_signal()
		{
			return m_data.changed_signal();
		}

		deleted_signal_t& deleted_signal()
		{
			return m_deleted_signal;
		}

		values_t values()
		{
			return m_values;
		}

	private:
		data_t& m_data;
		const char* const m_description;
		const ienumeration_property::values_t& m_values;
		deleted_signal_t m_deleted_signal;
	};
	
	proxy_t m_proxy;
	k3d::idag& m_dag;
};

/////////////////////////////////////////////////////////////////////////////
// list_proxy

/// Implementation of iproperty that can front for k3d::data objects, exposing the underlying type as a list (string) property
template<typename data_t>
class list_proxy :
	public data_t
{
public:
	typedef typename data_t::value_t value_t;
	
	template<typename init_t>
	list_proxy(const init_t& Init) :
		data_t(Init),
		m_proxy(*this, Init.description(), Init.values()),
		m_dag(Init.document().dag())
	{
	}

	iproperty& property()
	{
		return m_proxy;
	}
	
	operator iproperty&()
	{
		return m_proxy;
	}

	const value_t property_value()
	{
		iproperty* source = &m_proxy;
		for(iproperty* dependency = m_dag.dependency(*source); dependency; dependency = m_dag.dependency(*dependency))
			source = dependency;
			
		if(source != &m_proxy)
			return boost::any_cast<value_t>(source->value());

		return data_t::value();
	}	
	
private:
	class proxy_t :
		public iproperty,
		public iwritable_property,
		public ilist_property<value_t>
	{
	public:
		typedef typename ilist_property<value_t>::values_t values_t;
		
		proxy_t(data_t& Data, const char* const Description, const values_t& Values) :
			m_data(Data),
			m_description(Description),
			m_values(Values)
		{
		}

		~proxy_t()
		{
			m_deleted_signal.emit();
		}

		const std::string name()
		{
			return m_data.name();
		}

		const std::string description()
		{
			return m_description;
		}

		const std::type_info& type()
		{
			return typeid(value_t);
		}

		const boost::any value()
		{
			return boost::any(m_data.value());
		}

		bool set_value(const boost::any Value)
		{
			const value_t* const new_value = boost::any_cast<value_t>(&Value);
			if(!new_value)
				return false;

			m_data.set_value(*new_value);
			return true;
		}

		changed_signal_t& changed_signal()
		{
			return m_data.changed_signal();
		}

		deleted_signal_t& deleted_signal()
		{
			return m_deleted_signal;
		}

		values_t values()
		{
			return m_values;
		}

	private:
		data_t& m_data;
		const char* const m_description;
		const values_t& m_values;
		deleted_signal_t m_deleted_signal;
	};
	
	proxy_t m_proxy;
	k3d::idag& m_dag;
};

/////////////////////////////////////////////////////////////////////////////
// object_proxy

/// Implementation of iproperty that can front for k3d::object_data objects
template<typename data_t>
class object_proxy :
	public data_t
{
public:
	typedef typename data_t::value_t value_t;

	template<typename init_t>	
	object_proxy(const init_t& Init) :
		data_t(Init),
		m_proxy(*this, Init.description()),
		m_dag(Init.document().dag())
	{
	}

	iproperty& property()
	{
		return m_proxy;
	}

	operator iproperty&()
	{
		return m_proxy;
	}

	const value_t property_value()
	{
		iproperty* source = &m_proxy;
		for(iproperty* dependency = m_dag.dependency(*source); dependency; dependency = m_dag.dependency(*dependency))
			source = dependency;
			
		if(source != &m_proxy)
			return boost::any_cast<value_t>(source->value());

		return data_t::value();
	}	
	
private:
	class proxy_t :
		public iproperty,
		public iwritable_property,
		public iobject_property
	{
	public:
		proxy_t(data_t& Data, const char* const Description) :
			m_data(Data),
			m_description(Description)
		{
		}

		~proxy_t()
		{
			m_deleted_signal.emit();
		}

		const std::string name()
		{
			return m_data.name();
		}

		const std::string description()
		{
			return m_description;
		}

		const std::type_info& type()
		{
			return typeid(iobject*);
		}

		const boost::any value()
		{
			return boost::any(m_data.object());
		}

		bool set_value(const boost::any Value)
		{
			iobject* const * new_value = boost::any_cast<iobject*>(&Value);
			if(!new_value)
				return false;

			m_data.set_object(*new_value);
			return true;
		}

		changed_signal_t& changed_signal()
		{
			return m_data.changed_signal();
		}

		deleted_signal_t& deleted_signal()
		{
			return m_deleted_signal;
		}

		bool allow_none()
		{
			return m_data.allow_none();
		}
		
		bool allow(k3d::iplugin_factory& Factory)
		{
			return m_data.allow(Factory);
		}
		
		bool allow(iobject& Object)
		{
			return m_data.allow(Object);
		}

	private:
		data_t& m_data;
		const char* const m_description;
		deleted_signal_t m_deleted_signal;
	};
	
	proxy_t m_proxy;
	k3d::idag& m_dag;
};

} // namespace property

class enumeration_t
{
public:
	explicit enumeration_t(const ienumeration_property::values_t& Values) :
		m_values(Values)
	{
	}
	
	const ienumeration_property::values_t& values() const
	{
		return m_values;
	}
	
private:
	const ienumeration_property::values_t& m_values;
};

inline const initializer_t<enumeration_t> init_enumeration(const ienumeration_property::values_t& Values)
{
	return initializer_t<enumeration_t>(enumeration_t(Values));
}

template<typename data_t>
class values_t
{
public:
	explicit values_t(const data_t& Values) :
		m_values(Values)
	{
	}
	
	const data_t& values() const
	{
		return m_values;
	}
	
private:
	const data_t& m_values;
};

template<typename data_t>
inline const initializer_t<values_t<data_t> > init_values(const data_t& Values)
{
	return initializer_t<values_t<data_t> >(values_t<data_t>(Values));
}

class precision_t
{
public:
	explicit precision_t(const unsigned int Precision) :
		m_precision(Precision)
	{
	}
	
	const unsigned int precision() const
	{
		return m_precision;
	}

private:
	const unsigned int m_precision;
};

inline const initializer_t<precision_t> init_precision(const unsigned int Precision)
{
	return initializer_t<precision_t>(precision_t(Precision));
}

class step_increment_t
{
public:
	explicit step_increment_t(const double StepIncrement) :
		m_step_increment(StepIncrement)
	{
	}

	const double step_increment() const
	{
		return m_step_increment;
	}

private:
	const double m_step_increment;
};

inline const initializer_t<step_increment_t> init_step_increment(const double StepIncrement)
{
	return initializer_t<step_increment_t>(step_increment_t(StepIncrement));
}

class units_t
{
public:
	explicit units_t(const std::type_info& Units) :
		m_units(Units)
	{
	}

	const std::type_info& units() const
	{
		return m_units;
	}

private:
	const std::type_info& m_units;
};

inline const initializer_t<units_t> init_units(const std::type_info& Units)
{
	return initializer_t<units_t>(units_t(Units));
}

/////////////////////////////////////////////////////////////////////////////
// property_collection

/// Provides a default implementation of iproperty_collection
class property_collection :
	public iproperty_collection
{
public:
	property_collection(idag& Dag);
	virtual ~property_collection();

	// iproperty_collection implementation ...
	const properties_t& properties();
	properties_changed_signal_t& properties_changed_signal();

	/// Registers a new property
	void register_property(iproperty& Property);
	/// Unregisters a property
	void unregister_property(iproperty& Property);

private:
	idag& m_dag;
	/// Contains the collection of property proxy objects
	properties_t m_properties;
	/// Change-notification signal for the collection
	properties_changed_signal_t m_changed_signal;
};

/// Convenience macro so we can declare k3d::property::data_proxy objects as if they were k3d::data objects
#define k3d_data_property(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) \
	k3d::property::data_proxy<k3d_data(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) >
/// Convenience macro so we can declare k3d::property::read_only_data_proxy objects as if they were k3d::data objects
#define k3d_read_only_data_property(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) \
	k3d::property::read_only_data_proxy<k3d_data(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) >
/// Convenience macro so we can declare k3d::property::measurement_proxy objects as if they were k3d::data objects
#define k3d_measurement_property(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) \
	k3d::property::measurement_proxy<k3d_data(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) >
/// Convenience macro so we can declare k3d::property::string_proxy objects as if they were k3d::data objects
#define k3d_string_property(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) \
	k3d::property::string_proxy<k3d_data(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) >
/// Convenience macro so we can declare k3d::property::script_proxy objects as if they were k3d::data objects
#define k3d_script_property(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) \
	k3d::property::script_proxy<k3d_data(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) >
/// Convenience macro so we can declare k3d::property::enumeration_proxy objects as if they were k3d::data objects
#define k3d_enumeration_property(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) \
	k3d::property::enumeration_proxy<k3d_data(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) >
/// Convenience macro so we can declare k3d::property::list_proxy objects as if they were k3d::data objects
#define k3d_list_property(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) \
	k3d::property::list_proxy<k3d_data(data_type, name_policy, signal_policy, undo_policy, storage_policy, constraint_policy) >
/// Convenience macro so we can declare k3d::property::object_proxy objects as if they were k3d::data objects
#define k3d_object_property(interface_type, name_policy, undo_policy, storage_policy) \
	k3d::property::object_proxy<k3d_object_adaptor(interface_type, name_policy, undo_policy, storage_policy) >
/// Convenience macro so we can declare k3d::shader_adaptor objects as if they where k3d::data objects
#define k3d_shader_property(name_policy, undo_policy, storage_policy) \
	k3d::property::data_proxy<k3d_shader_adaptor(name_policy, undo_policy, storage_policy) >


} // namespace k3d

#endif // K3DSDK_PROPERTY_COLLECTION_H

