#include "Module.h"

#include "Project.h"
#include "Atom.h"
#include "Atom_kit.h"
#include "Bond.h"
#include "Bond_kit.h"
#include "Model.h"
#include "Model_kit.h"
#include "Residue.h"
#include "Molecule.h"
#include "User.h"
#include "Main_window.h"

#include "Abstracts.h"
#include "Create.h"

#include "Model_RM_window.h"
#include "Model_RM_widget.h"

#include <set>

namespace MM
{

class Module_select : public Module
{
    static Module_select    singleton_;
                            Module_select() ;
    void                    init ();

    void                    show   ();
    void                    hide   ();
    void                    freeze ();

    void                    expand_to_residue ();
    void                    expand_to_molecule ();

    int                     view_index (Model & model);
};
Module_select  Module_select::singleton_;


Module_select::Module_select () : Module ("Select") { }

void  Module_select::
init ()
{
    class New_selection : public Command_1 <bool>
    {
    public:
        void execute (bool /*on*/)  {} 
    };


    //adopt_command (prototype <Select> ().clone())
    //    //.set_icon       ("Select", "select.png")
    //    .set_icon       ("Select", "additional_selection.png")
    //    .set_menu       ("&Select", "&Select")
    //    ;

    insert_menu_separator ("&Select");

    //adopt_command (new New_selection)
    //    .set_tool_tip   ("New selection.")
    //    .set_icon       ("Fast Edit", "new_selection.png")
    //    .set_menu       ("&Edit", "&New selection")
    //    ;

    //adopt_command (new New_selection)
    //    .set_tool_tip   ("Add selection.")
    //    .set_icon       ("Fast Edit", "additional_selection.png")
    //    .set_menu       ("&Edit", "&Add selection")
    //    ;

    adopt_command (create_command (*this, &Module_select::show))
        .set_menu        ("&Select", "&Show all")
        .set_tool_tip    ("Show all atoms")
        .set_icon        ("Select", "show.png");

    adopt_command (create_command (*this, &Module_select::hide))
        .set_menu        ("&Select", "&Hide selected")
        .set_tool_tip    ("Hide selected")
        .set_icon        ("Select", "hide.png");

    adopt_command (create_command (*this, &Module_select::freeze))
        .set_menu        ("&Select", "&Freeze")
        .set_tool_tip    ("Freeze atom movement")
        .set_icon        ("Select", "freeze.png");
    
    insert_menu_separator ("&Select");


    adopt_command (create_command (*this, &Module_select::expand_to_residue))
        .set_tool_tip   ("Expand selection to full Residues.")
        .set_menu       ("&Select", "Expand to &Residue")
        ;

    adopt_command (create_command (*this, &Module_select::expand_to_molecule))
        .set_tool_tip   ("Expand selection to full Molecules.")
        .set_menu       ("&Select", "Expand to &Molecule")
        ;

    //insert_menu_separator ("&File");
}

void  Module_select::
show ()
{
    Model & model = Project::singleton().current_model();
    int count = model.atom_count ();

    if (count == 0)
        return;

    int i;
    int index = view_index (model);

    for (i=0;  i<count;  ++i)
        model.atom(i).kit().visual().view (index).show();

    count = model.bond_count ();

    for (i=0;  i<count;  ++i)
        model.bond(i).kit().visual().view (index).show ();
}

void  Module_select::
hide ()
{
    //fix the same
    Model & model = Project::singleton().current_model();
    //Model_RM_window * window = (Model_RM_window *)
    //    Main_window::singleton()->current_model_window();
    //Model & model = window->model();

    int i;
    int count = model.atom_count ();

    if (count == 0)
        return;

    int index = view_index (model);

    for (i=0;  i<count;  ++i)
    {
        Atom_kit &     atom = model.atom(i).kit();
        Visual_atom &  view = atom.visual().view (index);

        if (atom.is_selected())
            view.show (false);
    }

    count = model.bond_count ();

    for (i=0;  i<count;  ++i)
    {
        Bond_kit &     bond = model.bond(i).kit();
        Visual_bond &  view = bond.visual().view (index);

        if (bond.is_selected())
            view.show (false);
    }
}

void  Module_select::
freeze ()
{
    //Model_RM_window * window = (Model_RM_window *)
    //    Main_window::singleton()->current_model_window();
    //Model & model = window->model();
    Model & model = Project::singleton ().current_model ();

    int i;
    bool no_freezing = true;
    int count = model.atom_count ();

    if (count == 0)
        return;

    //int index = view_index (model);

    for (i=0;  i<count;  ++i)
    {
        Atom_kit & atom = model.atom(i).kit();

        if (atom.is_selected() && !atom.was_frozen())
        {
            atom.freeze ();
            no_freezing = false;
        }
    }

    if (no_freezing)
    {
        for (i=0;  i<count;  ++i)
        {
            Atom_kit & atom = model.atom(i).kit();
            atom.freeze (false);
        }
    }

    count = model.bond_count ();

    for (i=0;  i<count;  ++i)
    {
        Bond_kit &     bond = model.bond(i).kit();
        bond.visual().renew_view();
    }

}

//fix remove graphics
void  Module_select::
expand_to_residue ()
{
    //Model_RM_window * window = (Model_RM_window *)
    //    Main_window::singleton()->current_model_window();

    ////fix model exist
    //Model & model = window->model();
    Model & model = Project::singleton ().current_model ();
    Selector & selector = model.kit().selector();

    int i, count = model.atom_count();
    using namespace std;
    set <Molecule *> mol;
    set <Residue  *> res;

    for (i=0;  i<count;  ++i)
    {
        Atom & atom = model.atom (i);

        if (atom.kit().is_selected ())
        {
            Residue & residue = atom.kit().in_residue();
            if (&residue == &Residue::none())
            {
                Molecule & molecule = atom.kit().in_molecule();
                if (&molecule == &Molecule::none())
                    to_user().error 
                        ("There are atoms not included in molecules.\n"
                         "Try to detect molecules.");

                mol.insert (&molecule);
            }
            else
                res.insert (&residue);
        }
    }

    for (set <Residue *>::iterator it=res.begin();  it!=res.end();  ++it)
    {
        Residue * residue = *it;

        for (i=0;  i<residue->atom_count();  ++i)
            selector.select (residue->atom(i), true);
    }

    for (set <Molecule *>::iterator it=mol.begin();  it!=mol.end();  ++it)
    {
        Molecule * molecule = *it;

        for (i=0;  i<molecule->atom_count();  ++i)
            selector.select (molecule->atom(i), true);
    }
}

void  Module_select::
expand_to_molecule ()
{
    //Model_RM_window * window = (Model_RM_window *)
    //    Main_window::singleton()->current_model_window();

    ////fix model exist

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

    int i, count = model.atom_count();
    using namespace std;
    set <Molecule *> mol;

    for (i=0;  i<count;  ++i)
    {
        Atom & atom = model.atom (i);

        if (atom.kit().is_selected ())
        {
            Molecule & molecule = atom.kit().in_molecule();
            if (&molecule == &Molecule::none())
                to_user().error ("There are atoms not included in molecules.\n"
                    "Try to detect molecules.");

            mol.insert (&molecule);
        }
    }

    for (set <Molecule *>::iterator it=mol.begin();  it!=mol.end();  ++it)
    {
        Molecule * molecule = *it;

        for (i=0;  i<molecule->atom_count();  ++i)
            selector.select (molecule->atom(i), true);
    }
}

int Module_select::
view_index (Model & model)
{
    int count = model.atom_count ();
    //int index;

    if (count > 0)
    {
        Model_RM_window * model_window = (Model_RM_window *)
            Main_window::singleton()->current_model_window();

        OpenRMWidget * model_widget = model_window->current_model_widget ();

        Atom_kit &     atom = model.atom(0).kit();
        Visual_atom_composite & visual_atom = atom.visual();
        //int visual_atom_count = visual_atom.count();

        {
            Visual_atom &  view = visual_atom.view (0);
            OpenRM_atom &  rm_atom = (OpenRM_atom &)view;
            OpenRMWidget * atom_widget = rm_atom.rm_widget ();

            if (model_widget == atom_widget)
                return 0;
        }
        //if (visual_atom_count > 1)
        {
            Visual_atom &  view = visual_atom.view (1);
            OpenRM_atom &  rm_atom = (OpenRM_atom &)view;
            OpenRMWidget * atom_widget = rm_atom.rm_widget ();

            if (model_widget == atom_widget)
                return 1;
        }
    }

    FLAW ("@ Module_view::view_index");
    return -1;
}

}//MM
