#include "Crystal_builder_impl.h"

#include "Log.h"
#include "Atom_kit.h"

#include "Defs.h"
#include "Vector_3D_impl.h"
#include "Point_3D_impl.h"
#include "Model.h"
#include "Model_kit.h"
#include "Atom.h"
#include "Box_impl.h"
#include "Mach_eps.h"
#include "Lock.h"
#include "Cleaner.h"
#include "Lock.h"

#include "Create.h"

#include <math.h>

namespace
{
    const int default_copies = 1;
}

namespace MM
{

Text const& Crystal_builder_impl::
name () const
{
    static const Text result ("Crystal builder");
    return result;
}

void Crystal_builder_impl::handle_start_editing    () {}
void Crystal_builder_impl::handle_finish_editing   () {expand_model ();}
void Crystal_builder_impl::handle_cancel_editing   () {set_number_of_copies (1,1,1);}
void Crystal_builder_impl::handle_activate_editing () {}


Crystal_builder_impl::
Crystal_builder_impl (Prototype const &)
:
    use_PBC_(true),
    a_copies_(default_copies), b_copies_(default_copies), c_copies_(default_copies),
    model_(nil<Model>())
{
}


Crystal_builder_impl::
Crystal_builder_impl (Model & model)
:
    use_PBC_(true),
    a_copies_(default_copies), b_copies_(default_copies), c_copies_(default_copies),
    model_(model)
{
    if (box(). is_default())
        fit_box ();
}

Crystal_builder * Crystal_builder_impl::
clone (Model & model) const
{
    return new Crystal_builder_impl (model);
}


Box & Crystal_builder_impl::
box () const
{
    return model_.kit().crystal_cell ();
}

Vector_3D const & Crystal_builder_impl:: a () const{return box().a();}
Vector_3D const & Crystal_builder_impl:: b () const{return box().b();}
Vector_3D const & Crystal_builder_impl:: c () const{return box().c();}

double Crystal_builder_impl::a_mod () const {return box().a().length();}
double Crystal_builder_impl::b_mod () const {return box().b().length();}
double Crystal_builder_impl::c_mod () const {return box().c().length();}

void Crystal_builder_impl::
set (Vector_3D const & a, Vector_3D const & b, Vector_3D const & c)
{
    box().set (a, b, c);
    update_images();
}

double Crystal_builder_impl::alpha () const {return box().alpha ();}
double Crystal_builder_impl::beta  () const {return box().beta  ();}
double Crystal_builder_impl::gamma () const {return box().gamma ();}

void Crystal_builder_impl::
set (double new_a,     double new_b,    double new_c,
     double new_alpha, double new_beta, double new_gamma)
{
    box().set (new_a, new_b, new_c, new_alpha, new_beta, new_gamma);
    update_images ();
}

void Crystal_builder_impl::
show_cell_copies ()
{
    static bool entrance = false;
    REQUIRE ("No reentrance", !entrance);
    if (entrance)                   //fix remove
        return;

    entrance = true;


    //{
    //    int a_tmp = a_copies_;
    //    int b_tmp = b_copies_;
    //    int c_tmp = c_copies_;
    //    a_copies_ = 1;    b_copies_ = 1;    c_copies_ = 1;

    //Cleaner cleaner (model_);
    ////for (int i=0;  i<cell_copy_.atom_count();  ++i)
    //for (int i=cell_copy_.atom_count()-1;  i>=0;  --i)
    //    cleaner.add(cell_copy_.atom(i));
    //cleaner.execute();

    //cell_copy_.erase();
    ////model_.copy(0, &cell_copy_);

    ////update_images ();

    //    a_copies_ = a_tmp;    b_copies_ = b_tmp;    c_copies_ = c_tmp;
    //}


    int size = a_copies_ * b_copies_ * c_copies_;

    Cleaner cleaner (model_);
    //for (int i=0;  i<cell_copy_.atom_count();  ++i)
    for (int i=cell_copy_.atom_count()-1;  i>=0;  --i)
        cleaner.add(cell_copy_.atom(i));
    cleaner.execute();

    cell_copy_.erase();
    model_.copy(size-1, &cell_copy_);

    update_images ();

    entrance = false; 
}

void Crystal_builder_impl::
convert_from_fractional_to_cartesian ()
{
    // applay transposed matrix to each atom

    for (int i=0; i<model().atom_count();  ++i)
    {
        Atom &          current_atom     = model().atom (i);
        Point_3D const &current_position = current_atom.position();
        Point_3D_impl   new_position (0,0,0);

        new_position.set_x (box().a().x()*current_position.x()
                          + box().b().x()*current_position.y()
                          + box().c().x()*current_position.z());

        new_position.set_y (box().a().y()*current_position.x()
                          + box().b().y()*current_position.y()
                          + box().c().y()*current_position.z());

        new_position.set_z (box().a().z()*current_position.x()
                          + box().b().z()*current_position.y()
                          + box().c().z()*current_position.z());

        current_atom.set_position (new_position);
    }
}

void Crystal_builder_impl::
fit_box ()
{
    box().fit (model_, Box::Cuboid, 1.5);
}

void Crystal_builder_impl::
update_images ()
{
    static bool entrance = false;
    //REQUIRE ("No reentrance", !entrance);
    if (entrance)                   //fix remove
        return;
    entrance = true;
    //return;

    int i, j, k, n;
    int cells = a_copies_ * b_copies_ * c_copies_;
    int atoms_per_cell = model_.atom_count() / cells;
    Box & cell = box();
    Vector_3D_impl a (cell.a());
    Vector_3D_impl b (cell.b());
    Vector_3D_impl c (cell.c());

    //for (n=0;  n<atoms_per_cell;  ++n)
    //{
    //    Atom &current_atom = model_.atom (n);
    //    Point_3D const & base_position = current_atom.position();

    //    int shift = atoms_per_cell;
    //    for (i=0;  i<a_copies_;  ++i)
    //    for (j=0;  j<b_copies_;  ++j)
    //    for (k=0;  k<c_copies_;  ++k)
    //    {
    //        if (i==0 && j==0 && k==0) 
    //            continue;

    //        Vector_3D_impl movement (0,0,0);
    //        movement.add_scaled (i, a);
    //        movement.add_scaled (j, b);
    //        movement.add_scaled (k, c);
    //        Point_3D_impl new_position (base_position);
    //        new_position += movement;

    //        Atom &cell_atom = model_.atom (shift + n);
    //        cell_atom.set_position (new_position);

    //        shift += atoms_per_cell;
    //    }
    //}


    int shift = atoms_per_cell;

    for (i=0;  i<a_copies_;  ++i)
    for (j=0;  j<b_copies_;  ++j)
    for (k=0;  k<c_copies_;  ++k)
    {
        if (i==0 && j==0 && k==0) 
            continue;

        for (n=0;  n<atoms_per_cell;  ++n)
        {
            Atom const & current_atom = model_.atom (n);
            Point_3D const & base_position = current_atom.position();

            Vector_3D_impl movement (0,0,0);
            movement.add_scaled (i, a);
            movement.add_scaled (j, b);
            movement.add_scaled (k, c);
            Point_3D_impl new_position (base_position);
            new_position += movement;

            Atom &cell_atom = model_.atom (shift + n);
            cell_atom.set_position (new_position);
        }

        shift += atoms_per_cell;
    }
    entrance = false; 
}

void Crystal_builder_impl::
set_number_of_copies (int a, int b, int c)
{
    if (a_copies_ == a && b_copies_ == b && c_copies_ == c)
        return;

    if (a == 1 && b == 1 && c == 1)
        cancel_editing ();
    else
        start_editing ();

    a_copies_ = a;    b_copies_ = b;    c_copies_ = c;
    show_cell_copies ();
    model_.kit().visual().fit_to_screen ();
}

bool Crystal_builder_impl::
use_PBC () const
{
    return model_.kit().boundary_conditions().is_box_a_crystal_cell();
}

void Crystal_builder_impl::
set_use_PBC (bool use)          
{
    if (use)
        model_.kit().boundary_conditions().accept_crystal_cell_as_a_box();
    else
        model_.kit().boundary_conditions().reject_crystal_cell_as_a_box();
}

void Crystal_builder_impl::
expand_model () 
{
    cell_copy_.erase();
    box().scale_a (a_copies_);  a_copies_ = 1;
    box().scale_b (b_copies_);  b_copies_ = 1;
    box().scale_c (c_copies_);  c_copies_ = 1;
    finish_editing ();
}

}//MM












