//------------------------------------------------------------------------------
// This file is part of the OpenStructure project <www.openstructure.org>
//
// Copyright (C) 2008-2020 by the OpenStructure authors
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 3.0 of the License, or (at your option)
// any later version.
// This library 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 Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//------------------------------------------------------------------------------

#ifndef OST_MM_SETTINGS_HH
#define OST_MM_SETTINGS_HH

/*
  DO NOT EDIT, automatically generated by CMake
*/

#include <vector>

#include <boost/shared_ptr.hpp>
#include <limits>
#include <ost/mol/residue_handle.hh>
#include <ost/mol/mm/forcefield.hh>
#include <ost/platform.hh>
#include <boost/filesystem/path.hpp>
#include <ost/log.hh>

namespace OpenMM{
  class Integrator;//hacky way of telling the Integrator us around
                   //will be included in source file to avoid
                   //dependencies on external libraries
}

namespace ost { namespace mol{ namespace mm{

struct Settings;
class TerminiExceptions;
typedef boost::shared_ptr<Settings> SettingsPtr;
typedef boost::shared_ptr<TerminiExceptions> TerminiExceptionsPtr;

enum Platform{
  Reference,
  OpenCL,
  CUDA,
  CPU
};

enum NonbondedMethod{
  NoCutoff,
  CutoffNonPeriodic,
  CutoffPeriodic,
  Ewald,
  PME
};

class TerminiExceptions{

public:

  TerminiExceptions(){ }

  void SetException(const ost::mol::ResidueHandle& res, const String& exception_name){
    exceptions_[res.GetHashCode()] =  exception_name;
  }

  bool HasException(const ost::mol::ResidueHandle& res) const{
    return exceptions_.find(res.GetHashCode()) != exceptions_.end();
  }

  String GetException(const ost::mol::ResidueHandle& res) const {
    if(!this->HasException(res)){
      std::stringstream ss;
      ss<<"Tried to get termini exceptions of res "<<res<<" without defined exception!";
      throw ost::Error(ss.str());
    }
    std::map<unsigned long, String>::const_iterator i = exceptions_.find(res.GetHashCode());
    return i->second;
  }
private:
  std::map<unsigned long, String> exceptions_;

};

typedef std::map<String,String> PropertyMap;

struct Settings{

  Settings():   add_bonds(true),
                add_angles(true),
                add_dihedrals(true),
                add_impropers(true),
                add_cmaps(true),
                add_exclusions(true),
                add_nonbonded(true),
                add_gbsa(false),
                constrain_hbonds(false),
                constrain_bonds(false),
                rigid_water(false),
                strict_interactions(true),
                ideal_bond_length_constraints(true),
                fix_heavy_atoms(false),
                kill_electrostatics(false),
                generate_disulfid_bonds(true),
                nonbonded_method(NoCutoff),
                nonbonded_cutoff(10.0),
                remove_cmm_motion(true),
                cmm_frequency(1),
                periodic_box_extents(0,0,0),
                init_temperature(0.0),
                forcefield(), //note, that we don't create an empty forcefield at this point =>force the user
                              //to assign a forcefield
                termini_exceptions(new TerminiExceptions),
                platform(Reference),
                reference_properties(),
                cpu_properties(),
                opencl_properties(),
                cuda_properties(),
                add_thermostat(false),
                thermostat_temperature(std::numeric_limits<Real>::quiet_NaN()),
                thermostat_collision_frequency(std::numeric_limits<Real>::quiet_NaN()),
                add_barostat(false),
                barostat_temperature(std::numeric_limits<Real>::quiet_NaN()),
                barostat_pressure(std::numeric_limits<Real>::quiet_NaN()),
                barostat_frequency(25),
                integrator(),
                solvent_dielectric(78.3), //this is for GBSA
                solute_dielectric(1.0), //this is for GBSA
                reaction_field_dielectric(78.3),
                use_dispersion_correction(true),
                keep_ff_specific_naming(true),
                openmm_plugin_directory("/opt/conda/conda-bld/openstructure_1750079707958/_h_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold/lib/plugins") {

    try {
      // try to guess custom_plugin_directory
      boost::filesystem::path shared_path(GetSharedDataPath());
      custom_plugin_directory = (shared_path / "openmm_plugins").string();
    } catch (std::runtime_error& e) {
      // fallback to OpenMM path (used to be default in OST before 1.7)
      LOG_WARNING("Failed to find shared data path for openmm_plugins. "
                  "Please set custom_plugin_directory of Settings object "
                  "manually or ensure that OST_ROOT is set properly. "
                  "Caught exception: " << e.what());
      custom_plugin_directory = openmm_plugin_directory;
    }

  }

  //
  bool add_bonds;
  bool add_angles;
  bool add_dihedrals;
  bool add_impropers;
  bool add_cmaps;
  bool add_exclusions;
  bool add_nonbonded;
  bool add_gbsa;
  bool constrain_hbonds;
  bool constrain_bonds;
  bool rigid_water;
  bool strict_interactions;
  bool ideal_bond_length_constraints;
  bool fix_heavy_atoms;
  //set all charges to zero
  bool kill_electrostatics;
  //automatically generate disulfid bonds
  bool generate_disulfid_bonds;
  //see export_openmm.cc for all possibilities
  NonbondedMethod nonbonded_method;
  Real nonbonded_cutoff;
  bool remove_cmm_motion;
  int cmm_frequency;
  //currently only boxes defined by base vectors are supported 
  geom::Vec3 periodic_box_extents;
  //at the beginning of the simulation, the initial velociities are
  //randomly set according to a boltzmann distribution
  Real init_temperature;
  ForcefieldPtr forcefield;
  TerminiExceptionsPtr termini_exceptions;
  Platform platform;
  PropertyMap reference_properties;
  PropertyMap cpu_properties;
  PropertyMap opencl_properties;
  PropertyMap cuda_properties;
  bool add_thermostat;
  Real thermostat_temperature;
  Real thermostat_collision_frequency;
  bool add_barostat;
  Real barostat_temperature;
  Real barostat_pressure;
  int barostat_frequency;
  boost::shared_ptr<OpenMM::Integrator> integrator;
  Real solvent_dielectric;
  Real solute_dielectric;
  Real reaction_field_dielectric;
  bool use_dispersion_correction;
  bool keep_ff_specific_naming;
  String openmm_plugin_directory;
  String custom_plugin_directory;
};

}}} //namespace

#endif
