/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2013 Frederik Heber. 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 .
 */
/*
 * Particle.cpp
 *
 *  Created on: May 13, 2013
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "Particle.hpp"
#include 
#include 
#include 
#include 
#include 
#include 
#include "CodePatterns/Assert.hpp"
#include "Element/element.hpp"
#include "Element/periodentafel.hpp"
#include "Potentials/Exceptions.hpp"
#include "Potentials/Particles/ParticleRegistry.hpp"
using namespace boost::assign;
std::vector getParameterNames()
{
  std::vector tempnames;
  tempnames += "particle_type", "element_name", "sigma", "epsilon", "sigma14", "epsilon14", "mass", "free", "charge";
  return tempnames;
}
const std::vector Particle::ParameterNames = getParameterNames();
Particle::Particle(
    const periodentafel &_periode,
    const std::string &_token,
    const atomicNumber_t &_number) :
    name(_token),
    periode(_periode),
    charge(0.),
    mass(0.),
    dof(3),
    atomic_number(_number),
    sigma(0.),
    epsilon(0.),
    sigma_14(0.),
    epsilon_14(0.)
{
  ParticleRegistry::getInstance().registerInstance(this);
}
std::string Particle::findFreeName(
    const periodentafel &_periode,
    const atomicNumber_t &_number) const
{
  const element *e = _periode.FindElement(_number);
  ASSERT (e != NULL,
      "Particle::findFreeName() - cannot find element with number "
      +toString(_number)+".");
  bool FoundFlag = false;
  size_t index = 1;
  std::string returnname;
  while(!FoundFlag) {
    returnname = e->getSymbol()+toString(index);
    FoundFlag = !ParticleRegistry::getInstance().isPresentByName(returnname);
  }
  return returnname;
}
Particle::Particle(
    const periodentafel &_periode,
    const atomicNumber_t &_number) :
    name(findFreeName(_periode, _number)),
    periode(_periode),
    charge(0.),
    mass(0.),
    dof(3),
    atomic_number(_number),
    sigma(0.),
    epsilon(0.),
    sigma_14(0.),
    epsilon_14(0.)
{
  ParticleRegistry::getInstance().registerInstance(this);
}
void Particle::stream_to(std::ostream &ost) const
{
  // check stream
  if (ost.bad())
    throw SerializablePotentialException();
  /// print parameter key
  ost << "\tparticle:";
  /// print name and values
  ost << "\tparticle_type=" << getName();
  ost << ",\telement_name=" << getElement();
  ost << ",\tsigma=" << sigma;
  ost << ",\tepsilon=" << epsilon;
  ost << ",\tsigma14=" << sigma_14;
  ost << ",\tepsilon14=" << epsilon_14;
  ost << ",\tmass=" << mass;
  ost << ",\tfree=" << dof;
  ost << ",\tcharge=" << charge;
  ost << std::endl;
}
size_t Particle::lookupParameterName(const std::string &name) const
{
  std::vector::const_iterator iter =
      std::find(ParameterNames.begin(), ParameterNames.end(), name);
  if (iter == ParameterNames.end())
    return -1;
  else
    return std::distance(ParameterNames.begin(), iter);
}
void Particle::stream_from(std::istream &ist)
{
  // check stream
  if (ist.bad())
    throw SerializablePotentialException();
  // 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) != std::string("particle")))
    throw SerializablePotentialMissingValueException()
        << SerializablePotentialKey(std::string("particle"));
  // tokenize by ","
  typedef boost::tokenizer > tokenizer;
  boost::char_separator pairsep(",\t ;");
  boost::char_separator keyvaluesep("=");
  std::string remainderstring(linestring.substr(colonpos+1));
  tokenizer tokens(remainderstring, pairsep); //skip colon
  // step through each token
  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;
    const std::string &value = *keyvalue_iter;
    const size_t index = lookupParameterName(key);
    switch((parameters_t)index) {
    case e_particle_type:
      const_cast(name) = value;
      break;
    case e_element_name:
      setElement(value);
      break;
    case e_sigma:
      sigma = boost::lexical_cast(value);
      break;
    case e_epsilon:
      epsilon = boost::lexical_cast(value);
      break;
    case e_sigma_14:
      sigma_14 = boost::lexical_cast(value);
      break;
    case e_epsilon_14:
      epsilon_14 = boost::lexical_cast(value);
      break;
    case e_mass:
      mass = boost::lexical_cast(value);
      break;
    case e_free:
      dof = boost::lexical_cast(value);
      break;
    case e_charge:
      charge = boost::lexical_cast(value);
      break;
    default:
      ASSERT(0, "Particle::stream_from() - unknown index "
          +toString(index)+".");
      break;
    }
  }
}
std::ostream& operator<<(std::ostream &ost, const Particle &p)
{
  p.stream_to(ost);
  return ost;
}
std::string Particle::getElement() const
{
  std::string returnstring;
  const element *e = periode.FindElement(atomic_number);
  if (e != NULL)
    returnstring = e->getSymbol();
  return returnstring;
}
void Particle::setElement(const std::string &element_name)
{
  const element *e = periode.FindElement(element_name);
  if (e != NULL)
    atomic_number = e->getAtomicNumber();
}