#ifndef XML_FACTORY_H
#define XML_FACTORY_H

#include <typeinfo>			//AARGHH!
#include <map>


//Inspired by an object factory by Robert Geiman : http://www.gamedev.net/reference/articles/article2097.asp
//(listing 'number3')

//-Added possibilities:

//- An obligation to have only ONE std::string for each derived_type entered in the factory.
//- A reflexive map giving the std::string associated to each registered derived_type entered in the factory.
//- For the factory to fully work, the types it handles must inherit from class xmlClass. This enables the xmlParameters saving function to find back the xml_name of an object when saving it, even if the object was not created by the factory. However, the factory also works without this trick.


template<typename XMLIZED, typename derivedCLASS>
XMLIZED* CreateObject()
{
  return new derivedCLASS();
}


template<typename XMLIZED>
class xmlFactory
{
protected:
  typedef XMLIZED* (*CreateObjectFunc)(void);

  std::map< std::string , CreateObjectFunc > m_object_creator;
  std::map< std::string , const type_info * > m_object_type_info;
  std::map< const type_info * , std::string > m_object_type_info_reverse;

public:

  // Variables and functions allowing the factory to be SHARED by different xmlObject<XMLIZED*> objects.
  int sharedBy;

  static void assign(xmlFactory<XMLIZED> * & newFact, xmlFactory<XMLIZED> * const & oldFact)
  {
    newFact=oldFact;
    oldFact->sharedBy += 1;
  }

  static void remove_occurence(xmlFactory<XMLIZED> * & theFact)
  {
    if(theFact->sharedBy==1) delete theFact;
    else theFact->sharedBy-=1 ;
  }

xmlFactory() : sharedBy(1)	{}


  //OK, now come the real registration functions:

  template<typename derivedCLASS>
  bool Register(std::string unique_id)
  {
    //cerr<<"Registerrrring"<<endl;
    if (m_object_creator.find(unique_id) != m_object_creator.end())
      return false;

    m_object_creator[unique_id] = &CreateObject<XMLIZED, derivedCLASS>;

    //Check this! We store the type info of a POINTER on the derivedCLASS, so as not to create any useless object!
    derivedCLASS * fakePoint;
    m_object_type_info[unique_id] = & typeid(fakePoint) ;
    m_object_type_info_reverse[ & typeid(fakePoint)] = unique_id ;
    //cerr<<"REGISTERED TYPE: "<<typeid(fakePoint).name()<<endl;

    return true;
  }


  bool Unregister(std::string unique_id)
  {
    if (m_object_creator.erase(unique_id) == 1)
    {
      m_object_type_info_reverse.erase( m_object_type_info.find( unique_id )->second );
      m_object_type_info.erase( unique_id );
      return true;
    }
    else return false;
  }


  XMLIZED* Create(std::string unique_id)
  {
    typename std::map< std::string , CreateObjectFunc >::iterator iter = m_object_creator.find(unique_id);
    if (iter == m_object_creator.end())
      return NULL;
    return ((*iter).second());
  }

  std::string GetId (XMLIZED * objAddress)
  {
    if(objAddress)
    {
      //For this function to do its job , XMLIZED must inherit from the class xmlClass !!
      const type_info * ti = & xmlObject<XMLIZED>::pointer_typeid( objAddress );
      //cerr<<"IIIIIIIIIIIIIIi FOUND POINTER TYPE: "<<ti->name()<<endl;

      typename std::map< const type_info * , std::string >::iterator iter = m_object_type_info_reverse.find( ti );

      if (iter != m_object_type_info_reverse.end())
        return ((*iter).second);
    }

    return "not-registered-in-factory";
  }


  bool isRegistered(std::string unique_id)
  {
    return (m_object_creator.find(unique_id) != m_object_creator.end());
  }



};

#endif


