/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2012 University of Bonn. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder 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.
 *
 *    MoleCuilder 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 MoleCuilder.  If not, see .
 */
/*
 * XmlParser.cpp
 *
 *  Created on: Mar 23, 2012
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include 
#include 
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Verbose.hpp"
#include "XmlParser.hpp"
#include "Atom/atom.hpp"
#include "Box.hpp"
#include "Element/element.hpp"
#include "Element/periodentafel.hpp"
#include "molecule.hpp"
#include "MoleculeListClass.hpp"
#include "World.hpp"
#include "Parser/pugixml/pugixml.hpp"
// static instances
FormatParser< xml >::additionalAtomInfo FormatParser< xml >::defaultAtomInfo;
// declare specialized static variables
const std::string FormatParserTrait::name = "xml";
const std::string FormatParserTrait::suffix = "xml";
const ParserTypes FormatParserTrait::type = xml;
const char *box_axis[NDIM] = { "box_a", "box_b", "box_c" };
/**
 * Constructor.
 */
FormatParser< xml >::FormatParser() :
  FormatParser_common(NULL)
{}
/**
 * Destructor.
 */
FormatParser< xml >::~FormatParser()
{}
Vector toVector(const std::string &value)
{
  std::stringstream inputstream(value);
  Vector returnVector;
  for (size_t i=0;i> std::setprecision(16) >> returnVector[i];
  return returnVector;
}
double toDouble(const std::string &value)
{
  std::stringstream inputstream(value);
  double returnDouble;
  inputstream >> std::setprecision(16) >> returnDouble;
  return returnDouble;
}
std::string fromVector(const Vector&a)
{
  std::stringstream returnstring;
  for (size_t i=0;i::load(std::istream* file)
{
  // create the molecule
  molecule * const newmol = World::getInstance().createMolecule();
  newmol->ActiveFlag = true;
  // TODO: Remove the insertion into molecule when saving does not depend on them anymore. Also, remove molecule.hpp include
  World::getInstance().getMolecules()->insert(newmol);
  // load file into xml tree
  pugi::xml_document doc;
  doc.load(*file);
  // header
  pugi::xml_node scafacos_test = doc.root().child("scafacos_test");
  data.name = toString(scafacos_test.attribute("name").value());
  data.description = toString(scafacos_test.attribute("description").value());
  data.reference_method = toString(scafacos_test.attribute("reference_method").value());
  data.error_potential = scafacos_test.attribute("error_potential").as_double();
  data.error_field = scafacos_test.attribute("error_field").as_double();
  LOG(1, "INFO: scafacos_test.name is '" << data.name << "'.");
  newmol->setName(data.name);
  // configuration information
  pugi::xml_node configuration = scafacos_test.child("configuration");
  data.config.offset = toVector(configuration.attribute("offset").value());
  for (size_t i=0; i::load() - periodicity attribute has less than 3 entries.");
      inputstream >> data.config.periodicity[i];
    }
  }
  data.config.epsilon = toString(configuration.attribute("epsilon").value());
  // particles
  for(pugi::xml_node::iterator iter = configuration.begin();
      iter != configuration.end(); ++iter) {
    struct scafacos::configuration::particle p;
    p.position = toVector((*iter).attribute("position").value());
    p.q = toDouble((*iter).attribute("q").value());
    p.potential = toDouble((*iter).attribute("potential").value());
    p.field = toVector((*iter).attribute("field").value());
    data.config.p.push_back(p);
    LOG(2, "DEBUG: Parsing particle at " << p.position << ".");
    atom * const newAtom = World::getInstance().createAtom();
    // for the moment each becomes a hydrogen
    newAtom->setType(World::getInstance().getPeriode()->FindElement((atomicNumber_t)1));
    newAtom->setPosition(p.position);
    newAtom->setCharge(p.q);
    newmol->AddAtom(newAtom);
    additionalAtomInfo atomInfo(p.q, p.potential, p.field);
#ifndef NDEBUG
    std::pair inserter =
#endif
    additionalAtomData.insert( std::make_pair(newAtom->getId(), atomInfo) );
    ASSERT( inserter.second,
        "FormatParser< xml >::load() - atomInfo entry for atom "+toString(newAtom->getId())
        +" already present.");
  }
  BOOST_FOREACH(atom *_atom, World::getInstance().getAllAtoms())
    LOG(3, "INFO: Atom " << _atom->getName() << " " << *dynamic_cast(_atom) << ".");
  // refresh atom::nr and atom::name
  newmol->getAtomCount();
}
/**
 * Saves the \a atoms into as a XYZ file.
 *
 * \param file where to save the state
 * \param atoms atoms to store
 */
void FormatParser< xml >::save(std::ostream* file, const std::vector &atoms) {
  LOG(0, "Saving changes to xml.");
  // fill the structure with updated information
  const Box &domain = World::getInstance().getDomain();
  data.config.box = domain.getM();
  for (size_t i=0;i::const_iterator it = atoms.begin(); it != atoms.end(); it++) {
    struct scafacos::configuration::particle p;
    const additionalAtomInfo &atomInfo = getAtomData(*(*it));
    p.position = (*it)->getPosition();
    p.q = (*it)->getCharge();
    p.potential = atomInfo.potential;
    p.field = atomInfo.field;
    data.config.p.push_back(p);
  }
  // create the xml tree
  pugi::xml_document doc;
  pugi::xml_attribute attr;
  // header
  pugi::xml_node xml_scafacos_test = doc.root().append_child();
  xml_scafacos_test.set_name("scafacos_test");
  xml_scafacos_test.append_attribute("name").set_value(data.name.c_str());
  xml_scafacos_test.append_attribute("description").set_value(data.description.c_str());
  xml_scafacos_test.append_attribute("reference_method").set_value(data.reference_method.c_str());
  xml_scafacos_test.append_attribute("error_potential").set_value(data.error_potential);
  xml_scafacos_test.append_attribute("error_field").set_value(data.error_field);
  // configuration
  pugi::xml_node xml_configuration = xml_scafacos_test.append_child();
  xml_configuration.set_name("configuration");
  xml_configuration.append_attribute("offset").set_value(fromVector(data.config.offset).c_str());
  for (size_t i=0; i::const_iterator iter = data.config.p.begin();
      iter != data.config.p.end();++iter) {
    pugi::xml_node particle = xml_configuration.append_child();
    particle.set_name("particle");
    particle.append_attribute("position").set_value(fromVector((*iter).position).c_str());
    particle.append_attribute("q").set_value(fromDouble((*iter).q).c_str());
    particle.append_attribute("potential").set_value(fromDouble((*iter).potential).c_str());
    particle.append_attribute("field").set_value(fromVector((*iter).field).c_str());
  }
  // print standard header and save without declaration
  *file << "\n";
  *file << "\n";
  doc.save(*file, "\t", pugi::format_no_declaration, pugi::encoding_utf8);
}
/** Observer callback when new atom is added to World.
 *
 * @param id of atom
 */
void FormatParser< xml >::AtomInserted(atomId_t id)
{
  std::map::iterator iter = additionalAtomData.find(id);
  ASSERT(iter == additionalAtomData.end(),
      "FormatParser< xml >::AtomInserted() - additionalAtomData already present for newly added atom "
      +toString(id)+".");
  // additionalAtomData.insert( std::make_pair(id, additionalAtomInfo()) );
}
/** Remove additional AtomData info, when atom has been removed from World.
 *
 * @param id of atom
 */
void FormatParser< xml >::AtomRemoved(atomId_t id)
{
  std::map::iterator iter = additionalAtomData.find(id);
  // as we do not insert AtomData on AtomInserted, we cannot be assured of its presence
//  ASSERT(iter != additionalAtomData.end(),
//      "FormatParser< tremolo >::AtomRemoved() - additionalAtomData is not present for atom "
//      +toString(id)+" to remove.");
  if (iter != additionalAtomData.end())
    additionalAtomData.erase(iter);
}
const FormatParser< xml >::additionalAtomInfo& FormatParser< xml >::getAtomData(const atom &_atom) const
{
  {
    // has its own entry?
    AtomInfoMap_t::const_iterator iter = additionalAtomData.find(_atom.getId());
    if (iter != additionalAtomData.end()) {
      return iter->second;
    }
  }
  {
    // father has an entry?
    AtomInfoMap_t::const_iterator iter = additionalAtomData.find(_atom.GetTrueFather()->getId());
    if (iter != additionalAtomData.end()) {
      return iter->second;
    }
  }
  return defaultAtomInfo;
}
#define comparator(x,y) if (x != y) { LOG(2, "DEBUG: Mismatch in " << #x << ": " << x << " != " << y); return false; }
#define num_comparator(x,y) if (fabs(x - y) > MYEPSILON) { LOG(2, "DEBUG: Numeric mismatch in " << #x << ": " << x << " != " << y << " by " << fabs(x - y) << "."); return false; }
bool FormatParser< xml >::scafacos::configuration::particle::operator==(const particle &p) const {
  comparator(position, p.position)
  num_comparator(q, p.q)
  num_comparator(potential, p.potential)
  comparator(field, p.field)
  return true;
}
bool FormatParser< xml >::scafacos::configuration::operator==(const configuration &c) const {
  comparator(offset, c.offset)
  comparator(box, c.box)
  for (size_t i=0;i::const_iterator iter = p.begin();
  std::vector::const_iterator citer = c.p.begin();
  for (;iter != p.end(); ++iter, ++citer) {
    if ((*iter) != (*citer))
      return false;
  }
  return true;
}
bool FormatParser< xml >::scafacos::operator==(const scafacos &s) const {
  comparator(name, s.name)
  comparator(description, s.description)
  comparator(reference_method, s.reference_method)
  num_comparator(error_potential, s.error_potential)
  num_comparator(error_field, s.error_field)
  if (config != s.config) {
    return false;
  }
  return true;
}