/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2012 University of Bonn. All rights reserved.
 * Copyright (C)  2013 Frederik Heber. 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"
SerializablePotential::SerializablePotential() :
  ParticleTypes()
{}
std::ostream& operator<<(std::ostream &ost, const SerializablePotential &potential)
{
  potential.stream_to(ost);
  return ost;
}
void SerializablePotential::stream_to(std::ostream &ost) const
{
  // check stream
  if (ost.bad())
    throw SerializablePotentialException();
  /// print parameter key
  ost << getToken() << ":";
  /// print associated particles
  const SerializablePotential::ParticleTypes_t &types = 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 = getParameterNames();
  const SerializablePotential::parameters_t ¶ms = getParameters();
  SerializablePotential::ParameterNames_t::const_iterator nameiter = paramNames.begin();
  SerializablePotential::parameters_t::const_iterator valueiter = params.begin();
  if (!params.empty()) {
    if (!types.empty())
      ost << ",";
    ost << "\t" << *nameiter << "=" << *valueiter;
    for (++valueiter, ++nameiter; valueiter != params.end(); ++valueiter, ++nameiter) {
      ASSERT( nameiter != paramNames.end(),
          "SerializablePotential::operator<<() - there are less names than parameters.");
      if (*nameiter != std::string(""))
        ost << ",\t" << *nameiter << "=" << *valueiter;
    }
  }
  /// print terminating semi-colon
  ost << ";";
}
std::istream& operator>>(std::istream &ist, SerializablePotential &potential)
{
  potential.stream_from(ist);
  return ist;
}
void SerializablePotential::stream_from(std::istream &ist)
{
  // check stream
  if (ist.bad())
    throw SerializablePotentialException();
  // create copy of current parameters as line may contain not all required
  SerializablePotential::parameters_t params(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-strBegin) != getToken()))
    throw SerializablePotentialMissingValueException()
        << SerializablePotentialKey(getName());
  // tokenize by ","
  typedef boost::tokenizer > tokenizer;
  boost::char_separator pairsep(",\t ;");
  std::string remainderstring(linestring.substr(colonpos+1));
  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;
    const size_t equalitypos = keyvalue.find("=");
    const std::string key = keyvalue.substr(0,equalitypos);
    const std::string value = keyvalue.substr(equalitypos+1);
    /// parse the particle_types
    const std::string typetoken("particle_type");
    const size_t pos = key.find(typetoken);
    if (pos != std::string::npos) {
      // split of type and convert rest to index
      const size_t indexpos = pos+typetoken.length();
      const std::string &indexstring = key.substr(indexpos);
      const size_t index = ConvertToIndex(indexstring);
      if(index == 0)
          throw SerializablePotentialMissingValueException() << SerializablePotentialKey(key);
      // and set the type
      if (equalitypos == std::string::npos)
        throw SerializablePotentialMissingValueException() << SerializablePotentialKey(key);
      setParticleType(index-1, ConvertToParticleType(value));
    } else {
      const size_t index = getParameterIndex(key);
      // parse the coefficients
      if (index != (size_t)-1) {
        if (equalitypos == std::string::npos)
          throw SerializablePotentialMissingValueException() << SerializablePotentialKey(key);
        params[index] = ConvertToValue(value);
      } else {
        throw SerializablePotentialIllegalKeyException() << SerializablePotentialKey(key);
      }
    }
  }
  /// set the new paremeters
  setParameters(params);
}
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;
}