#include "Add_builder_impl.h"

#include "Log.h"

#include "User.h"
#include "Defs.h"
#include "Create.h"
#include "Atom.h"
#include "Atom_kit.h"
#include "Bond.h"
#include "Bond_kit.h"
#include "Molecule.h"
#include "Vector_3D_impl.h"
#include "View_atom_type.h"
#include "View_atom_type_manager.h"
#include "HSV.h"
#include "Length.h"
#include "Add_model_dialog.h"

namespace MM
{
Text const& Add_builder_impl::
name () const
{
    static Text result = "Add";
    return result;
}

void Add_builder_impl::handle_start_editing    () {}
void Add_builder_impl::handle_finish_editing   () {accept ();}
void Add_builder_impl::handle_cancel_editing   () {reject ();}
void Add_builder_impl::handle_activate_editing () {}


Add_builder_impl::
Add_builder_impl (Prototype const &)
:
    model_(nil<Model>()),
    translation_increment_  (0.),
    rotation_increment_     (0.),
    overlap_allowed_        (true),
    movable_model_(0),
    immutable_model_(0),
    overlapped_model_(0)
{
}

Add_builder_impl::
Add_builder_impl (Model & model)
:
    model_(model),
    new_model_ (prototype <Model> (). clone()),
    translation_increment_  (1),
    rotation_increment_     (6 * __Pi__ / 180.), //6      //fix __Pi__
    overlap_allowed_                    (true),
    movable_model_                      (&model),
    immutable_model_                    (&model),
    overlapped_model_                   (&new_model_())
{
    /*Atom * atom_prototype = model.atom_prototype().clone();
    atom_prototype->set_element ("Dummy");
    //atom_prototype->kit().visual().show();//fix
    new_model_().set_atom_prototype (atom_prototype);//*/
}

Add_builder * Add_builder_impl::
clone (Model & model) const
{
    return new Add_builder_impl (model);
}

//fix
void Add_builder_impl:: 
load (const char * filename, const char * file_type)
{
    start_editing ();

    Atom * atom_prototype = model_.atom_prototype().clone();
    atom_prototype->set_element ("Dummy");
    atom_prototype->kit().visual().set_style (Visual_style::ball_and_stick);
    //atom_prototype->kit().visual().show();//fix
    new_model_().set_atom_prototype (atom_prototype);

    Bond * bond_prototype = model_.bond_prototype().clone ();
    bond_prototype->kit().visual().set_style (Visual_style::ball_and_stick);
    new_model_().set_bond_prototype (bond_prototype);

    new_model_().load (filename, file_type);

    double kS = 0.3, kV = 0.6, kR = 0.5;

    for (int n=0;  n<new_model_().atom_count();  ++n)
    {
        Atom & current_atom = new_model_().atom (n);

        View_atom_type type = View_atom_type_manager::singleton().
            type (current_atom.element().c_str());     //fix speed

        double R = type.r(),   G = type.g(),   B = type.b();
        double H,S,V;
        RGB_to_HSV (R,G,B, H,S,V);
        S *= kS;
        V *= kV;
        HSV_to_RGB (H,S,V, R,G,B);
        type.set_color ((int)R,(int)G,(int)B);
        double radius = type.radius();
        type.set_radius (radius * kR);

        ///current_atom.kit().visual().update_view_type (type);
        current_atom.kit().visual().set_style (Visual_style::ball_and_stick);
        //current_atom.kit().visual().set_illusory ();
        current_atom.kit().visual().renew ();
        //current_atom.kit().visual().renew_view ();
    }

    for (int i=0;  i<new_model_().bond_count();  ++i)
    {
        Bond & current_bond = new_model_().bond (i);
        current_bond.kit().visual().set_style (Visual_style::ball_and_stick);
        current_bond.kit().visual().renew ();
    }

    user_interaction ();
}

void Add_builder_impl::
reject ()
{
    new_model_().clear (); //gix clear to erase ?
    cancel_editing ();
}

void Add_builder_impl::
accept ()
{
    for (int n=0;  n<new_model_().atom_count();  ++n)
    {
        Atom &current_atom = new_model_().atom (n);

        View_atom_type type = View_atom_type_manager::singleton().
            type (current_atom.element().c_str() );     //fix speed

///        current_atom.kit().visual().update_view_type (type);
        current_atom.kit().visual().renew_view ();
    }

    if (!overlap_allowed_)
        check_overlap (true);

    model_.merge_model (&new_model_());
    finish_editing ();
}

bool Add_builder_impl::
is_active () const
{
    return new_model_().atom_count() != 0;
}

void Add_builder_impl::
allow_overlap (bool on)
{
    overlap_allowed_ = on;
    check_overlap ();
}

void Add_builder_impl::
up ()
{
    movable_model_->move (Vector_3D_impl (0, translation_increment_, 0));
    check_overlap ();
    //repaint
}

void Add_builder_impl::
down ()
{
    movable_model_->move (Vector_3D_impl (0, -translation_increment_, 0));
    check_overlap ();
}

void Add_builder_impl::
right ()
{
    movable_model_->move (Vector_3D_impl (translation_increment_, 0, 0));
    check_overlap ();
}

void Add_builder_impl::
left ()
{
    movable_model_->move (Vector_3D_impl (-translation_increment_, 0, 0));
    check_overlap ();
}

void Add_builder_impl::
out ()
{
    movable_model_->move (Vector_3D_impl (0, 0, translation_increment_));
    check_overlap ();
}

void Add_builder_impl::
in ()
{
    movable_model_->move (Vector_3D_impl (0, 0, -translation_increment_));
    check_overlap ();
}

void Add_builder_impl::
right_X ()
{
    movable_model_->rotate (Vector_3D_impl (1, 0, 0), rotation_increment_);
}

void Add_builder_impl::
left_X ()
{
    movable_model_->rotate (Vector_3D_impl (-1, 0, 0), rotation_increment_);
}

void Add_builder_impl::
right_Y ()
{
    movable_model_->rotate (Vector_3D_impl (0, 1, 0), rotation_increment_);
}

void Add_builder_impl::
left_Y ()
{
    movable_model_->rotate (Vector_3D_impl (0, -1, 0), rotation_increment_);
}

void Add_builder_impl::
right_Z ()
{
    movable_model_->rotate (Vector_3D_impl (0, 0, 1), rotation_increment_);
}

void Add_builder_impl::
left_Z ()
{
    movable_model_->rotate (Vector_3D_impl (0, 0, -1), rotation_increment_);
}

double Add_builder_impl::
translation_increment () const
{
    return translation_increment_;
}

double Add_builder_impl::
rotation_increment () const
{
    return rotation_increment_;
}

void Add_builder_impl::
set_translation_increment (double value)
{
    translation_increment_ = value;
}

void Add_builder_impl::
set_rotation_increment (double value)
{
    rotation_increment_ = value;
}

void Add_builder_impl::
remove_overlap_in_additional_model (bool yes)
{
    if (yes)
    {
        immutable_model_  = &model_;
        overlapped_model_ = &new_model_();
    }
    else
    {
        immutable_model_  = &new_model_();
        overlapped_model_ = &model_;
    }

    check_overlap ();
}

void Add_builder_impl::
move_current_model (bool yes)
{
    if (yes)
        movable_model_ = &model_;
    else
        movable_model_ = &new_model_();
}



void Add_builder_impl::
user_interaction ()
{
    singleton <Add_model_dialog>(). execute();
}

void Add_builder_impl::
check_overlap (bool remove)
{
    int i, j, dead_count = 0;

    // show all 
    for (j=0;   j<immutable_model_->atom_count();  ++j)
        immutable_model_->atom(j).kit().visual().show();

    for (j=0;   j<overlapped_model_->atom_count();  ++j)
        overlapped_model_->atom(j).kit().visual().show();

    for (j=0;   j<immutable_model_->bond_count();  ++j)
        immutable_model_->bond(j).kit().visual().show();

    for (j=0;   j<overlapped_model_->bond_count();  ++j)
        overlapped_model_->bond(j).kit().visual().show();

    if (overlap_allowed_)
        return;

    for (i=0;  i<overlapped_model_->atom_count();  ++i)
    {
        Atom & overlapped_atom        = overlapped_model_->atom(i);
        
        double overlapped_atom_radius = 
            .5 * (overlapped_atom.element().covalent_radius() 
                + overlapped_atom.element().van_der_Waals_radius ()); 

        for (j=0;   j<immutable_model_->atom_count();  ++j)
        {
            Atom & immutable_atom        = immutable_model_->atom(j);

            double immutable_atom_radius = 
                .5 * (immutable_atom.element().covalent_radius() 
                    + immutable_atom.element().van_der_Waals_radius ());

            double distance_2 = Length 
                (overlapped_atom.position(), immutable_atom.position()).value_2();

            double radius_sum = overlapped_atom_radius + immutable_atom_radius;
            if (distance_2 < radius_sum * radius_sum)
                overlapped_atom.kit().set_overlap (true);
        }
    }

    for (int m=0;  m<overlapped_model_->molecule_count();  ++m)
    {
        Molecule & molecule = overlapped_model_->molecule(m);
        bool overlapped = false;

        for (int n=0;  n<molecule.atom_count();  ++n)
        {
            Atom & atom = molecule.atom (n);
            
            if (atom.kit().has_overlap())
            {
                for (int k=0;  k<molecule.atom_count();  ++k)
                {
                    Atom & atom = molecule.atom (k);
                    atom.kit().set_overlap (false);

                    if (remove)
                        atom.set_dead ();
                    else
                    {
                        atom.kit().visual().show (false);

                        for (int l=0;  l<atom.bound_count();  ++l)
                        {
                            atom.bond(l).kit().visual().show (false);
                        }
                    }
                }

                overlapped = true;
                continue;
            }
        }
        
        if (overlapped)
            ++dead_count;
    }

    //fix to kit().molecule
    //                    molecule.remove_atom (atom); 
    //                    new_model().erase (atom);   
    if (remove)
        overlapped_model_->remove_deads();

    to_user ().status (Text(dead_count) + " overlapped molecules");

    //fix ENSURE ("No overlap flags.");
}

}//MM





