#ifndef LIST_INTERACTIONS_H
#define LIST_INTERACTIONS_H

#ifndef YAKUB_RONCHI_INTERACTIONS_H
#include "Yakub_Ronchi_interactions.h"
#endif

#ifndef NEXT_ATOM_H
#include "Next_atom.h"
#endif

#ifndef ATOM_CHUNK_H
#include "Atom_chunk.h"
#endif

#ifndef VALENT_ANGLE_IN_LIST_H
#include "Valent_angle_in_list.h"
#endif

#ifndef VALENT_TORSION_IN_LIST_H
#include "Valent_torsion_in_list.h"
#endif

#include <deque>

namespace MM
{

class List_interactions : public Yakub_Ronchi_interactions
{
    static bool                 list_on_;

protected:
    //Array <Next_atom>               pair_list_;
    Next_atom *                 pair_list_;
    bool                        is_locked_;

    //Array <Atom_chunk> chunk_;
    std::deque <Atom_chunk_big> chunk_;
    int                         big_chunk_size   () const {return Atom_chunk_big  ().count();}  // 16 32 64 128 256
    int                         small_chunk_size () const {return Atom_chunk_small().count();}    //  1  2  3  4   8 

    Array <int>                                 water_;
    Array <Valent_angle_in_list>                angle_;
    Array <Valent_torsion_in_list>              torsion_;
    Array <Atom_chunk_template <double, 1> >    nonbonded_14_;

private:
    // Grant et all 
    // 2007
    // A simple formula for dielectric polarisation energies: 
    // The Sheffield Solvation Model 
    // Chemical Physics Letters, v.441. pp.163-166
    static bool                 solvation_, Sheffield_solvation_;
    static double               Sheffield_a_, Sheffield_b_, Sheffield_RH_;

public:
    static bool        implicit_water ()           {return solvation_;}
    static void        set_implicit_water (bool v) {solvation_ = v;}

    static bool        Sheffield_water ()           {return Sheffield_solvation_;}
    static void        set_Sheffield_water (bool v) {Sheffield_solvation_ = v;}

    static double      Sheffield_a ()               {return Sheffield_a_;}
    static double      Sheffield_b ()               {return Sheffield_b_;}
    static double      Sheffield_RH ()              {return Sheffield_RH_;}
    static void        set_Sheffield_a  (double v) {Sheffield_a_ = v;}
    static void        set_Sheffield_b  (double v) {Sheffield_b_ = v;}
    static void        set_Sheffield_RH (double v) {Sheffield_RH_ = v;}

public:
    enum Hint {Whole, Long, Short}/* hint_*/;

    ~List_interactions () {delete [] pair_list_;}

    Interaction * clone () const
    {
        return new List_interactions (model_);
            //(atom_group_, bond_group_, force_field_);
    }

    //Common_interactions *   clone (Atom_group &  atom_group,
    //                               Bond_group &  bond_group,
    //                               Force_field & ff) const
    //{return new List_interactions (atom_group, bond_group, ff);}

    Common_interactions *   clone (Model &  model/*,
                                   Force_field & ff*/) const
    {return new List_interactions (model/*, ff*/);}

    //List_interactions (Atom_group & atom_group, Force_field & ff);

    //List_interactions (Atom_group & atom_group,
    //                   Bond_group & bond_group, Force_field & ff);

    List_interactions (Model &  model/*, Force_field & ff*/);

    static bool         is_on ()            {return list_on_;}
    static void         set_on (bool on)    {list_on_ = on;}

    virtual void        set_lock (bool on);
    virtual bool        locked () const;

    virtual void        start  ();
    virtual void        finish ();

    virtual void        make_list ();
    virtual void        list (double skin) {list (chunk_, skin);}

    virtual void        list (std::deque <Atom_chunk_big>   &chunk, double skin);
    virtual void        list (Array      <Atom_chunk_small> &chunk, double R,
                              std::deque <Atom_chunk_big>   &from);
    virtual void        list (Array      <Atom_chunk_small> &chunk, double R,
                              Array      <Atom_chunk_small> &from);
    //virtual void        list (Array <Atom_chunk> &chunk, double skin);
    //virtual void        list (Array <Atom_chunk> &chunk, double R,
    //                          Array <Atom_chunk> &from);

    virtual void        list_water (Atom_cache & atom_j);
    virtual void        add_water_force ();
    virtual void        list_valent_angles ();
    virtual void        add_valent_angle_force ();
    virtual void        list_valent_torsions ();
    virtual void        add_valent_torsion_force ();
    virtual void        list_nonbonded_14 ();
    virtual void        list_nonbonded_14 (Atom_cache &, Atom_cache &);
    virtual void        add_nonbonded_14 ();

    virtual void        make_volumes () {}//fix remove
    virtual void        cache_coords ();

    virtual double      potential ();
    virtual void        add_force ();
    ///virtual void        add_partial_force  (Hint hint);           //fix

    virtual void        add_long_force  (std::deque <Atom_chunk_big> & chunk);
    virtual void        add_long_force  (double R, double Switching,
                                         Array <Atom_chunk_small> & chunk); //{add_partial_force (Long);}
    //virtual void        add_long_force  (Array <Atom_chunk> & chunk);
    //virtual void        add_long_force  (double R, double Switching,
    //                                     Array <Atom_chunk> & chunk); //{add_partial_force (Long);}

    virtual void        add_short_force ();// {add_partial_force (Short);} //fix

    virtual void        full_nonbonded_potential ();
    virtual void        atom_potential (Atom_cache & atom);

    virtual void        calc_nonbonded_force () {}
    virtual void        add_full_nonbonded_force ();
    virtual void        add_atom_force (Atom_cache & atom);
    virtual void        add_bond_force (Bond_cache & bond);

    // Sheffield
    virtual void        Sheffield_potential ();
    virtual void        add_full_Sheffield_force ();
};

}//MM

#endif //LIST_INTERACTIONS_H
