#include "Panel_charges.h"

//#include "Main_window.h"
#include "Selected_atoms.h"
#include "Project.h"
#include "Selector.h"
#include "Model.h"
#include "Model_kit.h"
#include "Atom_kit.h"
#include "Bond_length.h"

namespace MM
{

Panel_charges::
Panel_charges ()
:
    Panel ("Charges", "bottom"),
    timer_(this)
{
    setupUi (this);

    connect(&timer_,    SIGNAL(timeout()),
            this,       SLOT(renew()));
    timer_.start(200);

    connect(toolButton_Select,          SIGNAL(clicked(bool)),
            this,                       SLOT(select()));

    connect(toolButton_Deselect,        SIGNAL(clicked(bool)),
            this,                       SLOT(deselect()));

    connect(toolButton_Set,             SIGNAL(clicked(bool)),
            this,                       SLOT(set()));

    connect(toolButton_Add,             SIGNAL(clicked(bool)),
            this,                       SLOT(add()));


}

void Panel_charges::select   () {select(true);}
void Panel_charges::deselect () {select(false);}

void Panel_charges::
select (bool on)
{
    Model & model = Project::singleton().current_model();
    Selector & selector = model.kit().selector();

    bool H    = checkBox_H   ->isChecked();
    bool HCR2 = checkBox_HCR2->isChecked();
    bool HCR3 = checkBox_HCR3->isChecked();

    for (int i=0;  i<model.atom_count();  ++i)
    {
        Atom & atom = model.atom (i);
        if (atom.element() == Element(1) )
        {
            if (H)
                selector.select (atom, on);
            
            else if (atom.bound_count() > 0)
            {
                Atom &          bound         = atom.bound(0);
                Element const & bound_element = bound.element();
                int             bound_count   = bound.bound_count();
                
                if (bound_element == Element(6) &&
                    (HCR2 && bound_count == 3 ||
                     HCR3 && bound_count == 4))
                    selector.select (atom, on);
            }
        }
    }
}


void Panel_charges::
renew ()
{
    renew (Model_event::Structure_changed);
}

void Panel_charges::
renew_current_model (Model &)
{
    renew (Model_event::Structure_changed);
}

void Panel_charges::
renew (Model_event::Hint /*hint*/)
{
    //fix hint

    //static int version = -1;

    Model & model = Project::singleton().current_model();
    //Selector & selector = model.kit().selector();

    //if (selector.version() == version)
    //    return;
    //version = selector.version();

    Selected_atoms selected(model);
    {
        double mean = 0.;

        if (selected.atom_count() > 0)
        {
            for (int i=0;  i<selected.atom_count();  ++i)
                mean += selected.atom(i).charge();

            doubleSpinBox_sum->setEnabled (true);
            doubleSpinBox_sum->setValue   (mean);

            mean /= selected.atom_count();

            doubleSpinBox_mean->setEnabled (true);
            doubleSpinBox_mean->setValue   (mean);
        }
        else
        {
            doubleSpinBox_sum ->setEnabled (false);
            doubleSpinBox_mean->setEnabled (false);
        }
    }
}

void Panel_charges::
set ()
{
    Model & model = Project::singleton().current_model();
    Selected_atoms selected(model);
    double val = doubleSpinBox_new->value();

    if (checkBox_keep_dipole->isChecked())
    {
        for (int i=0;  i<selected.atom_count();  ++i)
        {
            Atom & atom = selected.atom(i);

            if (atom.bound_count() == 1)
            {
                Bond const & bond = atom.bond (0);
                double l    = Bond_length (bond).value();
                double old  = atom.charge();
                double d    = old - val;
                double v = -d;
                atom.set_charge (/*old + */val);

                Atom & central = atom.bound(0);
                int selected = 0, k;

                for (k=0;  k<central.bound_count();  ++k)
                    if (central.bound (k).kit().is_selected())
                        ++selected;

                for (k=0;  k<central.bound_count();  ++k)
                {
                    Atom & neighbour = central.bound (k);
                    Bond & n_bond    = central.bond  (k);
                    double n_l       = Bond_length (n_bond).value();
                    double n_old     = neighbour.charge();
                    
                    if (neighbour.kit().is_selected())
                        continue;

                    double n_val = v * l / n_l / selected;
                    neighbour.set_charge (n_old + n_val);

                    d -= n_val;
                }

                central.set_charge (central.charge() + d);
            }
        }
    }
    else
    {
        for (int i=0;  i<selected.atom_count();  ++i)
            selected.atom(i).set_charge (val);
    }
}

void Panel_charges::
add ()
{
    Model & model = Project::singleton().current_model();
    Selected_atoms selected(model);
    double val = doubleSpinBox_new->value();

    if (checkBox_keep_dipole->isChecked())
    {
        for (int i=0;  i<selected.atom_count();  ++i)
        {
            Atom & atom = selected.atom(i);

            if (atom.bound_count() == 1)
            {
                Bond const & bond = atom.bond (0);
                double l    = Bond_length (bond).value();
                double old  = atom.charge();
                double d    = -val;
                atom.set_charge (old + val);

                Atom & central = atom.bound(0);
                int selected = 0, k;

                for (k=0;  k<central.bound_count();  ++k)
                    if (central.bound (k).kit().is_selected())
                        ++selected;

                for (k=0;  k<central.bound_count();  ++k)
                {
                    Atom & neighbour = central.bound (k);
                    Bond & n_bond    = central.bond  (k);
                    double n_l       = Bond_length (n_bond).value();
                    double n_old     = neighbour.charge();
                    
                    if (neighbour.kit().is_selected())
                        continue;

                    double n_val = val * l / n_l / selected;
                    neighbour.set_charge (n_old + n_val);

                    d -= n_val;
                }

                central.set_charge (central.charge() + d);
            }
        }
    }
    else
    {
        for (int i=0;  i<selected.atom_count();  ++i)
        {
            double old = selected.atom(i).charge();
            selected.atom(i).set_charge (old + val);
        }
    }
}

}//MM
