#ifndef XML_OBJECT_BASE_H
#define XML_OBJECT_BASE_H

// The base xml-handling object.
// associated to its own xml node if its xmlName!="". Otherwise it is associated to its father node.

#include <xml_misc_tools.h>
#include <xml_parameters.h>
#include<sstream>

struct xmlObject_base
{

public:

  //****************************  NAMES , AND HANDLING OF RECURSIVE INCLUSIONS :

  std::string xmlName;

  xmlObject_base* fatherSpace;		//never used for the moment, but well, who knows...
  vector<xmlObject_base*> childSpace;	//all effective xmlObjects created by THIS xmlObject
  vector<xmlObject_base*> toDelete;	//slightly diferent: all those that need to be destroyed by THIS destructor
  void add_childSpace( xmlObject_base * newChild );
  void add_toDelete( xmlObject_base * newObj );
  void delete_recursive ();

  void* wrapped_noType;	//The (void*) address of the future wrapped object (in xmlObject< whatever > )
  
  
  //****************************  CONSTRUCTORS, DESTRUCTORS :

  xmlObject_base();
  xmlObject_base( string name );
  virtual ~xmlObject_base();


  //****************************  LOADING, SAVING :

  //The loading and saving paradigms:
  void save_XML(xmlpp::Node* father_node);
  void load_XML(xmlpp::Node* father_node);

  //sub-routines, used by the two preceding functions:

  xmlpp::Element* make_child_Element (xmlpp::Node* father_node);
  virtual list<xmlpp::Element*> get_load_List (xmlpp::Node* father_node);

  virtual void save_Element (xmlpp::Element* the_element);
  virtual void load_Element (xmlpp::Element* the_element);

  bool isLoaded;


  //***************************** BASE TYPE PARAMETERS, HANDLED AS ATTRIBUTES OF THE XML NODE:

  struct paramHandler_base
  {
    paramHandler_base(){}
    virtual ~paramHandler_base(){}
    virtual void save_Attributes(xmlpp::Element* the_element) = 0;
    virtual void load_Attributes(list<xmlpp::Attribute*> & the_attributes) = 0;
  };

  template<typename TYPE>
  struct paramHandler : public paramHandler_base
  {
    map<string , TYPE *> registery;
    paramHandler(){}
    ~paramHandler(){}
    void save_Attributes(xmlpp::Element* the_element);
    void load_Attributes(list<xmlpp::Attribute*> & the_attributes);
  };

  typedef map< const type_info * , paramHandler_base* > TypeMap;
  TypeMap my_paramHandlers;

  template<typename TYPE>
  void add_param(TYPE & newParam, string paramName);


  //****************************** STATIC VARIABLES:

  //The very important reverseMap, useful for all global functions!
  static std::map< void * , xmlObject_base * > reverseMap;

  static vector<xmlObject_base*> static_toDelete;
  static void static_Delete();

private:  //non copyable
  xmlObject_base(const xmlObject_base & yo){}

};



// *******************  AND HERE COMES THE CORRESPONDING TEMPLATE CODE ! *****************
// ***************************************************************************************


// ************************ Class xmlObject_base::paramHandler<TYPE>


template<typename TYPE>
void xmlObject_base::paramHandler<TYPE>::save_Attributes(xmlpp::Element* the_element)
{
  //ENTERING_NODE(xmlObject_base::paramHandler<TYPE>::save_Attributes, the_element)
  for(typename map<string,TYPE*>::iterator it=registery.begin(); it!=registery.end(); ++it)
  {
    ostringstream ost;
    ost<<*(it->second);
    the_element->set_attribute(it->first,ost.str());
  }
}


template<typename TYPE>
void xmlObject_base::paramHandler<TYPE>::load_Attributes ( list<xmlpp::Attribute*> & the_attributes )
{
  //ENTERING(xmlObject_base::paramHandler<TYPE>::load_Attributes)
  for( typename map<string,TYPE*>::iterator it = registery.begin() ; it!=registery.end() ; ++it )
    for (typename list<xmlpp::Attribute*>::iterator ut=the_attributes.begin(); ut!=the_attributes.end(); )
    {
      bool atNext=false;

      if( (*ut)->get_name() == it->first )
      {
        istringstream ist;
        ist.str((*ut)->get_value());
        ist>>*(it->second);
        //removing (*ut) from the list, so as to be able to check for useless attributes and emit a WARNING.
        ut = the_attributes.erase ( ut );
        atNext=true;
      }
      if(!atNext) ut++;
    }
}


//Specialization of load, for strings, to resolve the formatting problem :

template<>
struct xmlObject_base::paramHandler<string> : public paramHandler_base
{
  //have to rewrite that one although it's unchanged :-) (had linking problem if redefining only the changed function)
  map<string , string *> registery;
  paramHandler(){}
  ~paramHandler(){}

  void save_Attributes(xmlpp::Element* the_element)
  {
    //ENTERING_NODE(xmlObject_base::paramHandler<string>::save_Attributes, the_element)
    for(map<string,string*>::iterator it=registery.begin(); it!=registery.end(); ++it)
    {
      ostringstream ost;
      ost<<*(it->second);
      the_element->set_attribute(it->first,ost.str());
    }
  }

  //Now here is what is changed:
  void load_Attributes(list<xmlpp::Attribute*> & the_attributes )
  {
    //ENTERING(xmlObject_base::paramHandler<TYPE>::load_Attributes)
    for( map<string, string*>::iterator it = registery.begin() ; it!=registery.end() ; ++it )
      for ( list<xmlpp::Attribute*>::iterator ut=the_attributes.begin(); ut!=the_attributes.end(); )
      {
        bool atNext=false;

        if( (*ut)->get_name() == it->first )
        {
          istringstream ist;
          ist.str((*ut)->get_value());
          *(it->second)=ist.str();
          //removing (*ut) from the list, so as to be able to check for useless attributes and emit a WARNING.
          ut = the_attributes.erase ( ut );
          atNext=true;
        }
        if(!atNext) ut++;
      }
  }
};

// ************************ Class xmlObject_base


template<typename TYPE>
void xmlObject_base::add_param(TYPE & newParam, string paramName)
{
  //ENTERING(xmlObject_base::add_param)
  //const type_info & typ = typeid(*this);
  //cerr<<"IN TYPE"<<typ.name()<<endl;
  //creating my_handler_of_TYPE if it does not exist yet...
  if (my_paramHandlers.find( & typeid(newParam) ) == my_paramHandlers.end())
  {
    //cerr<<"Registered type, "<<typeid(newParam).name()<<", DOES NOT already exist!"<<endl;
    my_paramHandlers[& typeid(newParam)] = new paramHandler<TYPE>();
  }

  //adding newParam in my_handler_of_TYPE
  paramHandler<TYPE> * my_handler_of_TYPE = dynamic_cast< paramHandler<TYPE>* > (my_paramHandlers[& typeid(newParam)]);
  if ( my_handler_of_TYPE->registery.find(paramName) != my_handler_of_TYPE->registery.end() )
  {
    cerr<<"WARNING in xmlObject_base::add_param() : Trying to store an already created parameter name : "<<paramName<<". But... OK! Old value indicated by the name is now forgotten."<<endl;
  }
  my_handler_of_TYPE->registery[paramName] = &newParam;
}




#endif
