/*
 * 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 . 
 */
/*
 * PotentialFactory.cpp
 *
 *  Created on: 30.11.2012
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
//#include "CodePatterns/MemDebug.hpp"
#include "PotentialFactory.hpp"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "CodePatterns/Singleton_impl.hpp"
#include "Potentials/SerializablePotential.hpp"
#include "AllPotentialHeaders.hpp"
#include "Potentials/Specifics/PotentialTypes.def"
//!> function to print each member of the sequence in the enumeration
#define sequence_print(z,n,seq) \
  ( \
  BOOST_PP_STRINGIZE( BOOST_PP_SEQ_ELEM(n, seq) ), \
  BOOST_PP_SEQ_ELEM(n, seq) \
  )
//!> function to print each member of the sequence in the enumeration
#define reverse_sequence_print(z,n,seq) \
    ( \
    BOOST_PP_SEQ_ELEM(n, seq), \
    BOOST_PP_STRINGIZE( BOOST_PP_SEQ_ELEM(n, seq) ) \
    )
// static defines
PotentialFactory::NameToType_t PotentialFactory::NameToType =
    boost::assign::map_list_of
#if defined PotentialTypes_END // do we have parameters at all?
#define BOOST_PP_LOCAL_MACRO(n) sequence_print(~, n, POTENTIALSEQUENCE)
#define BOOST_PP_LOCAL_LIMITS  (0, PotentialTypes_END-1)
#include BOOST_PP_LOCAL_ITERATE()
#undef BOOST_PP_LOCAL_MACRO
#undef BOOST_PP_LOCAL_LIMITS
#endif
  ;
PotentialFactory::TypeToName_t PotentialFactory::TypeToName =
    boost::assign::map_list_of
#if defined PotentialTypes_END // do we have parameters at all?
#define BOOST_PP_LOCAL_MACRO(n) reverse_sequence_print(~, n, POTENTIALSEQUENCE)
#define BOOST_PP_LOCAL_LIMITS  (0, PotentialTypes_END-1)
#include BOOST_PP_LOCAL_ITERATE()
#undef BOOST_PP_LOCAL_MACRO
#undef BOOST_PP_LOCAL_LIMITS
#endif
  ;
#undef sequence_print
#undef reverse_sequence_print
#include "Potentials/Specifics/PotentialTypes.undef"
enum PotentialTypes PotentialFactory::getTypeForName(const std::string &_name)
{
  ASSERT (NameToType.count(_name),
      "PotentialFactory::getTypeForName() - cannot find potential of name "+_name);
  return NameToType[_name];
}
const std::string & PotentialFactory::getNameForType(enum PotentialTypes _type)
{
  ASSERT (TypeToName.count(_type),
      "PotentialFactory::getNameForType() - cannot find potential of name "+_type);
  return TypeToName[_type];
}
EmpiricalPotential *PotentialFactory::createInstance(
    const std::string &potentialtype,
    const SerializablePotential::ParticleTypes_t &types) const
{
  switch (getTypeForName(potentialtype)) {
  case constant:
      return new ConstantPotential(types);
  case tersoff:
      return new ManyBodyPotential_Tersoff(types);
  case morse:
      return new PairPotential_Morse(types);
  case harmonic_bond:
      return new PairPotential_Harmonic(types);
  case harmonic_angle:
      return new ThreeBodyPotential_Angle(types);
  case lennardjones:
      return new PairPotential_LennardJones(types);
  case torsion:
      return new FourBodyPotential_Torsion(types);
  case improper:
      return new FourBodyPotential_Improper(types);
  default:
    ASSERT(0, "PotentialFactory::createInstance() - unknown potential desired to create.");
  }
  return NULL;
}
EmpiricalPotential* PotentialFactory::getDefaultPotential(const std::string &_name) const
{
  EmpiricalPotential *potential = NULL;
  switch (getTypeForName(_name)) {
  case constant:
    potential = new ConstantPotential();
    break;
  case tersoff:
    potential = new ManyBodyPotential_Tersoff();
    break;
  case morse:
    potential = new PairPotential_Morse();
    break;
  case harmonic_bond:
    potential = new PairPotential_Harmonic();
    break;
  case harmonic_angle:
    potential = new ThreeBodyPotential_Angle();
    break;
  case lennardjones:
    potential = new PairPotential_LennardJones();
    break;
  case torsion:
    potential = new FourBodyPotential_Torsion();
    break;
  case improper:
    potential = new FourBodyPotential_Improper();
    break;
  default:
    ASSERT(0, "PotentialFactory::createInstance() - unknown potential desired to create.");
    break;
  }
  return potential;
}
EmpiricalPotential const * const PotentialFactory::getDefaultPotential(const enum PotentialTypes &_type) const {
  return const_cast(getDefaultPotential(getNameForType(_type)));
}
CONSTRUCT_SINGLETON(PotentialFactory)