/************************************************************************************
TerraLib - a library for developing GIS applications.
Copyright © 2001-2007 INPE and Tecgraf/PUC-Rio.

This code is part of the TerraLib library.
This library 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.1 of the License, or (at your option) any later version.

You should have received a copy of the GNU Lesser General Public
License along with this library.

The authors reassure the license terms regarding the warranties.
They specifically disclaim any warranties, including, but not limited to,
the implied warranties of merchantability and fitness for a particular purpose.
The library provided hereunder is on an "as is" basis, and the authors have no
obligation to provide maintenance, support, updates, enhancements, or modifications.
In no event shall INPE and Tecgraf / PUC-Rio be held liable to any party for direct,
indirect, special, incidental, or consequential damages arising out of the use
of this library and its documentation.
*************************************************************************************/
/*! \file TeAbstractFactory.h
	\brief Implements a template for the Abstract Factory pattern
*/
#ifndef  __TERRALIB_INTERNAL_NEW_FACTORY_H
#define  __TERRALIB_INTERNAL_NEW_FACTORY_H

#include "TeDefines.h"

#include <map>
#include <string>

//using std::map;
//using std::string;

//!  Implements a template for the Abstract Factory pattern.
/*
    This file implements a templated version of the Abstract Factory Pattern 
	See: "Design Patterns - Elements of Reusable Object-Oriented Software", by Gamma et. al
	page 87.
  \author Gilberto Camara, Lúbia Vinhas
*/
template <typename PRODUCT,						// The product to be made
		  typename PARAMS,						// The parameter necessary to make the product
		  typename PRODUCTIDTYPE=std::string>	// The type used to identify the product
class /*TL_DLL*/ TeAbstractFactory
{
public:

	//! To link each product to its factory
	typedef std::map<PRODUCTIDTYPE, TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>* > TeFactoryMap;

	//! Returns the single instance of the factory registration
	static TeFactoryMap& instance()
	{ 
		static TeFactoryMap Fmap_;
		return Fmap_;
	}

protected:

	//!  Builds a new product from a set of parameters (should be implemented by descendants)
    virtual PRODUCT* build(PARAMS* /*arg*/)
	{ 
		return PRODUCT::DefaultObject(); 
	}

 	//!  Builds a new product without parameters (should be implemented by descendants)
   virtual PRODUCT* build()
	{ 
		return PRODUCT::DefaultObject(); 
	}

public:
	//! Factory Constructor
	/*
		\param prodId the identification of the product that the factory produces
	*/
   TeAbstractFactory(PRODUCTIDTYPE prodId):
		productIdentification_(prodId)
	{
		TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance()[prodId] = this;
	}


	//! Destructor
   virtual ~TeAbstractFactory(){ }

	//! Virtual constructor: make the product from some arguments
	/*
		\param arg a pointer to an structure that contains the arguments
		necessary to build the product. The type of product has to be extracted
		from the arguments
	*/
	static PRODUCT* make(PARAMS* arg)
	{
		// If there are no arguments or factory identification return the default object
		if (!arg)
			return PRODUCT::DefaultObject();

		PRODUCTIDTYPE productId = arg->getProductId();

		// try to find the name on the factory dictionary
		typename TeFactoryMap::iterator i = TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance().find(productId);

		// Not found ?  return the Default Object
		if (i == TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance().end())
			return PRODUCT::DefaultObject();

		// Create an object, based on the input parameters
			return (*i).second->build(arg);
	}

	//! Virtual constructor: explictly identifies which product should be made
	static PRODUCT* make(PRODUCTIDTYPE prodId)
	{
		// try to find the name on the factory dictionary
		typename TeFactoryMap::iterator i = TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance().find(prodId);

		// Not found ?  return the Default Object
		if (i == TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance().end())
			return PRODUCT::DefaultObject();

		// Create an object, based on the input parameters
		return (*i).second->build();
	}

private:
	PRODUCTIDTYPE  productIdentification_;
};

//// Factor Constructor
//template <typename PRODUCT, typename PARAMS, typename PRODUCTIDTYPE>
//inline TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::TeAbstractFactory(PRODUCTIDTYPE prodId):
//	productIdentification_(prodId)
//{
//	TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance()[prodId] = this;
//}

//template <typename PRODUCT, typename PARAMS, typename PRODUCTIDTYPE>
//inline TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::~TeAbstractFactory()
//{

//}

////! Builds an object, based on the input parameters
//template <typename PRODUCT, typename PARAMS, typename PRODUCTIDTYPE>
//inline PRODUCT*
//TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::make(PRODUCTIDTYPE producId)
//{
//	// try to find the name on the factory dictionary
//	typename TeFactoryMap::iterator i = TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance().find(producId);

//	// Not found ?  return the Default Object
//	if (i == TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance().end())
//		return PRODUCT::DefaultObject();

//	// Create an object, based on the input parameters
//	return (*i).second->build();
//}

////! Builds an object, based on the input parameters
//template <typename PRODUCT, typename PARAMS, typename PRODUCTIDTYPE>
//inline PRODUCT*
//TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::make(PARAMS* arg)
//{
//	// If there are no arguments or factory identification return the default object
//	if (!arg)
//		return PRODUCT::DefaultObject();

//	PRODUCTIDTYPE productId = arg->getProductId();

//	// try to find the name on the factory dictionary
//	typename TeFactoryMap::iterator i = TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance().find(productId);

//	// Not found ?  return the Default Object
//	if (i == TeAbstractFactory<PRODUCT,PARAMS,PRODUCTIDTYPE>::instance().end())
//		return PRODUCT::DefaultObject();

//	// Create an object, based on the input parameters
//		return (*i).second->build(arg);
//}
#endif

