#ifndef ATOM_IMPL_H
#define ATOM_IMPL_H

#ifndef ELEMENT_H
#include "Element.h"
#endif

#ifndef ARRAY_OF_REF_H
#include "Array_of_ref.h"
#endif

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

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

#ifndef ID_H
#include "ID.h"
#endif

#ifndef ATOM_H
#include "Atom.h"
#endif

#ifndef ATOM_KIT_H
#include "Atom_kit.h"
#endif

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

#ifndef POINT_3D_IMPL_H
#include "Point_3D_impl.h"
#endif

#ifndef MASS_H
#include "Mass.h"
#endif


namespace MM
{

class Atom_impl
    :
        public Atom,
        protected DbC
{
    Atom_kit                        kit_;
    double                          charge_;
    Own <ID>                        id_;
    mutable Text                    local_id_;
    Point_3D_impl                   position_;
    Element                         element_;

    Array_of_ref <Atom>             bound_atoms_;
    Array_of_ref <Bond>             bonds_;

public:
    explicit Atom_impl ();
    explicit Atom_impl (Element const & element);
    virtual ~Atom_impl();

    virtual Atom_kit &              kit ()                                      {return kit_;}
    virtual Atom_kit const &        kit () const                                {return kit_;}

    virtual ID const &              id () const                                 {return id_();}
    bool                            has_local_id () const                       {INVARIANT; return local_id_ != "";}
    Text const&                     local_id () const                           {INVARIANT; return local_id_;}
    void                            set_local_id (Text const & id) const        {INVARIANT; if (id == "-") local_id_=""; else local_id_= id;}

    bool                            is_backbone () const;

    double                          charge () const                             {return charge_;}
    void                            set_charge(double charge)                   {charge_= charge; kit().event().charge_changed ();}

    //virtual ID const &              id      () const                            {return id_();}
    virtual Element const &         element () const                            {return element_;}
    virtual void                    set_element (Element e);

    double                          ff_mass  () const;
    double                          mass  () const;

    Atom &                          bound (int i)                               {INVARIANT; return bound_atoms_[i];}
    Atom const &                    bound (int i) const                         {INVARIANT; return bound_atoms_[i];}
    Bond &                          bond  (int i)                               {INVARIANT; return bonds_[i];}
    Bond const &                    bond  (int i) const                         {INVARIANT; return bonds_[i];}
    int                             bound_count () const                        {INVARIANT; return bound_atoms_.size();}
    bool                            bound_with (Atom const &, int * i=0) const;
    bool                            not_bound_with (Atom const &at) const       {return !bound_with (at);}

    virtual Point_3D const &        position () const {return position_;}
    virtual void                    set_position (Point_3D const& new_position,
                                            bool              silent = false);
    virtual void                    set_position_silent
                                        (double x, double y, double z)          {position_.set (x, y, z);}
    virtual void                    move   (Vector_3D const & movement,
                                            bool              silent = false);

    virtual void                    update ();

    virtual void                    rotate (Vector_3D const& around,
                                            Point_3D  const& point,
                                            double teta);

    virtual double                  x     () const                              {return position_.x();}
    virtual double                  y     () const                              {return position_.y();}
    virtual double                  z     () const                              {return position_.z();}

    virtual void                    set_x (double x)                            {position_.set_x (x); on_move();}
    virtual void                    set_y (double y)                            {position_.set_y (y); on_move();}
    virtual void                    set_z (double z)                            {position_.set_z (z); on_move();}

//    virtual bool                    operator == (const Associated & item) const;
    virtual Atom *                  clone () const;
    virtual Atom *                  clone (Element const &) const;

protected:
    void                            handle_clone (Atom_impl *) const;

    void                            add_bond     (Bond & bond);
    void                            add_bound    (Atom & atom);
    void                            remove_bond  (Bond & bond);
    void                            remove_bound (Atom & atom);
    void                            release_all_bonds ();

    void                            on_move ()                                  {update_coordinates(); kit().event().moved ();}
    void                            on_element_change ()                        {update_view_type();   kit().event().element_changed ();}

    virtual void                    rotate (Vector_3D const & around,
                                            double teta);

    virtual bool                    invariant() const;

private:
    void                            update_coordinates ();
    void                            update_view_type   ();
};

}//MM

#endif //ATOM_IMPL_H
