/*
    ChemCon - molecular mechanics and molecular graphics
    Copyright (C) 1998-2002  Alexei Nikitin

    This program 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.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#ifndef FORCE_FIELD_H
#define FORCE_FIELD_H

//fix
#ifdef WIN32
#pragma warning(disable : 4786)
#endif

//#include <functional>
#include <map>
//#include <fstream>


//#include "Log.h"

#ifndef CHEMDEFS_H
#include "ChemDefs.h"//fix remove?
#endif

#ifndef MM_HASH_H
#include "MM_hash.h"
#endif

#include "Fine_type.h"

#ifndef TEXT_H
#include "Text.h"
#endif

#ifdef TESHI
  #ifndef MOT_H
    #include "MOT.h"
  #endif
#endif

//#include <map>

//#include "Defs.h"

namespace MM
{

//class   Force_field;
class   Improper_torsion;
struct  Smooth;

//-------------------- Smooth -----------------------------------------------
/*
struct  Smooth
  {
      double      bond_;
      double      angle_;
      double      torsion_;
      double      impTor_;
      double      VdWaals_;
      double      e_static_;

      Smooth();
  };//*/

//-------------------- Force_field ------------------------------------------

//fix Objects too big to be allocated on steck.

class Force_field {
    mutable bool                    show_user_warning_;
    Text                            name_;
    mutable int                     problem_;
    mutable bool                    success_;
    //mutable std::ofstream           error_file_;

public:
    //bool                            problem () const                            {return problem_;}
    //void                            have_a_problem (bool yes) const             {problem_ = yes;}
    //void                            show_user_warning () const                  {show_user_warning_ = true;}
    void                            start_work (bool warnings) const;
    int                             was_problems () const                       {return problem_;}
    bool                            fail () const                               {return !success_;}


   // static Force_field &    singleton ();//fix remove
    explicit                Force_field ();
    explicit                Force_field (Text const &type);
    void                    init    (Text const & force_field_name);

    Text const& name () const   {return name_;}

    void        save_as (Text const & force_field_name) const;
    Text        save_atom_types (Text const & force_field_name) const;
    Text        save_bond (Text const & force_field_name) const;
    Text        save_angle (Text const & force_field_name) const;
    Text        save_torsional (Text const & force_field_name) const;
    Text        save_improper_torsions (Text const & force_field_name) const;
    Text        save_Van_der_Waals (Text const & force_field_name) const;

//...........................................................................
    
    double                          eps;
    double                          E_Static_Scale, VdW_Scale;
///      Smooth                    smooth;
    Text                            sigma_rule_;
    Text                            epsilon_rule_;

    static double                   ad_hoc_R_star_;
    static double                   ad_hoc_Eps_;

//...........................................................................
    //Rough_hydrophobic_interactions

    double Rm_, l_, Rm_hph_, K_rough_;
//...........................................................................

    double  I_, I_cut_;

    struct  Bonds
    {
        double                      Kr, R_eq;
    }   bond_par[TYPE_N][TYPE_N];

    void  bond (int type1, int type2, double & Kr, double & Req) const;
    void  read_bonds (const char * bonds_file);
    void  clear_bonds ();
//...........................................................................

    struct  Angles
    {
        double                      K, angle_eq;

        Angles ()                   {K = 0; angle_eq = 0;}
        Angles (double k, double a) {K = k; angle_eq = a;}

    };
    Angle_Hash_table <Angles>       Angle_par;

    void  angle (int type1, int type2, int type3,
                 double & K, double & angle_eq) const;
    void  read_angles (const char * angles_file);
//...........................................................................

    struct  Torsions
    {
        int                         Mult, N;
        double                      half_V, Fi0;

        Torsions ()     {Mult = 0; half_V = 0; Fi0 = 0;  N =0;}
        Torsions (int m, double v, double fi, int n)
                        {Mult = m; half_V = v; Fi0 = fi; N =n;}
    };
    Torsion_Hash_table <Array <Torsions> >  Torsion_par;

    const Array <Torsions> * torsion (int type1, int type2,
                                      int type3, int type4) const;
    void  read_torsions (const char * torsions_file);
//...........................................................................

    struct  Imp_Tor
    {
        int                         T1, T2, T3, T4;
        int                         N;
        double                      half_V;
    };
    Array <Imp_Tor>                 Imp_tor_par;

    const Imp_Tor * imp_tor (int type1, int type2, int type3, int type4,
                             int & a1,  int & a2,             int & a4) const;
    void  read_imp_tor (const char * imp_tor_file);
//...........................................................................

    struct  VdWaals
    {
        double                      A, B;
    }                               VdW_par [TYPE_N][TYPE_N];//fix TYPE_N

    struct  CUDA_VdW
    {
        float                       A, B;
    }                               cu_VdW [TYPE_N][TYPE_N];//fix TYPE_N

    struct  R_Eps
    {
        double                      R_star, Eps;
    };
    Array <R_Eps>                   R_eps;

    double                          sigma (int type) const {return 0.89089871814033930474022620559051 * R_eps[type].R_star;}

    void  VdW (int type1, int type2, double & A, double & B) const;
    void  read_VdW            (const char * VdW_file);
    void  read_VdW_exceptions (const char * VdW_file);
    void  clear_VdW ();
//...........................................................................
    //Nonbonded
    //Table_interaction const&  
//...........................................................................

    class Partial_charge
    {
        double                      value_;
        bool                        defined_;
        //int                         mm_type_;

    public:
        explicit                    Partial_charge ()
        :   value_   (0.)
        ,   defined_ (false)
        //,               mm_type_ (Micro_object_type::undefined ())
        {
        }

        explicit                    Partial_charge (double value)
        :   value_   (value)
        ,   defined_ (true)
        //,               mm_type_ (Micro_object_type::undefined ())
        {
        }

        /*explicit                    Partial_charge (Partial_charge const& r)
        :   value_   (r.value_)
        ,   defined_ (r.defined_)
        {
        }//*/

        bool                        was_defined     () const {return defined_;}
        bool                        was_not_defined () const {return !defined_;}
        double                      value      () const {return value_;}
    }; 
    
    //std::map <int, MM::Force_field::Partial_charge> partial_charge_;
    std::map <int, Partial_charge> partial_charge_;

    Partial_charge const &  partial_charge (int mm_type) //const
    {
    //    static Partial_charge result;
        Partial_charge const& result = partial_charge_[mm_type];
        return result;
    }

    void                    load_partial_charges (Text const & file);
    Text                    save_partial_charges (Text const & force_field_name) const;


    class Induction_charge
    {
        double      value_;

    public:
        explicit    Induction_charge () : value_   (12345.) { }
        explicit    Induction_charge (double v) : value_(v) { }

        bool        was_defined     () const {return value_ != 12345.;}
        bool        was_not_defined () const {return value_ == 12345.;}
        double      value      () const {return value_;}
    };     
    std::map <int, Induction_charge> induction_charge_;
    Induction_charge const& induction_charge (int mm_type) //const
    {
        Induction_charge const& result = induction_charge_[mm_type];
        return result;
    }
    void                    read_induction (Text const & file);

//...........................................................................

private:
    Force_field (const Force_field & r);
    const Force_field &     operator = (const Force_field & r);
};


inline
void Force_field::
VdW (int type1, int type2, double & A, double & B) const
{
    const VdWaals & tmp = VdW_par[type1][type2];
    A = tmp.A;
    B = tmp.B;

#ifndef NDEBUG
    if (type1 == Micro_object_type::none ||  //fix
        type1 == Micro_object_type::undefined_type    || 
        type1 == Micro_object_type::any_type          ||
        type2 == Micro_object_type::none ||
        type2 == Micro_object_type::undefined_type    || 
        type2 == Micro_object_type::any_type)
    {
        Text str = "No van der Waals parameters:  ";
        str += ::mot().itoa( type1 );   str += " ";
        str += ::mot().itoa( type2 );   
        
        if (show_user_warning_)
        {
            to_user().warning (str);
            show_user_warning_ = false;
        }
    }
#endif

#ifdef TESHI
    if (A == 0)
    {
        char buf[128];
        strcpy (buf, MOT.itoa (type1));
        strcat (buf, "-");
        strcat (buf, MOT.itoa (type2));
        strcat (buf, " - No such Van der Waals type");
        throw errorM (buf);
      }
#endif
  }

//---------------- Improper torsions ------------------

class Improper_torsion
{
public:
    int                         atom1;
    int                         atom2;
    int                         _atom3_;
    int                         atom4;

    int                         N;
    double                      half_V;

    //friend std::ostream &     std::operator<<( std::ostream & out,
    //                            const Improper_torsion & iT );

    Improper_torsion ();
    Improper_torsion (int a1, int a2, int _a3_, int a4, int n, double h);
};

//---------------------------------------------------------------------------

}//MM

#endif//FORCE_FIELD_H
