/*
#ifdef RM_X
#ifndef GLX_GLXEXT_PROTOTYPES
#define GLX_GLXEXT_PROTOTYPES
#endif
#include <GL/glx.h>
#endif


#endif 
//*/
//#include "Model_kit.h"

//#include "OpenRMWidget.h"
//#include <QGLWidget>
//#include <rm/rm.h>

#include <QMouseEvent>
//#include <QTimerEvent>
#include <QEvent>
#include <QPaintEvent>
#include <QCloseEvent>

#include <QAction>
#include <QWidgetAction>


//#include "Model_RM_widget.h"

#include "Atom.h"
#include "Atom_kit.h"
#include "Model_kit.h"
#include "User.h"

//#include "Main_window.h"
#include "Log.h"
//#include "User.h"
#include "Debug.h"

#include "Model.h"
#include "Molecule.h"
#include "Mass_center.h"
#include "Point_3D_impl.h"
#include "Bond_kit.h"
#include "User.h"
#include "Text.h"
#include "Selected_atoms.h"

#include "Flaw.h"
#include "User.h"
#include "OpenRM_bond.h"
#include "Timer.h"
#include "Angle_unit.h"

#include <math.h>
#include <set>

#include "Model_RM_widget.h"
/*#include <rm/rm.h>
#include <rm/rmaux.h>
//*/

#define max(a, b)  (((a) > (b)) ? (a) : (b))
#define min(a, b)  (((a) < (b)) ? (a) : (b))

namespace MM
{
Model_RM_widget::Mouse_handler *  Model_RM_widget::press_        =0;         
Model_RM_widget::Mouse_handler *  Model_RM_widget::release_      =0;         
Model_RM_widget::Mouse_handler *  Model_RM_widget::double_click_ =0;         
Model_RM_widget::Mouse_handler *  Model_RM_widget::move_         =0;         


Model_RM_widget::
Model_RM_widget (Model & model, QWidget *parent)
:   
    OpenRMWidget(parent),
    model_      (model),
    pick_       (0),
    select_     (0)
{
    selection_movement_.x = 0;
    selection_movement_.y = 0;
    selection_movement_.z = 0;

    setAutoFillBackground (false);
}

Model_RM_widget::
~Model_RM_widget()
{
    if (pick_ != 0)
        rmPickDelete (pick_);
    pick_       = 0;

    if (select_ != 0)
        rmPickDelete (select_);
    select_       = 0;
}

void Model_RM_widget::
fit_to_screen ()
{
    double center_x, center_y, center_z, scale;
    get_dimensions (center_x, center_y, center_z, scale);
    fit (center_x, center_y, center_z, scale);
}

void Model_RM_widget::
pick (int x, int y)
{
    if (pick_ != 0)
        rmPickDelete (pick_);

    pick_ = rmFramePick (pipe_, root_node_, x, y);
    RMnode * node = rmPickedNode (pick_);

    if (node != 0)
    {
        char * node_name = rmNodeGetName (node);

        bool is_atom = false, is_bond = false;

        if (node_name != 0 && strcmp (node_name, "Atom") == 0)
            is_atom = true;

        else if (node_name != 0 && strcmp (node_name, "Bond") == 0)
            is_bond = true;

        free (node_name);


        void * data = rmNodeGetClientData (node);

        if (data != 0 && is_atom)
        {
            OpenRM_atom * RM_atom = (OpenRM_atom*) (data);

            if (RM_atom == 0)
                FLAW ("RM_atom == 0    @ Model_RM_widget::mousePressEvent");

            Atom_kit & kit  = RM_atom->kit();
            Atom &     atom = kit.atom();
            model_.kit().selector().pick (atom);
//                kit.pick (true);
        }

        if (data != 0 && is_bond)
        {
            OpenRM_bond * RM_bond = 
                reinterpret_cast <OpenRM_bond*> (data);

            if (RM_bond == 0)
                FLAW ("RM_bond == 0    @ Model_RM_widget::mousePressEvent");

            Bond_kit & kit  = RM_bond->kit();
            Bond &     bond = kit.bond();
            model_.kit().selector().pick (bond);
            //kit.select (!kit.is_selected());
        }
    }
}

void Model_RM_widget::
right_X_view (Angle_unit const & angle)
{
    Angle_unit a;
    a.set_rad (-angle.rad());
    left_X_view (a);
}

void Model_RM_widget::
left_X_view (Angle_unit const & angle)
{
    RMmatrix last_transform;
    rmNodeGetRotateMatrix (camera_node_, &last_transform);

    RMmatrix result_transform;
    rmMatrixIdentity (&result_transform);

    double sin_angle = sin (angle.rad ());
    double cos_angle = cos (angle.rad ());

    //  1   0   0
    //  0  cos sin
    //  0 -sin cos

    rmMatrixSetValue (&result_transform, 1, 1, (float) cos_angle);
    rmMatrixSetValue (&result_transform, 1, 2, (float) sin_angle);
    rmMatrixSetValue (&result_transform, 2, 1, (float)-sin_angle);
    rmMatrixSetValue (&result_transform, 2, 2, (float) cos_angle);

    rmMatrixMultiply (&last_transform, &result_transform, &result_transform);
    rmNodeSetRotateMatrix (camera_node_, &result_transform);
    updateGL ();
}

void Model_RM_widget::
right_Y_view (Angle_unit const & angle)
{
    RMmatrix last_transform;
    rmNodeGetRotateMatrix (camera_node_, &last_transform);

    RMmatrix result_transform;
    rmMatrixIdentity (&result_transform);

    double sin_angle = sin (angle.rad ());
    double cos_angle = cos (angle.rad ());

    // cos  0 -sin
    //  0   1   0
    // sin  0  cos 

    rmMatrixSetValue (&result_transform, 0, 0, (float) cos_angle);
    rmMatrixSetValue (&result_transform, 0, 2, (float)-sin_angle);
    rmMatrixSetValue (&result_transform, 2, 0, (float) sin_angle);
    rmMatrixSetValue (&result_transform, 2, 2, (float) cos_angle);

    rmMatrixMultiply (&last_transform, &result_transform, &result_transform);
    rmNodeSetRotateMatrix (camera_node_, &result_transform);
    updateGL ();
}

void Model_RM_widget::
left_Y_view (Angle_unit const & angle)
{
    Angle_unit a;
    a.set_rad (-angle.rad());
    right_Y_view (a);
}

void Model_RM_widget::
right_Z_view (Angle_unit const & angle)
{
    RMmatrix last_transform;
    rmNodeGetRotateMatrix (camera_node_, &last_transform);

    RMmatrix result_transform;
    rmMatrixIdentity (&result_transform);

    double sin_angle = sin (angle.rad ());
    double cos_angle = cos (angle.rad ());

    //  cos sin  0
    // -sin cos  0
    //   0   0   1

    rmMatrixSetValue (&result_transform, 0, 0, (float) cos_angle);
    rmMatrixSetValue (&result_transform, 0, 1, (float) sin_angle);
    rmMatrixSetValue (&result_transform, 1, 0, (float)-sin_angle);
    rmMatrixSetValue (&result_transform, 1, 1, (float) cos_angle);

    rmMatrixMultiply (&last_transform, &result_transform, &result_transform);
    rmNodeSetRotateMatrix (camera_node_, &result_transform);
    updateGL ();
}

void Model_RM_widget::
left_Z_view (Angle_unit const & angle)
{
    Angle_unit a;
    a.set_rad (-angle.rad());
    right_Z_view (a);
}


void Model_RM_widget::
get_dimensions (double &center_x,
                double &center_y,
                double &center_z,
                double &scale)
{
    double atoms = model_.atom_count();
    if (atoms == 0)
    {
        center_x = 0.;
        center_y = 0.;
        center_z = 0.;
        scale    = 0.;
        return;
    }

    const Atom & first_atom = model_.atom(0);

    double sum_x = 0, x_min = first_atom.x(), x_max = first_atom.x();
    double sum_y = 0, y_min = first_atom.y(), y_max = first_atom.y();
    double sum_z = 0, z_min = first_atom.z(), z_max = first_atom.z();

    for (int i=0;  i<atoms;  ++i)
    {
        const Atom & current_atom = model_.atom (i);
        double current_x = current_atom. x();
        double current_y = current_atom. y();
        double current_z = current_atom. z();

        sum_x += current_x;
        sum_y += current_y;
        sum_z += current_z;

        x_min = current_x < x_min ? current_x : x_min;
        x_max = current_x > x_max ? current_x : x_max;
        y_min = current_y < y_min ? current_y : y_min;
        y_max = current_y > y_max ? current_y : y_max;
        z_min = current_z < z_min ? current_z : z_min;
        z_max = current_z > z_max ? current_z : z_max;
    }

    center_x = sum_x / atoms;
    center_y = sum_y / atoms;
    center_z = sum_z / atoms;

    double dx = x_max - x_min;
    double dy = y_max - y_min;
    double dz = z_max - z_min;

    scale = sqrt (dx*dx + dy*dy + dz*dz) + 5.;
}


//void Model_RM_widget::
//fit (double & center_x,
//     double & y_center,
//     double & z_center,
//     double & scale)
//{
//    if (scale == 0)
//        return;
//
//    RMcamera3D *camera;
//    rmNodeGetSceneCamera3D (camera_node_, &camera);
//
//    RMvertex3D  center;
//
//    //rmNodeUnionAllBoxes (root_node_);
//    //rmNodeComputeCenterFromBoundingBox(root_node_);
//    //rmNodeComputeBoundingBox(root_node_);
//
//    double new_eye_distance = 
//        (scale/2) / tan (rmCamera3DGetFOV (camera) * 3.14 / 180 / 2);
//
//    set_eye_distance (new_eye_distance);
//    
//    center.x = -center_x;
//    center.y = -y_center;
//    center.z = -z_center;
//    rmNodeSetTranslateVector (camera_node_, &center);
//
//    center.x = center_x;
//    center.y = y_center;
//    center.z = z_center;
//    rmNodeSetCenter          (camera_node_, &center);
//
//    rmCamera3DDelete (camera);
//    to_user().status ("Fit to screen.");
//    updateGL ();
//}

void Model_RM_widget::
set_level_of_details (int lod)
{
    //fix to Group_of_visual_atoms
    rmPipeSetDisplayListEnable (pipe_, RM_FALSE);

    for (int i=0;  i<model_.atom_count();  ++i)
    {
//        model_.atom(i).kit().openRM().set_level_of_details (lod);
        model_.atom(i).kit().visual().set_level_of_details (lod);
    }

    //rmFrame (rm_widget_.pipe(), rm_widget_.root_node());
    //rmPipeSetDisplayListEnable (rm_widget_.pipe(), RM_TRUE);
}

void Model_RM_widget::
keyPressEvent (QKeyEvent * event)
{
    if (event->key () == Qt::Key_Space)
        fit_to_screen();

    //if (event->key () == Qt::Key_F1)
    //    fit_to_screen();

    if (event->key () == Qt::Key_Escape)
        model_.kit().selector().clear();
}

void Model_RM_widget::
mousePressEvent (QMouseEvent * event)
{
    if (press_ != 0 && press_->execute (event, this))
        return;

    OpenRMWidget::mousePressEvent (event);

    if (event->buttons () == (Qt::RightButton | Qt::LeftButton)) // Move selected molecules
    {
        selection_movement_.x = 0;
        selection_movement_.y = 0;
        selection_movement_.z = 0;
    }
}

void Model_RM_widget::
mouseReleaseEvent (QMouseEvent * event)
{
    if (release_ != 0 && release_->execute (event, this))
        return;

    OpenRMWidget::mouseReleaseEvent (event);

    selection_movement_.x = 0;
    selection_movement_.y = 0;
    selection_movement_.z = 0;

    //QPoint 

    if (((last_press_position_ - event->pos()).manhattanLength ()) < 20)
    {
        if (event->button () == Qt::RightButton)
        {
            // select
            if (select_ != 0)
                rmPickDelete (select_);

            select_ = rmFramePick (pipe_, root_node_, event->x(), event->y());
            RMnode * node = rmPickedNode (select_);

            if (node != 0)
            {
                char * node_name = rmNodeGetName (node);

                bool is_atom = false, is_bond = false;

                if (node_name != 0 && strcmp (node_name, "Atom") == 0)
                    is_atom = true;

                else if (node_name != 0 && strcmp (node_name, "Bond") == 0)
                    is_bond = true;

                free (node_name);

                void * data = rmNodeGetClientData (node);

                if (data != 0 && is_atom)
                {
                    OpenRM_atom * RM_atom = 
                        reinterpret_cast <OpenRM_atom*> (data);

                    if (RM_atom == 0)
                        FLAW ("RM_atom == 0    @ Model_RM_widget::mousePressEvent");

                    Atom_kit & kit  = RM_atom->kit();
                    Atom &     atom = kit.atom();
                    model_.kit().selector().select (atom, !kit.is_selected());
                    //kit.select (!kit.is_selected());
                }

                if (data != 0 && is_bond)
                {
                    OpenRM_bond * RM_bond = 
                        reinterpret_cast <OpenRM_bond*> (data);

                    if (RM_bond == 0)
                        FLAW ("RM_bond == 0    @ Model_RM_widget::mousePressEvent");

                    Bond_kit & kit  = RM_bond->kit();
                    Bond &     bond = kit.bond();
                    model_.kit().selector().select (bond, !kit.is_selected());
                    //kit.select (!kit.is_selected());
                }
            }
        }
        else if (event->button () == Qt::LeftButton)
        {
            pick (event->x(), event->y());
        }
    }
}

void Model_RM_widget::
mouseDoubleClickEvent (QMouseEvent * event)
{
    if (double_click_ != 0 && double_click_->execute (event, this))
        return;

    OpenRMWidget::mouseDoubleClickEvent (event);

    if (event->button () == Qt::RightButton)
    {
        model_.kit().selector().invert_all ();
    }
}

void Model_RM_widget::
movement (QMouseEvent * event, int dX, int dY, 
          double * x, double * y, double * z,
          RMvertex3D * eye, RMvertex3D * up, RMvertex3D * rigth, 
          RMvertex3D * at)
{
    RMcamera3D *camera;
    rmNodeGetSceneCamera3D (camera_node_, &camera);

    RMvertex3D  eye_point, up_point, move, at_point;

    rmCamera3DGetEye      (camera, &eye_point);
    rmCamera3DGetAt       (camera, &at_point);
    rmCamera3DGetUpVector (camera, &up_point);

    rmVertex3DDiff  (&eye_point, &at_point, eye);
    rmVertex3DDiff  (&up_point,  &at_point, up);
    rmVertex3DCross (eye, up,               rigth);

    double eye_distance = rmVertex3DMag (eye);

    rmVertex3DNormalize (up);
    rmVertex3DNormalize (rigth);
    rmVertex3DNormalize (eye);

    double tg_FOV 
        = tan ((rmCamera3DGetFOV (camera) * 3.14159265 / 180));

    double x_movement = double(dX) / height () * eye_distance * tg_FOV;
    double y_movement = double(dY) / height () * eye_distance * tg_FOV;
    double z_movement = y_movement;

    //fix
    //RMvertex3D v;
    rmVertex3DCross (rigth, eye, up); 
    rmVertex3DNormalize (up);

    //RMmatrix view, projection;
    //rmCamera3DComputeViewMatrix (camera, &view,	&projection);
    RMmatrix view;
    rmNodeGetCompositeModelMatrix (camera_node_, &view);
    rmMatrixTranspose (&view, &view);
    //rmMatrixInverse  (&view, &view);

    rmPointMatrixTransform (up,    &view, up);
    rmPointMatrixTransform (rigth, &view, rigth);
    rmPointMatrixTransform (eye,   &view, eye);

    //float v4[4];
    //v4[0] = at_point.x;
    //v4[1] = at_point.y;
    //v4[2] = at_point.z; 
    //v4[3] = 1.f;
    //rmPoint4MatrixTransform (v4,  &view, v4);
    //at->x = v4[0];
    //at->y = v4[1];
    //at->z = v4[2];
    ///rmPointMatrixTransform (&at_point,  &view, at);
    rmNodeGetTranslateVector (camera_node_, at);

    rmCamera3DDelete (camera);  
    camera = 0;

    if (event->modifiers() == Qt::ShiftModifier ) // Z Move
    {
        *x = -eye->x * z_movement;
        *y = -eye->y * z_movement;
        *z = -eye->z * z_movement;

    }
    else // XY Move
    {
        up->x *= y_movement;
        up->y *= y_movement;
        up->z *= y_movement;

        rigth->x *= x_movement;
        rigth->y *= x_movement;
        rigth->z *= x_movement;

        rmVertex3DSum (up, rigth, &move);

        *x = move.x;
        *y = move.y;
        *z = move.z;
    }
}

void Model_RM_widget::
mouseMoveEvent (QMouseEvent * event)
{
    if (move_ != 0 && move_->execute (event, this))
        return;

    if (event->buttons () == (Qt::RightButton | Qt::LeftButton)) // Move selected molecules
    {
        int dX = event->x() - last_position_.x();
        int dY = event->y() - last_position_.y();
        double x, y, z;
        RMvertex3D eye_vector, up_vector, rigth_vector, at_point;
        movement (event, dX, dY,    
            &x, &y, &z, 
            &eye_vector, &up_vector, &rigth_vector, &at_point);

        if (event->modifiers() == Qt::ShiftModifier ||
            event->modifiers() == Qt::ControlModifier) // Zoom
        {
            selection_movement_.x += x;
            selection_movement_.y += y;
            selection_movement_.z += z;
        }
        else // Rotate
        {
            RMvertex3D  move;
            move.x = x;     move.y = y;     move.z = z;     

            Selected_atoms selected (model_);
            Mass_center center (selected);
            Point_3D_impl zero(0,0,0), mass_center(0,0,0);
            center.evaluate (mass_center);
            Vector_3D_impl shift(0,0,0);
            zero.difference (mass_center, &shift);

            std::set <Molecule *> mol;
            for (int i=0;  i<selected.atom_count();  ++i)
                mol.insert (&selected.atom(i).kit().in_molecule());

            for (std::set<Molecule *>::iterator it=mol.begin();  
                 it!=mol.end();  ++it)
            {
                double      teta;
                RMvertex3D  around;
                rmVertex3DCross (&move, &eye_vector, &around);
                rmVertex3DMagNormalize (&around, &teta);

                Vector_3D_impl vect (around.x, 
                                     around.y, 
                                     around.z);

                (*it)->move (shift);
                (*it)->rotate (vect, 0.1*teta);
                (*it)->move (shift.negate());
            }
        }

        last_position_ = event->pos();
        updateGL ();
    }
    else
        OpenRMWidget::mouseMoveEvent (event);
}

void Model_RM_widget::
paintGL ()
{
    if (selection_movement_.x != 0 || 
        selection_movement_.y != 0 || 
        selection_movement_.z != 0)
    {
            Selected_atoms selected (model_);
            std::set <Molecule *> mol;
            for (int i=0;  i<selected.atom_count();  ++i)
                mol.insert (&selected.atom(i).kit().in_molecule());

            for (std::set<Molecule *>::iterator it=mol.begin();  
                 it!=mol.end();  ++it)
            {
                Vector_3D_impl vect (-selection_movement_.x, 
                                     -selection_movement_.y, 
                                     -selection_movement_.z);
                (*it)->move (vect);
            }

        selection_movement_.x = 0;
        selection_movement_.y = 0;
        selection_movement_.z = 0;
    }

    OpenRMWidget::paintGL ();
}

}//MM
