#ifndef ATOM_KIT_H
#define ATOM_KIT_H

#ifndef OWN_H
#include "Own.h"
#endif

#ifndef DBC_H
#include "DbC.h"
#endif

#ifndef SELECTABLE_H
#include "Selectable.h"
#endif

#ifndef OPENRM_ATOM_H
#include "OpenRM_atom.h"
#endif

#ifndef VISUAL_ATOM_COMPOSITE_H
#include "Visual_atom_composite.h"
#endif

#ifndef PDB_ATTRIBUTES_H
#include "Pdb_attributes.h"
#endif

#ifndef HYPERCHEM_ATTRIBUTES_H
#include "Hyperchem_attributes.h"
#endif

#ifndef MM_FLAG_H
#include "MM_Flag.h"
#endif

#ifndef VECTOR_3D_IMPL_H
#include "Vector_3D_impl.h"
#endif

#ifndef ENERGY_TERMS_H
#include "Energy_terms.h"
#endif

#ifndef ATOM_EVENT_H
#include "Atom_event.h"
#endif

#ifndef FINE_TYPE_H
#include "Fine_type.h"
#endif

namespace MM
{
class  Atom;
struct Atom_cache;
class  Molecule;
class  Residue;

class Atom_kit : public Selectable, protected DbC
{
    Molecule *                      in_molecule_;
    Residue *                       in_residue_;

    // ChemCon                                      //fix remove
    int                             mm_type_;
    mutable int                     number_;
    Text                            mm_type_name_;
    //Flag                            flag_;
    mutable MM_Flag                 flag_;
    double                          ff_mass_;
    double                          induction_charge_;

    //Text                            pdb_name_;

    Atom &                          atom_;
    Text                            comment_;
    int                             fine_type_;

    Energy_terms <double>           energy_terms_;
    Vector_3D_impl                  potential_force_;
    Vector_3D_impl                  bond_stretch_force_;
    Vector_3D_impl                  angle_force_;
    Vector_3D_impl                  torsion_force_;
    Vector_3D_impl                  improper_force_;
    Vector_3D_impl                  nonbonded_force_;
    Vector_3D_impl                  short_nonbonded_force_;
    Vector_3D_impl                  long_nonbonded_force_;
    Vector_3D_impl                  velocity_;
    bool                            overlap_flag_;
    double                          transparency_;
    double                          born_radius_;
    double                          V_;
    double                          Gpol_;
    double                          Gpol13_;

    Visual_atom_composite           visual_;
    Own <Pdb_attributes>            pdb_attributes_;
    mutable Own <Hyperchem_attributes>  hyperchem_attributes_;

    Atom_event                      event_;

    bool                            picked_;
    bool                            selected_;
    bool                            illusory_;
    bool                            frozen_;

public:
    Atom_cache                      *cache___;

    explicit                        Atom_kit (Atom & atom);
    Atom &                          atom ()          {return atom_;}
    const Atom &                    atom () const    {return atom_;}

    bool                            is_picked () const      {return picked_;}
private:
    friend class Selector;
    friend class Bond_kit;
    void                            pick   (bool on = true);
    void                            select (bool on = true);
public:
    bool                            is_selected () const    {return selected_;}

    bool                            is_illusory () const    {return illusory_;}
    void                            set_illusory (bool on = true);

    bool                            was_frozen  () const    {return frozen_;}
    void                            freeze (bool on = true);


    bool                            has_overlap () const {return overlap_flag_;}//fix to algorithm?
    void                            set_overlap (bool on){overlap_flag_ = on;}

    Text const &                    comment_line () const    {return comment_;}
    void                            set_comment_line(Text const& v){comment_=v;}

    Pdb_attributes &                pdb ();
    const Pdb_attributes &          pdb () const;
    bool                            has_pdb_attributes() const;
    void                            add_pdb_attributes(Pdb_attributes *adoptee);
    void                            remove_pdb_attributes();

    Hyperchem_attributes &          hyperchem();
    const Hyperchem_attributes &    hyperchem() const;
    bool                            has_hyperchem_attributes() const;
    void                            add_hyperchem_attributes
                                        (Hyperchem_attributes * adoptee);
    void                            remove_hyperchem_attributes();

    Visual_atom_composite &         visual();    //fix to view
    const Visual_atom_composite &   visual() const;
    bool                            has_visual() const;
    void                            add_visual (Visual_atom *adoptee);
    //void                            remove_visual();

    // ChemCon //////////////////////////////////////////////////////////////
    enum  //fix
    {
        already_used = 1, mark_1_2 = 2, mark_1_3 = 4, mark_1_4 = 8,
        hetero = 16, has_improper_torsion = 32, united = 64, selected = 128,
        coords_was_changed = 256, already_calculated = 512, missed = 1024,
        molecular_mark = 2048
    };

    Molecule &              in_molecule ()               {return *in_molecule_;}
    const Molecule &        in_molecule () const         {return *in_molecule_;}
    void                    set_molecule (Molecule &mol) {in_molecule_ = &mol;}

    Residue &               in_residue ()               {return *in_residue_;}
    Residue const &         in_residue () const         {return *in_residue_;}
    void                    set_residue (Residue &res)  {in_residue_ = &res;}

    bool                    has_mm_type () const;
    int                     mm_type () const                {return mm_type_;}
    const char *            mm_type_c_str    () const;
    void                    set_mm_type (int type)          {mm_type_ = type;}
    void                    set_mm_type (const char * str);
    //MM_Flag &               flag ()                         {return flag_;}
    //MM_Flag const &         flag () const                   {return flag_;}
    MM_Flag &               flag () const                   {return flag_;}

    bool                    has_ff_mass () const            {return ff_mass_ != 0.;}
    double                  ff_mass     () const            {return ff_mass_;}
    void                    set_ff_mass (double v)          {ff_mass_ = v;}

    double                  induction_charge () const                           {return induction_charge_;}
    void                    set_induction_charge (double v)                     {induction_charge_ = v;}

    // ChemCon //////////////////////////////////////////////////////////////

    bool                    has_fine_type () const                              {return fine_type_!= Fine_type::none;}
    int                     fine_type_id  () const          {return fine_type_;}
    char const *            fine_type_name() const;
    void                    set_fine_type (int id)          {fine_type_ = id;}
    void                    set_fine_type (char const* name);

    Energy_terms <double> const &
                            energy_terms () const        {return energy_terms_;}
    void                    set_energy_terms
                                (Energy_terms <double> const &new_value)
                                    {energy_terms_ = new_value;}
    void                    add_energy_terms
                                (Energy_terms <double> const &new_value)
                                    {energy_terms_ += new_value;}

    Vector_3D const &       potential_force () const  {return potential_force_;}
    void                    set_potential_force (Vector_3D const & new_value);
    void                    add_potential_force (Vector_3D const & value);

    Vector_3D const &       bond_stretch_force () const  {return bond_stretch_force_;}
    void                    set_bond_stretch_force (Vector_3D const & new_value);
    void                    add_bond_stretch_force (Vector_3D const & value);

    Vector_3D const &       angle_force () const  {return angle_force_;}
    void                    set_angle_force (Vector_3D const & new_value);
    void                    add_angle_force (Vector_3D const & value);

    Vector_3D const &       torsion_force () const  {return torsion_force_;}
    void                    set_torsion_force (Vector_3D const & new_value);
    void                    add_torsion_force (Vector_3D const & value);

    Vector_3D const &       improper_force () const  {return improper_force_;}
    void                    set_improper_force (Vector_3D const & new_value);
    void                    add_improper_force (Vector_3D const & value);

    Vector_3D const &       nonbonded_force () const  {return nonbonded_force_;}
    void                    set_nonbonded_force (Vector_3D const & new_value);
    void                    add_nonbonded_force (Vector_3D const & value);

    Vector_3D const &       short_nonbonded_force () const  {return short_nonbonded_force_;}
    void                    set_short_nonbonded_force (Vector_3D const & new_value);
    void                    add_short_nonbonded_force (Vector_3D const & value);

    Vector_3D const &       long_nonbonded_force () const  {return long_nonbonded_force_;}
    void                    set_long_nonbonded_force (Vector_3D const & new_value);
    void                    add_long_nonbonded_force (Vector_3D const & value);

    Vector_3D const &       velocity () const               {return velocity_;}
    void                    set_velocity (Vector_3D const & new_value);
    void                    set_velocity (double fx, double fy, double fz);
    void                    add_velocity (Vector_3D const & value);
    void                    scale_velocity (double value);

    double                  transparency () const       {return transparency_;}
    void                    set_transparency (double v) {transparency_ = v;}

    double                  born_radius () const            {return born_radius_;}
    double                  volume () const                 {return V_;}
    double                  Gpol () const                   {return Gpol_;}
    double                  Gpol13 () const                 {return Gpol13_;}
    void                    set_born_radius (double v)      {born_radius_ = v;}
    void                    set_volume (double v)           {V_ = v;}
    void                    set_Gpol   (double v)           {Gpol_ = v;}
    void                    set_Gpol13 (double v)           {Gpol13_ = v;}

    //fit export to Atom_numerator
    int                     number () const         {return number_;}
    void                    set_number (int n) const{number_ = n;}

    Atom_event &            event () {return event_;}
};

inline Pdb_attributes & Atom_kit::
pdb ()
{
    REQUIRE ("", has_pdb_attributes());
    return pdb_attributes_();
}

inline const Pdb_attributes & Atom_kit::
pdb () const
{
    REQUIRE ("", has_pdb_attributes());
    return pdb_attributes_();
}

inline Hyperchem_attributes & Atom_kit::
hyperchem()
{
    //REQUIRE ("", has_hyperchem_attributes());
    if (!has_hyperchem_attributes())
        add_hyperchem_attributes (new Hyperchem_attributes (atom()));

    return hyperchem_attributes_();
}

inline const Hyperchem_attributes & Atom_kit::
hyperchem() const
{
    return const_cast <Atom_kit*> (this)->hyperchem();
}

inline Visual_atom_composite & Atom_kit::
visual()
{
    //REQUIRE ("", has_visual());
    return visual_;
}

inline const Visual_atom_composite & Atom_kit::
visual() const
{
    //REQUIRE ("", has_visual());
    return visual_;
}

}//MM

#endif //ATOM_KIT_H
