#include "Bond_detector.h"

#include "User.h"
#include "Log.h"

#include "Atom.h"
#include "Atom_kit.h"
#include "Atom_numerator.h"
#include "Atom_grid.h"

#include "Model.h"
#include "Model_kit.h"


#include "Length.h"
#include "Default_bond.h"

#include <math.h>

namespace MM
{

void Bond_detector::    //fix speed
execute ()
{
    bool changes = false;
    int i,  atom_count = model_->atom_count();
    Array_of_own <Bond> tmp_bond;

    //Atom_grid grid (*model_);
    Atom_grid grid (*model_, 2.7);

    for (i=0;  i<atom_count;  ++i)
    {
        Atom & first_atom  = model_->atom (i);
        Atom_grid::Near group (grid, first_atom.position(), 2.5);
        double group_count = group.atom_count();

        //for (int j=i+1;  j<atom_count;  ++j)
        //{
        //    Atom & second_atom = model_->atom (j);
        for (int j=0;  j<group_count;  ++j)
        {
            Atom & second_atom = group.atom (j);
            if (&first_atom == &second_atom)
                continue;

            double min_distance = 0.7 * (
                first_atom.element().covalent_radius()
              + second_atom.element().covalent_radius());

            double max_distance = 0.8 * (
                first_atom.element().van_der_Waals_radius()
              + second_atom.element().van_der_Waals_radius());

            double max_distance_cov = 1.2 * (
                first_atom.element().covalent_radius()
              + second_atom.element().covalent_radius());

			if (max_distance_cov < max_distance)
				max_distance = max_distance_cov;

            double distance_2 = Length 
                (first_atom.position(), second_atom.position()). value_2();
    
            if (distance_2 < min_distance * min_distance)
            {
                Atom_numerator tmp (*model_);

                warning_ += "Atoms ";
                warning_ += first_atom.element().c_str();
                warning_ += " ";
                warning_ += first_atom.kit().number() + 1;
                warning_ += " and ";
                warning_ += second_atom.element().c_str();
                warning_ += " ";
                warning_ += second_atom.kit().number() + 1;
                warning_ += " are too close to each other.\n";
                warning_ += "Distance is ";
                warning_ += sqrt (distance_2);
                warning_ += ". Min distance is ";
                warning_ += min_distance;
                warning_ += ".\n\n";

                log () << "\n=== Warning: " << warning_;
                to_user(). warning 
                    (Text("Bonds not detected.\n") + warning_);

                return;
            }

            if (distance_2 < max_distance * max_distance &&
                first_atom.not_bound_with (second_atom))
            {
                tmp_bond.adopt (model_->bond_prototype().
                    clone(first_atom, second_atom, Order()));
            }
        }
    }

    int bond_count = tmp_bond.size();

    if (bond_count > 0)
        changes = true;

    for (i=0;  i<bond_count;  ++i)
    {
        model_->add_bond (&tmp_bond[i]);
        tmp_bond[i].kit().visual().set_style (Visual_style::ball_and_stick);
    }

    tmp_bond.orphan_all();

    if (changes)
        model_->kit().event().structure_changed();

    return;
}

}//MM

