﻿/*
This source file is part of Castor3D (http://castor3d.developpez.com/castor3d.htm)

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
the program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.
*/
#ifndef ___Castor_Factory___
#define ___Castor_Factory___

namespace Castor
{
	/*!
	\author		Sylvain DOREMUS
	\version	0.6.1.0
	\date		03/01/2011
	\~english
	\brief		Factory concept implementation
	\remark		The classes that can be registered must be a IClonable
	\see		IClonable
	\~french
	\brief		Implémentation du concept de fabrique
	\remark		Les classes pouvant être enregistrées doivent étendre IClonable
	\see		IClonable
	*/
	template< class Obj, class Key=String >
	class Factory : CuNonCopyable
	{
	protected:
		typedef std::shared_ptr< Obj >		obj_ptr;
		typedef std::map< Key, obj_ptr >	obj_map;

		std::map< Key, obj_ptr > m_mapRegistered;

	public:
		/**
		 *\~english
		 *\brief		Constructor
		 *\~french
		 *\brief		Constructeur
		 */
		Factory(){}
		/**
		 *\~english
		 *\brief		Destructor
		 *\~french
		 *\brief		Destructeur
		 */
		virtual ~Factory(){}
		/**
		 *\~english
		 *\brief		Registers an object type
		 *\param[in]	p_key	The object type
		 *\param[in]	p_obj	The object to register (it will be cloned each time a creation is asked for)
		 *\~french
		 *\brief		Enregistre un type d'objet
		 *\param[in]	p_key	Le type d'objet
		 *\param[in]	p_obj	L'objet à enregistrer (il sera clôné à chaque demande de création)
		 */
		template< class SubObj >
		void Register( Key const & p_key, std::shared_ptr< SubObj > p_obj )
		{
			if( m_mapRegistered.find( p_key ) == m_mapRegistered.end() )
			{
				m_mapRegistered.insert( std::make_pair( p_key, std::static_pointer_cast< Obj >( p_obj ) ) );
			}
		}
		/**
		 *\~english
		 *\brief		Creates an object from a key
		 *\param[in]	p_key	The object type
		 *\return		The created object
		 *\~french
		 *\brief		Crée un objet à partir d'une clef (type d'objet)
		 *\param[in]	p_key	Le type d'objet
		 *\return		L'objet créé
		 */
		virtual obj_ptr Create( Key const & p_key )
		{
			obj_ptr l_pReturn;
			typename obj_map::iterator l_it = m_mapRegistered.find( p_key );

			if( l_it != m_mapRegistered.end() )
			{
				l_pReturn = l_it->second->Clone();
			}

			return l_pReturn;
		}
	};
	/*!
	\author		Sylvain DOREMUS
	\version	0.6.1.0
	\date		03/01/2011
	\~english
	\brief		Clonable interface
	\remark		Base class for classes that must be created by a factory
	\see		Factory
	\~french
	\brief		Interface de clonables
	\remark		Classe de base pour les classes devant être créées par une fabrique
	\see		Factory
	*/
	template< class Obj, class Key=String >
	class IClonable : CuNonCopyable
	{
	protected:
		 Factory< Obj, Key > & m_factory;

	public:
		/**
		 *\~english
		 *\brief		Constructor
		 *\param[in]	p_pFactory	The Factory that was used to build this instance
		 *\~french
		 *\brief		Constructeur
		 *\param[in]	p_pFactory	La Factory ayant créé cette instance
		 */
		IClonable( Factory< Obj, Key > & p_factory )	:	m_factory( p_factory )	{}
		/**
		 *\~english
		 *\brief		Destructor
		 *\~french
		 *\brief		Destructeur
		 */
		virtual ~IClonable(){}
		/**
		 *\~english
		 *\brief		Clone function, used by the factory to create objects of a wanted type
		 *\remark		Must be implemented in derieved classes
		 *\return		A clone of this object
		 *\~french
		 *\brief		Fonction de clonage, utilisée par la fabrique pour créer des objets d'un type donné
		 *\remark		Doit être implémentée dans les classes dérivées
		 *\return		Un clône de cet objet
		 */
		virtual std::shared_ptr< Obj > Clone()=0;
	};
}

#endif
