/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2012 University of Bonn. All rights reserved.
 * Please see the COPYING file or "Copyright notice" in builder.cpp for details.
 * 
 *
 *   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 . 
 */
/*
 * SerializablePotential.cpp
 *
 *  Created on: 23.11.2012
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "SerializablePotential.hpp"
#include 
#include 
#include 
#include 
#include 
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/toString.hpp"
#include "Potentials/Exceptions.hpp"
std::ostream& operator<<(std::ostream &ost, const SerializablePotential &potential)
{
  // check stream
  if (ost.bad())
    throw SerializablePotentialException();
  /// print parameter key
  ost << potential.getToken() << ":";
  /// print associated particles
  const SerializablePotential::ParticleTypes_t &types = potential.getParticleTypes();
  for (size_t index=0; index < types.size(); ++index) {
    ost << "\tparticle_type" << index+1 << "=" << types[index];
    ost << (index != (types.size()-1) ? std::string(",") : std::string(""));
  }
  /// print coefficients
  const SerializablePotential::ParameterNames_t ¶mNames = potential.getParameterNames();
  const SerializablePotential::parameters_t ¶ms = potential.getParameters();
  SerializablePotential::ParameterNames_t::const_iterator nameiter = paramNames.begin();
  SerializablePotential::parameters_t::const_iterator valueiter = params.begin();
  for (; valueiter != params.end(); ++valueiter, ++nameiter) {
    ASSERT( nameiter != paramNames.end(),
        "ManyBodyPotential_Tersoff::operator<<() - there are less names than parameters.");
    if (*nameiter != std::string(""))
      ost << ",\t" << *nameiter << "=" << *valueiter;
  }
  /// print terminating semi-colon
  ost << ";";
  return ost;
}
std::istream& operator>>(std::istream &ist, SerializablePotential &potential)
{
  // check stream
  if (ist.bad())
    throw SerializablePotentialException();
  // create copy of current parameters, hence line may contain not all required
  SerializablePotential::parameters_t params(potential.getParameters());
  // read in full line
  std::string linestring;
  getline(ist, linestring);
  const std::string whitespace(" \t");
  const size_t strBegin = linestring.find_first_not_of(whitespace);
  const size_t colonpos = linestring.find(":");
  if ((strBegin == std::string::npos) || (colonpos == std::string::npos) ||
      (linestring.substr(strBegin, colonpos-1) != potential.getToken()))
    throw SerializablePotentialMissingValueException()
        << SerializablePotentialKey(potential.getName());
  // tokenize by ","
  typedef boost::tokenizer > tokenizer;
  boost::char_separator pairsep(",\t ;");
  boost::char_separator keyvaluesep("=");
  std::string remainderstring(linestring.substr(colonpos+1));
//  {
//    std::stringstream remainderstream
//    remainderstream >> std::ws >> remainderstring;
//  }
  tokenizer tokens(remainderstring, pairsep); //skip colon
  // step through each token
  ConvertTo ConvertToIndex;
  ConvertTo ConvertToValue;
  ConvertTo ConvertToParticleType;
  for (tokenizer::iterator tok_iter = tokens.begin();
    tok_iter != tokens.end(); ++tok_iter) {
    const std::string &keyvalue = *tok_iter;
    tokenizer keyvaluetoken(keyvalue, keyvaluesep);
    tokenizer::iterator keyvalue_iter = keyvaluetoken.begin();
    const std::string &key = *keyvalue_iter;
    /// parse the particle_types
    const size_t pos = key.find("particle_type");
    if (pos != std::string::npos) {
      // split of type and convert rest to index
      const size_t index = ConvertToIndex(key.substr(pos, std::string::npos));
      // and set the type
      if (++keyvalue_iter == keyvaluetoken.end())
        throw SerializablePotentialMissingValueException() << SerializablePotentialKey(key);
      const std::string &value = *keyvalue_iter;
      potential.setParticleType(index, ConvertToParticleType(value));
    } else {
      const size_t index = potential.getParameterIndex(key);
      // parse the coefficients
      if (index != (size_t)-1) {
        if (++keyvalue_iter == keyvaluetoken.end())
          throw SerializablePotentialMissingValueException() << SerializablePotentialKey(key);
        const std::string &value = *keyvalue_iter;
        params[index] = ConvertToValue(value);
      } else {
        throw SerializablePotentialIllegalKeyException() << SerializablePotentialKey(key);
      }
    }
  }
  /// set the new paremeters
  potential.setParameters(params);
  return ist;
}
const size_t SerializablePotential::getParameterIndex(const std::string &_name) const
{
  const ParameterNames_t& ParameterNames = getParameterNames();
  ParameterNames_t::const_iterator iter =
      std::find(ParameterNames.begin(), ParameterNames.end(), _name);
  if (iter == ParameterNames.end())
    return (size_t)-1;
  else
    return std::distance(ParameterNames.begin(), iter);
}
const std::string SerializablePotential::getName() const
{
  std::string returnstring = getToken() + std::string("_");
  BOOST_FOREACH(const ParticleType_t &type, getParticleTypes()) {
    returnstring += toString(type);
  }
  return returnstring;
}