#include "Scene_edit_box_item.h"
#include <QApplication>
#include <CGAL/Three/Viewer_interface.h>
#include <CGAL/Qt/manipulatedFrame.h>
#include <CGAL/Qt/constraint.h>
#include <QMouseEvent>
#include <QOpenGLFramebufferObject>
#include <QOpenGLShaderProgram>
#include <QCursor>

using namespace CGAL::Three;
struct Scene_edit_box_item::vertex{
  int id;
  double *x;
  double *y;
  double *z;

  CGAL::Point_3<Kernel> position()const
  {
    return  CGAL::Point_3<Kernel>(*x,*y,*z);
  }
  double operator[](int i)
  {
    switch(i)
    {
    case 0:
      return *x;
    case 1:
      return *y;
    case 2:
      return *z;
    default:
      return 0;
    }
  }
};
struct Scene_edit_box_item::edge{
  vertex* source;
  vertex* target;
};
struct Scene_edit_box_item::face{
  vertex* vertices[4];
};

struct Scene_edit_box_item_priv{
  typedef CGAL::Simple_cartesian<double>  Kernel;
  enum VAOs{
    Edges = 0,
    Spheres,
    Faces,
    S_Edges,
    S_Spheres,
    S_Faces,
    P_Edges,
    P_Spheres,
    P_Faces,
    NumberOfVaos
  };
  enum VBOs{
    VertexEdges = 0,
    ColorsEdges,
    VertexSpheres,
    NormalSpheres,
    CenterSpheres,
    ColorsSpheres,
    VertexFaces,
    NormalFaces,
    ColorsFaces,
    S_Vertex,
    S_Normal,
    NumberOfVbos
  };
  enum HL_Primitive{
    VERTEX=0,
    EDGE,
    FACE,
    NO_TYPE
  };

  Scene_edit_box_item_priv(const Scene_interface *scene_interface, Scene_edit_box_item* ebi)
  {
    const CGAL::qglviewer::Vec offset = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
    ready_to_hl = true;
    scene = scene_interface;
    item = ebi;
    selection_on = false;
    Scene_item::Bbox bb = scene->bbox();
    double x=(bb.xmin()+bb.xmax())/2;
    double y=(bb.ymin()+bb.ymax())/2;
    double z=(bb.zmin()+bb.zmax())/2;
    center_ = CGAL::qglviewer::Vec(x,y,z);
    relative_center_ = CGAL::qglviewer::Vec(0,0,0);
    remodel_frame = new Scene_item::ManipulatedFrame();
    remodel_frame->setTranslationSensitivity(1.0);
    frame = new Scene_item::ManipulatedFrame();
    frame->setPosition(center_+offset);
    frame->setSpinningSensitivity(100.0); //forbid spinning
    constraint.setRotationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::AXIS);
    constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE);
    constraint.setRotationConstraintDirection(CGAL::qglviewer::Vec(.0,.0,.1));
    frame->setConstraint(&constraint);
    //create the sphere model
    pool[0] = bb.xmin(); pool[3] = bb.xmax();
    pool[1] = bb.ymin(); pool[4] = bb.ymax();
    pool[2] = bb.zmin(); pool[5] = bb.zmax();

    vertex_spheres.resize(0);
    normal_spheres.resize(0);
    create_flat_sphere(1.0f, vertex_spheres, normal_spheres,10);
    //      5-----6
    //  .   |  .  |
    // 4------7   |
    // |    | |   |
    // |    1-|---2
    // | .    |.
    // 0------3

    //vertices
    for( int i = 0; i< 8; ++i)
    {

      vertices[i].x = ((i/2)%2==0)? &pool[0]:&pool[3];
      vertices[i].y = (i/4==0)? &pool[1]:&pool[4];
      vertices[i].z = (((i+1)/2)%2==1)? &pool[2]:&pool[5];
      vertices[i].id = i;
    }

    //      .--5--.
    //  4   |  6  |
    // .--7-1-.   2
    // |    | |   |
    // 0    .-39--.
    // | 8    |10
    // .--11--.

    //edges
    for( int i=0; i<12; ++i)
    {
      if(i<4)
      {
        edges[i].source = &vertices[i];
        edges[i].target = &vertices[i+4];
      }
      else if(i<8)
      {
        edges[i].source = &vertices[i];
        edges[i].target = &vertices[(i+1)%4 +4];
      }
      else
      {
        edges[i].source = &vertices[i%4];
        edges[i].target = &vertices[(i+1) %4];
      }
      vertex_edges.resize(0);
    }

    //
    //                          5------6
    //                          |      |
    //      .-----.             |  2   |
    //  .   |5 .  |             |      |
    // .------.2  |      5------1------2------6------5
    // | 1  | |   |      |      |      |      |      |
    // |   4.-|-3-.      |   1  |   0  |  3   |   5  |
    // | .  0 |.         |      |      |      |      |
    // .------.          4------0------3------7------4
    //                          |      |
    //                          |  4   |
    //                          |      |
    //                          4------7


    //faces
    for( int i=0; i<4; ++i)
    {
      faces[0].vertices[i] = &vertices[i];
    }

    for( int i=1; i<4; ++i)
    {
      faces[i].vertices[0] = &vertices[i];
      faces[i].vertices[1] = &vertices[(i-1)];
      faces[i].vertices[2] = &vertices[i+3];
      faces[i].vertices[3] = &vertices[i+4];
    }

    faces[4].vertices[0] = &vertices[0];
    faces[4].vertices[1] = &vertices[3];
    faces[4].vertices[2] = &vertices[7];
    faces[4].vertices[3] = &vertices[4];

    for( int i=0; i<4; ++i)
    {
      faces[5].vertices[i] = &vertices[i+4];
    }

    vertex_faces.resize(0);
    normal_faces.resize(0);

    for(int i=0; i<8; ++i)
      for(int j=0; j<3; ++j)
        last_pool[i][j] = vertices[i][j];

    if(QOpenGLContext::currentContext()->format().majorVersion() >= 3)
    {
      pick_sphere_program.addShaderFromSourceFile(QOpenGLShader::Vertex,":/cgal/Polyhedron_3/resources/shader_spheres.v");
      pick_sphere_program.addShaderFromSourceFile(QOpenGLShader::Fragment,":/cgal/Polyhedron_3/resources/shader_without_light.f");
    }
    else
    {
      pick_sphere_program.addShaderFromSourceFile(QOpenGLShader::Vertex,":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_spheres.v");
      pick_sphere_program.addShaderFromSourceFile(QOpenGLShader::Fragment,":/cgal/Polyhedron_3/resources/compatibility_shaders/shader_without_light.f");
    }
    pick_sphere_program.bindAttributeLocation("colors", 1);
    pick_sphere_program.link();


    //Vertex source code
    const char vertex_source[] =
    {
      "#version 150 \n"
      "in vec4 vertex;            "
      "in vec3 normals;           "
      "in vec4 colors;            "
      "uniform mat4 mvp_matrix;          "
      "uniform mat4 mv_matrix;           "
      "out vec4 fP;                  "
      "out vec3 fN;                  "
      "out vec4 color;               "
      "void main(void)                         "
      "{                                       "
      "   color = colors;                      "
      "   fP = mv_matrix * vertex;             "
      "   mat3 mv_matrix_3;                    "
      "   mv_matrix_3[0] = mv_matrix[0].xyz;   "
      "   mv_matrix_3[1] = mv_matrix[1].xyz;   "
      "   mv_matrix_3[2] = mv_matrix[2].xyz;   "
      "   fN = mv_matrix_3* normals;           "
      "   gl_Position = mvp_matrix * vertex;   "
      "}\n                                     "
      "\n                                      "
    };
    
    const char vertex_source_comp[] =
    {
      "attribute highp vec4 vertex;            "
      "attribute highp vec3 normals;           "
      "attribute highp vec4 colors;            "
      "uniform highp mat4 mvp_matrix;          "
      "uniform highp mat4 mv_matrix;           "
      "varying highp vec4 fP;                  "
      "varying highp vec3 fN;                  "
      "varying highp vec4 color;               "
      "void main(void)                         "
      "{                                       "
      "   color = colors;                      "
      "   fP = mv_matrix * vertex;             "
      "   mat3 mv_matrix_3;                    "
      "   mv_matrix_3[0] = mv_matrix[0].xyz;   "
      "   mv_matrix_3[1] = mv_matrix[1].xyz;   "
      "   mv_matrix_3[2] = mv_matrix[2].xyz;   "
      "   fN = mv_matrix_3* normals;           "
      "   gl_Position = mvp_matrix * vertex;   "
      "}\n                                     "
      "\n                                      "
    };

    //Fragment source code
    const char fragment_source[] =
    {
      "#version 150 \n"
      "in vec4 color;"
      "in vec4 fP; "
      "in vec3 fN; "
      "uniform vec4 light_pos;  "
      "uniform vec4 light_diff; "
      "uniform vec4 light_spec; "
      "uniform vec4 light_amb;  "
      "uniform float spec_power ; "
      "uniform int is_two_side; "
      "uniform bool is_selected;"
      "out vec4 out_color; \n"
      "void main(void) {"
      " vec3 L = light_pos.xyz - fP.xyz;"
      " vec3 V = -fP.xyz;"
      " vec3 N;"
      "if(fN == vec3(0.0,0.0,0.0)) "
      "N = vec3(0.0,0.0,0.0);"
      "else "
      "N = normalize(fN);"
      "L = normalize(L);"
      "V = normalize(V);"
      "vec3 R = reflect(-L, N);"
      "vec4 diffuse;"
      "if(is_two_side == 1) "
      "diffuse = abs(dot(N,L)) * light_diff * color;"
      "else "
      "diffuse = max(dot(N,L), 0.0) * light_diff * color;"
      "vec4 specular = pow(max(dot(R,V), 0.0), spec_power) * light_spec;"
      "vec4 ret_color = vec4((color*light_amb).xyz + diffuse.xyz + specular.xyz,1);"
      "if(is_selected) "
      "  out_color = vec4(ret_color.r+70.0/255.0, ret_color.g+70.0/255.0, ret_color.b+70.0/255.0, color.a);"
      "else "
      "  out_color = vec4(ret_color.rgb, color.a); }\n"
      "\n"
    };
    //Fragment source code
    const char fragment_source_comp[] =
    {
      "varying highp vec4 color;"
      "varying highp vec4 fP; "
      "varying highp vec3 fN; "
      "uniform highp vec4 light_pos;  "
      "uniform highp vec4 light_diff; "
      "uniform highp vec4 light_spec; "
      "uniform highp vec4 light_amb;  "
      "uniform highp float spec_power ; "
      "uniform int is_two_side; "
      "uniform bool is_selected;"
      "void main(void) {"
      "highp vec3 L = light_pos.xyz - fP.xyz;"
      "highp vec3 V = -fP.xyz;"
      "highp vec3 N;"
      "if(fN == highp vec3(0.0,0.0,0.0)) "
      "N = highp vec3(0.0,0.0,0.0);"
      "else "
      "N = normalize(fN);"
      "L = normalize(L);"
      "V = normalize(V);"
      "highp vec3 R = reflect(-L, N);"
      "vec4 diffuse;"
      "if(is_two_side == 1) "
      "diffuse = abs(dot(N,L)) * light_diff * color;"
      "else "
      "diffuse = max(dot(N,L), 0.0) * light_diff * color;"
      "highp vec4 specular = pow(max(dot(R,V), 0.0), spec_power) * light_spec;"
      "vec4 ret_color = vec4((color*light_amb).xyz + diffuse.xyz + specular.xyz,1);"
      "if(is_selected) "
      "gl_FragColor = vec4(ret_color.r+70.0/255.0, ret_color.g+70.0/255.0, ret_color.b+70.0/255.0, color.a);"
      "else "
      "gl_FragColor = vec4(ret_color.rgb, color.a); }\n"
      "\n"
    };
    
    if(QOpenGLContext::currentContext()->format().majorVersion() >= 3)
    {
      transparent_face_program.addShaderFromSourceCode(QOpenGLShader::Vertex,vertex_source);
      transparent_face_program.addShaderFromSourceCode(QOpenGLShader::Fragment,fragment_source);
    }
    else
    {
      transparent_face_program.addShaderFromSourceCode(QOpenGLShader::Vertex,vertex_source_comp);
      transparent_face_program.addShaderFromSourceCode(QOpenGLShader::Fragment,fragment_source_comp);
    }
    transparent_face_program.bindAttributeLocation("colors", 1);
    transparent_face_program.link();
    reset_selection();
    last_picked_id = -1;
    last_picked_type = -1;
    QPixmap pix(":/cgal/cursors/resources/rotate_around_cursor.png");
    rotate_cursor = QCursor(pix);
  }
  ~Scene_edit_box_item_priv(){
    delete frame;
    delete remodel_frame;
  }
  mutable std::vector<float> vertex_edges;
  mutable std::vector<float> color_edges;
  mutable std::vector<float> vertex_spheres;
  mutable std::vector<float> normal_spheres;
  mutable std::vector<float> center_spheres;
  mutable std::vector<float> color_spheres;
  mutable std::vector<float> vertex_faces;
  mutable std::vector<float> normal_faces;
  mutable std::vector<float> color_faces;
  mutable std::vector<float> hl_vertex;
  mutable std::vector<float> hl_normal;

  double pool[6];
  //id|coord
  double last_pool[8][3];
  bool ready_to_hl;


  CGAL::qglviewer::ManipulatedFrame* frame;
  CGAL::qglviewer::ManipulatedFrame* remodel_frame;
  CGAL::qglviewer::Vec rf_last_pos;
  CGAL::qglviewer::LocalConstraint constraint;
  CGAL::qglviewer::Vec center_;
  CGAL::qglviewer::Vec relative_center_;

  mutable QOpenGLShaderProgram pick_sphere_program;
  mutable QOpenGLShaderProgram transparent_face_program;
  mutable Scene_edit_box_item::vertex vertices[8];
  mutable Scene_edit_box_item::edge edges[12];
  mutable Scene_edit_box_item::face faces[6];

  std::vector<Scene_edit_box_item::vertex*> selected_vertices;
  void reset_selection();
  bool selection_on;
  void picking(int& type, int& id, Viewer_interface *viewer);

  mutable QOpenGLShaderProgram* program;
  void initializeBuffers(Viewer_interface *viewer)const;

  void computeElements() const;
  void draw_picking(Viewer_interface*);
  void remodel_box(const QVector3D &dir);
  double applyX(int id, double x, double dirx);
  double applyY(int id, double y, double diry);
  double applyZ(int id, double z, double dirz);
  const Scene_interface* scene;
  Scene_edit_box_item* item;
  QPoint picked_pixel;
  HL_Primitive hl_type;
  int last_picked_id;
  int last_picked_type;
  QCursor rotate_cursor;

};

Scene_edit_box_item::Scene_edit_box_item()
{
  d = NULL;
}
Scene_edit_box_item::Scene_edit_box_item(const Scene_interface *scene_interface)
  :  Scene_item(Scene_edit_box_item_priv::NumberOfVbos,Scene_edit_box_item_priv::NumberOfVaos)

{
  d = new Scene_edit_box_item_priv(scene_interface, this);

  are_buffers_filled = false;
  CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin();
  viewer->setMouseTracking(true);
}
QString Scene_edit_box_item::toolTip() const {

  return QString();
}

void Scene_edit_box_item::drawSpheres(Viewer_interface *viewer, const QMatrix4x4 f_matrix ) const
{
  vaos[Scene_edit_box_item_priv::Spheres]->bind();
  GLdouble d_mat[16];
  QMatrix4x4 mvp_mat;
  viewer->camera()->getModelViewProjectionMatrix(d_mat);
  for (int i=0; i<16; ++i)
    mvp_mat.data()[i] = GLfloat(d_mat[i]);
  mvp_mat = mvp_mat*f_matrix;
  QMatrix4x4 mv_mat;
  viewer->camera()->getModelViewMatrix(d_mat);
  for (int i=0; i<16; ++i)
    mv_mat.data()[i] = GLfloat(d_mat[i]);
  mv_mat = mv_mat*f_matrix;
  QVector4D light_pos(0.0f,0.0f,1.0f, 1.0f );
  light_pos = light_pos*f_matrix;
  double radius =std::sqrt(
      (point(6,0) - point(0,0)) * (point(6,0) - point(0,0)) +
      (point(6,1) - point(0,1)) * (point(6,1) - point(0,1)) +
      (point(6,2) - point(0,2)) * (point(6,2) - point(0,2))) *0.02 ;

  attribBuffers(viewer, PROGRAM_SPHERES);
  d->program = getShaderProgram(PROGRAM_SPHERES, viewer);
  d->program->bind();
  d->program->setUniformValue("mvp_matrix", mvp_mat);
  d->program->setUniformValue("mv_matrix", mv_mat);
  d->program->setUniformValue("light_pos", light_pos);
  d->program->setUniformValue("is_clipbox_on", false);
  d->program->setAttributeValue("radius",radius);
  d->program->setAttributeValue("colors", QColor(Qt::red));
  viewer->glDrawArraysInstanced(GL_TRIANGLES, 0,
                                static_cast<GLsizei>(d->vertex_spheres.size()/3),
                                static_cast<GLsizei>(8));
  d->program->release();
  vaos[Scene_edit_box_item_priv::Spheres]->release();
}

void Scene_edit_box_item::draw(Viewer_interface *viewer) const
{
  if (!are_buffers_filled)
  {
    d->computeElements();
    d->initializeBuffers(viewer);
  }
  QMatrix4x4 f_matrix;
  for (int i=0; i<16; ++i){
    f_matrix.data()[i] = (float)d->frame->matrix()[i];
  }

  drawSpheres(viewer, f_matrix);
  
}

void Scene_edit_box_item::drawEdges(Viewer_interface* viewer) const
{
  if(!are_buffers_filled)
  {
    d->computeElements();
    d->initializeBuffers(viewer);
  }
  QMatrix4x4 f_matrix;
  for (int i=0; i<16; ++i){
    f_matrix.data()[i] = (float)d->frame->matrix()[i];
  }
  vaos[Scene_edit_box_item_priv::Edges]->bind();
  if(!viewer->isOpenGL_4_3())
  {
    attribBuffers(viewer, PROGRAM_WITHOUT_LIGHT);
    d->program = getShaderProgram(PROGRAM_WITHOUT_LIGHT);
    d->program->bind();
    d->program->setUniformValue("is_clipbox_on", false);
  }
  else
  {
    attribBuffers(viewer, PROGRAM_SOLID_WIREFRAME);
    d->program = getShaderProgram(PROGRAM_SOLID_WIREFRAME);
    d->program->bind();
    QVector2D vp(viewer->width(), viewer->height());
    d->program->setUniformValue("viewport", vp);
    d->program->setUniformValue("is_clipbox_on", false);
    d->program->setUniformValue("near",(GLfloat)viewer->camera()->zNear());
    d->program->setUniformValue("far",(GLfloat)viewer->camera()->zFar());
    d->program->setUniformValue("width", 6.0f);
  }
  d->program->setUniformValue("f_matrix", f_matrix);
  d->program->setAttributeValue("colors", QColor(Qt::black));
  viewer->glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(d->vertex_edges.size()/3));
  vaos[Scene_edit_box_item_priv::Edges]->release();
  d->program->release();
  if(renderingMode() == Wireframe)
  {
    drawSpheres(viewer, f_matrix);
  }
  drawHl(viewer);
  drawTransparent(viewer);
}

void Scene_edit_box_item::compute_bbox() const
{
  const CGAL::qglviewer::Vec offset = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();


  QVector3D min(d->pool[0], d->pool[1], d->pool[2]);
  QVector3D max(d->pool[3], d->pool[4], d->pool[5]);

  for(int i=0; i< 3; ++i)
  {
    min[i] += d->frame->translation()[i]-d->center_[i]-offset[i];
    max[i] += d->frame->translation()[i]-d->center_[i]-offset[i];
  }

  _bbox = Scene_item::Bbox(min.x(),min.y(),min.z(),max.x(),max.y(),max.z());
}


void
Scene_edit_box_item_priv::initializeBuffers(Viewer_interface *viewer)const
{

  //vao containing the data for the lines
  {
    if(viewer->isOpenGL_4_3())
      program = item->getShaderProgram(Scene_edit_box_item::PROGRAM_SOLID_WIREFRAME, viewer);
    else
      program = item->getShaderProgram(Scene_edit_box_item::PROGRAM_WITHOUT_LIGHT, viewer);
    program->bind();

    item->vaos[Edges]->bind();
    item->buffers[VertexEdges].bind();
    item->buffers[VertexEdges].allocate(vertex_edges.data(),
                                        static_cast<GLsizei>(vertex_edges.size()*sizeof(float)));
    program->enableAttributeArray("vertex");
    program->setAttributeBuffer("vertex",GL_FLOAT,0,3);
    item->buffers[VertexEdges].release();
    item->vaos[Edges]->release();

    item->vaos[P_Edges]->bind();
    item->buffers[VertexEdges].bind();
    program->enableAttributeArray("vertex");
    program->setAttributeBuffer("vertex",GL_FLOAT,0,3);
    item->buffers[VertexEdges].release();
    item->buffers[ColorsEdges].bind();
    item->buffers[ColorsEdges].allocate(color_edges.data(),
                                        static_cast<GLsizei>(color_edges.size()*sizeof(float)));
    program->enableAttributeArray("colors");
    program->setAttributeBuffer("colors",GL_FLOAT,0,3);
    item->buffers[ColorsEdges].release();
    item->vaos[P_Edges]->release();
    program->release();

  }

  //vao containing the data for the spheres
  {
    program = item->getShaderProgram(Scene_edit_box_item::PROGRAM_SPHERES, viewer);
    item->attribBuffers(viewer, Scene_edit_box_item::PROGRAM_SPHERES);

    program->bind();
    item->vaos[Spheres]->bind();
    item->buffers[VertexSpheres].bind();
    item->buffers[VertexSpheres].allocate(vertex_spheres.data(),
                                          static_cast<int>(vertex_spheres.size()*sizeof(float)));
    program->enableAttributeArray("vertex");
    program->setAttributeBuffer("vertex", GL_FLOAT, 0, 3);
    item->buffers[VertexSpheres].release();

    item->buffers[NormalSpheres].bind();
    item->buffers[NormalSpheres].allocate(normal_spheres.data(),
                                          static_cast<int>(normal_spheres.size()*sizeof(float)));
    program->enableAttributeArray("normals");
    program->setAttributeBuffer("normals", GL_FLOAT, 0, 3);
    item->buffers[NormalSpheres].release();

    item->buffers[CenterSpheres].bind();
    item->buffers[CenterSpheres].allocate(center_spheres.data(),
                                          static_cast<int>(center_spheres.size()*sizeof(float)));
    program->enableAttributeArray("center");
    program->setAttributeBuffer("center", GL_FLOAT, 0, 3);
    item->buffers[CenterSpheres].release();
    program->disableAttributeArray("radius");
    program->disableAttributeArray("colors");

    viewer->glVertexAttribDivisor(program->attributeLocation("center"), 1);
    viewer->glVertexAttribDivisor(program->attributeLocation("radius"), 1);
    item->vaos[Spheres]->release();
    program->release();

    pick_sphere_program.bind();
    pick_sphere_program.bind();
    item->vaos[P_Spheres]->bind();
    item->buffers[VertexSpheres].bind();
    pick_sphere_program.enableAttributeArray("vertex");
    pick_sphere_program.setAttributeBuffer("vertex", GL_FLOAT, 0, 3);
    item->buffers[VertexSpheres].release();

    item->buffers[NormalSpheres].bind();
    pick_sphere_program.disableAttributeArray("normals");
    pick_sphere_program.setAttributeValue("normals",QVector3D(0,0,0));
    item->buffers[NormalSpheres].release();

    item->buffers[CenterSpheres].bind();
    pick_sphere_program.enableAttributeArray("center");
    pick_sphere_program.setAttributeBuffer("center", GL_FLOAT, 0, 3);
    item->buffers[CenterSpheres].release();

    pick_sphere_program.disableAttributeArray("radius");
    viewer->glVertexAttribDivisor(program->attributeLocation("radius"), 1);

    item->buffers[ColorsSpheres].bind();
    item->buffers[ColorsSpheres].allocate(color_spheres.data(),
                                          static_cast<int>(color_spheres.size()*sizeof(float)));
    pick_sphere_program.enableAttributeArray("colors");
    pick_sphere_program.setAttributeBuffer("colors", GL_FLOAT, 0, 3);
    item->buffers[ColorsSpheres].release();

    viewer->glVertexAttribDivisor(program->attributeLocation("center"), 1);
    viewer->glVertexAttribDivisor(program->attributeLocation("colors"), 1);
    item->vaos[P_Spheres]->release();
    pick_sphere_program.release();
  }

  //vao containing the data for the faces
  {
    program = item->getShaderProgram(Scene_edit_box_item::PROGRAM_WITH_LIGHT, viewer);
    item->attribBuffers(viewer, Scene_edit_box_item::PROGRAM_WITH_LIGHT);

    program->bind();
    item->vaos[Faces]->bind();
    item->buffers[VertexFaces].bind();
    item->buffers[VertexFaces].allocate(vertex_faces.data(),
                                        static_cast<int>(vertex_faces.size()*sizeof(float)));
    program->enableAttributeArray("vertex");
    program->setAttributeBuffer("vertex", GL_FLOAT, 0, 3);
    item->buffers[VertexFaces].release();

    item->buffers[NormalFaces].bind();
    item->buffers[NormalFaces].allocate(normal_faces.data(),
                                        static_cast<int>(normal_faces.size()*sizeof(float)));
    program->enableAttributeArray("normals");
    program->setAttributeBuffer("normals", GL_FLOAT, 0, 3);
    item->buffers[NormalFaces].release();
    item->vaos[Faces]->release();
    program->release();


    program = item->getShaderProgram(Scene_edit_box_item::PROGRAM_WITHOUT_LIGHT, viewer);
    item->attribBuffers(viewer, Scene_edit_box_item::PROGRAM_WITHOUT_LIGHT);
    program->bind();
    item->vaos[P_Faces]->bind();
    item->buffers[VertexFaces].bind();
    program->enableAttributeArray("vertex");
    program->setAttributeBuffer("vertex", GL_FLOAT, 0, 3);
    item->buffers[VertexFaces].release();

    item->buffers[ColorsFaces].bind();
    item->buffers[ColorsFaces].allocate(color_faces.data(),
                                        static_cast<int>(color_faces.size()*sizeof(float)));
    program->enableAttributeArray("colors");
    program->setAttributeBuffer("colors", GL_FLOAT, 0, 3);
    item->buffers[ColorsFaces].release();

    item->vaos[P_Faces]->release();
    program->release();
  }
  item->are_buffers_filled = true;
}

void push_xyz(std::vector<float> &v,
              const Scene_edit_box_item::Kernel::Point_3& p,
              CGAL::qglviewer::Vec center_ = CGAL::qglviewer::Vec(0,0,0))
{
  v.push_back(p.x()-center_.x);
  v.push_back(p.y()-center_.y);
  v.push_back(p.z()-center_.z);
}

void push_normal(std::vector<float> &v, int id)
{
  switch(id)
  {
  case 0:
    v.push_back(0);
    v.push_back(-1);
    v.push_back(0);
    break;
  case 1:
    v.push_back(-1);
    v.push_back(0);
    v.push_back(0);
    break;
  case 2:
    v.push_back(0);
    v.push_back(0);
    v.push_back(-1);
    break;
  case 3:
    v.push_back(1);
    v.push_back(0);
    v.push_back(0);
    break;
  case 4:
    v.push_back(0);
    v.push_back(0);
    v.push_back(1);
    break;
  case 5:
    v.push_back(0);
    v.push_back(1);
    v.push_back(0);
    break;
  default:
    break;
  }
}

void Scene_edit_box_item_priv::computeElements() const
{
  vertex_edges.clear();
  vertex_faces.clear();
  normal_faces.clear();
  center_spheres.clear();
  color_edges.clear();
  color_faces.clear();
  color_spheres.clear();

  //edges
  for( int i=0; i<12; ++i)
  {
    if(i<4)
    {
      vertex_edges.push_back(vertices[i].position().x()-center_.x);
      vertex_edges.push_back(vertices[i].position().y()-center_.y);
      vertex_edges.push_back(vertices[i].position().z()-center_.z);

      center_spheres.push_back(vertices[i].position().x()-center_.x);
      center_spheres.push_back(vertices[i].position().y()-center_.y);
      center_spheres.push_back(vertices[i].position().z()-center_.z);

      vertex_edges.push_back(vertices[i+4].position().x()-center_.x);
      vertex_edges.push_back(vertices[i+4].position().y()-center_.y);
      vertex_edges.push_back(vertices[i+4].position().z()-center_.z);

    }
    else if(i<8)
    {
      vertex_edges.push_back(vertices[i].position().x()-center_.x);
      vertex_edges.push_back(vertices[i].position().y()-center_.y);
      vertex_edges.push_back(vertices[i].position().z()-center_.z);

      center_spheres.push_back(vertices[i].position().x()-center_.x);
      center_spheres.push_back(vertices[i].position().y()-center_.y);
      center_spheres.push_back(vertices[i].position().z()-center_.z);

      vertex_edges.push_back(vertices[(i+1)%4 +4].position().x()-center_.x);
      vertex_edges.push_back(vertices[(i+1)%4 +4].position().y()-center_.y);
      vertex_edges.push_back(vertices[(i+1)%4 +4].position().z()-center_.z);
    }
    else
    {
      vertex_edges.push_back(vertices[i%4].position().x()-center_.x);
      vertex_edges.push_back(vertices[i%4].position().y()-center_.y);
      vertex_edges.push_back(vertices[i%4].position().z()-center_.z);

      vertex_edges.push_back(vertices[(i+1) %4].position().x()-center_.x);
      vertex_edges.push_back(vertices[(i+1) %4].position().y()-center_.y);
      vertex_edges.push_back(vertices[(i+1) %4].position().z()-center_.z);
    }
    color_edges.push_back(0);
    color_edges.push_back((20.0*i+10)/255);
    color_edges.push_back(0);

    color_edges.push_back(0);
    color_edges.push_back((20.0*i+10)/255);
    color_edges.push_back(0);

  }
  //faces
  for( int i=0; i<6; ++i)
  {
    push_xyz(vertex_faces, faces[i].vertices[0]->position(), center_);
    push_xyz(vertex_faces, faces[i].vertices[3]->position(), center_);
    push_xyz(vertex_faces, faces[i].vertices[2]->position(), center_);

    push_xyz(vertex_faces, faces[i].vertices[0]->position(), center_);
    push_xyz(vertex_faces, faces[i].vertices[2]->position(), center_);
    push_xyz(vertex_faces, faces[i].vertices[1]->position(), center_);

    for( int j=0; j<6; ++j)
    {
      push_normal(normal_faces, i);

      color_faces.push_back(0);
      color_faces.push_back(0);
      color_faces.push_back((20.0*i+10)/255);
    }
  }

  //spheres
  for( int i=0; i<8; ++i)
  {
    color_spheres.push_back((20.0*i+10)/255);
    color_spheres.push_back(0);
    color_spheres.push_back(0);
  }
}

Scene_edit_box_item::~Scene_edit_box_item()
{
  CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin();
  viewer->setMouseTracking(false);

  delete d;
}

// Indicate if rendering mode is supported
bool Scene_edit_box_item::supportsRenderingMode(RenderingMode m) const {
  return (m==Wireframe || m==FlatPlusEdges);
}

Scene_item::ManipulatedFrame*
Scene_edit_box_item::manipulatedFrame()
{
  return d->frame;
}

double Scene_edit_box_item::point(short i, short j) const
{
  CGAL::qglviewer::Vec pos(d->vertices[i].position().x()-d->center_.x,
                     d->vertices[i].position().y()-d->center_.y,
                     d->vertices[i].position().z()-d->center_.z);
  return (d->frame->inverseCoordinatesOf(pos))[j];
}

void Scene_edit_box_item::highlight(Viewer_interface *viewer)
{
  d->ready_to_hl = true;
  viewer->makeCurrent();
  int type = -1, id = -1;
  //pick
  if(!d->selection_on)
  {
    d->picking(type, id, viewer);
    d->last_picked_id = id;
    d->last_picked_type = type;
  }
  //highlight
  d->hl_normal.clear();
  d->hl_vertex.clear();
  if(type !=-1)
  {
    switch(d->last_picked_type)
    {
    case 0:
    {
      //compute
      d->hl_vertex.push_back(d->vertices[d->last_picked_id].position().x()-d->center_.x);
      d->hl_vertex.push_back(d->vertices[d->last_picked_id].position().y()-d->center_.y);
      d->hl_vertex.push_back(d->vertices[d->last_picked_id].position().z()-d->center_.z);
      //fill buffers
      d->program = getShaderProgram(Scene_edit_box_item::PROGRAM_SPHERES, viewer);
      d->program->bind();

      vaos[Scene_edit_box_item_priv::S_Spheres]->bind();
      buffers[Scene_edit_box_item_priv::VertexSpheres].bind();
      buffers[Scene_edit_box_item_priv::VertexSpheres].allocate(d->vertex_spheres.data(),
                                      static_cast<int>(d->vertex_spheres.size()*sizeof(float)));
      d->program->enableAttributeArray("vertex");
      d->program->setAttributeBuffer("vertex", GL_FLOAT, 0, 3);
      buffers[Scene_edit_box_item_priv::VertexSpheres].release();

      buffers[Scene_edit_box_item_priv::NormalSpheres].bind();
      buffers[Scene_edit_box_item_priv::NormalSpheres].allocate(d->normal_spheres.data(),
                                      static_cast<int>(d->normal_spheres.size()*sizeof(float)));
      d->program->enableAttributeArray("normals");
      d->program->setAttributeBuffer("normals", GL_FLOAT, 0, 3);
      buffers[Scene_edit_box_item_priv::NormalSpheres].release();

      buffers[Scene_edit_box_item_priv::S_Vertex].bind();
      buffers[Scene_edit_box_item_priv::S_Vertex].allocate(d->hl_vertex.data(),
                                 static_cast<int>(d->hl_vertex.size()*sizeof(float)));
      d->program->enableAttributeArray("center");
      d->program->setAttributeBuffer("center", GL_FLOAT, 0, 3);
      buffers[Scene_edit_box_item_priv::S_Vertex].release();
      d->program->disableAttributeArray("colors");

      viewer->glVertexAttribDivisor(d->program->attributeLocation("center"), 1);
      d->program->release();
      vaos[Scene_edit_box_item_priv::S_Spheres]->release();
      //draw
      d->hl_type = Scene_edit_box_item_priv::VERTEX;
      break;
    }
    case 1:
    {
      //compute
      d->hl_vertex.push_back(d->edges[d->last_picked_id].source->position().x()-d->center_.x);
      d->hl_vertex.push_back(d->edges[d->last_picked_id].source->position().y()-d->center_.y);
      d->hl_vertex.push_back(d->edges[d->last_picked_id].source->position().z()-d->center_.z);

      d->hl_vertex.push_back(d->edges[d->last_picked_id].target->position().x()-d->center_.x);
      d->hl_vertex.push_back(d->edges[d->last_picked_id].target->position().y()-d->center_.y);
      d->hl_vertex.push_back(d->edges[d->last_picked_id].target->position().z()-d->center_.z);

      //fill buffers
      d->program = viewer->isOpenGL_4_3() 
          ? getShaderProgram(Scene_edit_box_item::PROGRAM_SOLID_WIREFRAME, viewer)
          : getShaderProgram(Scene_edit_box_item::PROGRAM_WITHOUT_LIGHT, viewer);
      d->program->bind();

      vaos[Scene_edit_box_item_priv::S_Edges]->bind();
      buffers[Scene_edit_box_item_priv::S_Vertex].bind();
      buffers[Scene_edit_box_item_priv::S_Vertex].allocate(d->hl_vertex.data(),
                                 static_cast<GLsizei>(d->hl_vertex.size()*sizeof(float)));
      d->program->enableAttributeArray("vertex");
      d->program->setAttributeBuffer("vertex",GL_FLOAT,0,3);
      buffers[Scene_edit_box_item_priv::S_Vertex].release();
      vaos[Scene_edit_box_item_priv::S_Edges]->release();
      d->program->release();
      //draw
      d->hl_type = Scene_edit_box_item_priv::EDGE;
      break;
    }
    case 2:
    {
      //compute
      push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[0]->position(), d->center_);
      push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[3]->position(), d->center_);
      push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[2]->position(), d->center_);

      push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[0]->position(), d->center_);
      push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[2]->position(), d->center_);
      push_xyz(d->hl_vertex, d->faces[d->last_picked_id].vertices[1]->position(), d->center_);

      for( int j=0; j<6; ++j)
      {
        push_normal(d->hl_normal, d->last_picked_id);
      }
      //fill buffers
      d->program = getShaderProgram(Scene_edit_box_item::PROGRAM_WITH_LIGHT, viewer);
      attribBuffers(viewer, Scene_edit_box_item::PROGRAM_WITH_LIGHT);

      d->program->bind();
      vaos[Scene_edit_box_item_priv::S_Faces]->bind();
      buffers[Scene_edit_box_item_priv::S_Vertex].bind();
      buffers[Scene_edit_box_item_priv::S_Vertex].allocate(d->hl_vertex.data(),
                                 static_cast<int>(d->hl_vertex.size()*sizeof(float)));
      d->program->enableAttributeArray("vertex");
      d->program->setAttributeBuffer("vertex", GL_FLOAT, 0, 3);
      buffers[Scene_edit_box_item_priv::S_Normal].release();

      buffers[Scene_edit_box_item_priv::S_Normal].bind();
      buffers[Scene_edit_box_item_priv::S_Normal].allocate(d->hl_normal.data(),
                                 static_cast<int>(d->hl_normal.size()*sizeof(float)));
      d->program->enableAttributeArray("normals");
      d->program->setAttributeBuffer("normals", GL_FLOAT, 0, 3);
      buffers[Scene_edit_box_item_priv::S_Normal].release();
      vaos[Scene_edit_box_item_priv::S_Faces]->release();
      d->program->release();

      //draw
      d->hl_type = Scene_edit_box_item_priv::FACE;
      break;
    }
    default:
      d->hl_type = Scene_edit_box_item_priv::NO_TYPE;
      break;
    }
  }
  itemChanged();

  d->ready_to_hl = false;
}

void Scene_edit_box_item::clearHL()
{
  Viewer_interface* viewer = dynamic_cast<Viewer_interface*>(*CGAL::QGLViewer::QGLViewerPool().begin());
  viewer->makeCurrent();
  d->hl_normal.clear();
  d->hl_vertex.clear();

  d->program = getShaderProgram(Scene_edit_box_item::PROGRAM_SPHERES, viewer);
  d->program->bind();

  vaos[Scene_edit_box_item_priv::S_Spheres]->bind();
  buffers[Scene_edit_box_item_priv::VertexSpheres].bind();
  buffers[Scene_edit_box_item_priv::VertexSpheres].allocate(d->vertex_spheres.data(),
                                                            static_cast<int>(d->vertex_spheres.size()*sizeof(float)));
  d->program->enableAttributeArray("vertex");
  d->program->setAttributeBuffer("vertex", GL_FLOAT, 0, 3);
  buffers[Scene_edit_box_item_priv::VertexSpheres].release();

  buffers[Scene_edit_box_item_priv::NormalSpheres].bind();
  buffers[Scene_edit_box_item_priv::NormalSpheres].allocate(d->normal_spheres.data(),
                                                            static_cast<int>(d->normal_spheres.size()*sizeof(float)));
  d->program->enableAttributeArray("normals");
  d->program->setAttributeBuffer("normals", GL_FLOAT, 0, 3);
  buffers[Scene_edit_box_item_priv::NormalSpheres].release();

  buffers[Scene_edit_box_item_priv::S_Vertex].bind();
  buffers[Scene_edit_box_item_priv::S_Vertex].allocate(d->hl_vertex.data(),
                                                       static_cast<int>(d->hl_vertex.size()*sizeof(float)));
  d->program->enableAttributeArray("center");
  d->program->setAttributeBuffer("center", GL_FLOAT, 0, 3);
  buffers[Scene_edit_box_item_priv::S_Vertex].release();
  d->program->disableAttributeArray("colors");

  viewer->glVertexAttribDivisor(d->program->attributeLocation("center"), 1);
  d->program->release();
  vaos[Scene_edit_box_item_priv::S_Spheres]->release();
  //draw
  d->hl_type = Scene_edit_box_item_priv::VERTEX;
  d->program = getShaderProgram(Scene_edit_box_item::PROGRAM_WITHOUT_LIGHT, viewer);
  d->program->bind();

  vaos[Scene_edit_box_item_priv::S_Edges]->bind();
  buffers[Scene_edit_box_item_priv::S_Vertex].bind();
  buffers[Scene_edit_box_item_priv::S_Vertex].allocate(d->hl_vertex.data(),
                                                       static_cast<GLsizei>(d->hl_vertex.size()*sizeof(float)));
  d->program->enableAttributeArray("vertex");
  d->program->setAttributeBuffer("vertex",GL_FLOAT,0,3);
  buffers[Scene_edit_box_item_priv::S_Vertex].release();
  vaos[Scene_edit_box_item_priv::S_Edges]->release();
  d->program->release();

  d->program = getShaderProgram(Scene_edit_box_item::PROGRAM_WITH_LIGHT, viewer);
  attribBuffers(viewer, Scene_edit_box_item::PROGRAM_WITH_LIGHT);

  d->program->bind();
  vaos[Scene_edit_box_item_priv::S_Faces]->bind();
  buffers[Scene_edit_box_item_priv::S_Vertex].bind();
  buffers[Scene_edit_box_item_priv::S_Vertex].allocate(d->hl_vertex.data(),
                                                       static_cast<int>(d->hl_vertex.size()*sizeof(float)));
  d->program->enableAttributeArray("vertex");
  d->program->setAttributeBuffer("vertex", GL_FLOAT, 0, 3);
  buffers[Scene_edit_box_item_priv::S_Normal].release();

  buffers[Scene_edit_box_item_priv::S_Normal].bind();
  buffers[Scene_edit_box_item_priv::S_Normal].allocate(d->hl_normal.data(),
                                                       static_cast<int>(d->hl_normal.size()*sizeof(float)));
  d->program->enableAttributeArray("normals");
  d->program->setAttributeBuffer("normals", GL_FLOAT, 0, 3);
  buffers[Scene_edit_box_item_priv::S_Normal].release();
  vaos[Scene_edit_box_item_priv::S_Faces]->release();
  d->program->release();
  d->hl_type = Scene_edit_box_item_priv::NO_TYPE;

  itemChanged();

}
void Scene_edit_box_item_priv::reset_selection()
{
  selection_on = false;
  CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin();
  viewer->setManipulatedFrame(frame);
  viewer->setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, CGAL::qglviewer::SELECT);
  constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE);
  selected_vertices.clear();
}

//intercept events for picking
bool Scene_edit_box_item::eventFilter(QObject *obj, QEvent *event)
{
  if(!visible())
    return false;
  CGAL::Three::Viewer_interface* viewer = qobject_cast<CGAL::Three::Viewer_interface*>(obj);
  if(!viewer)
    return false;
  if(event->type() == QEvent::MouseButtonPress)
  {
    QMouseEvent* e = static_cast<QMouseEvent*>(event);
    if(e->modifiers() == Qt::NoModifier)
    {
      Viewer_interface* v_i = dynamic_cast<Viewer_interface*>(viewer);
      //pick
      int type, picked;
      d->picked_pixel = e->pos();
      d->picking(type, picked, v_i);
      if(type !=-1)
      {
        bool found = false;
        QApplication::setOverrideCursor(Qt::DragMoveCursor);
        CGAL::qglviewer::Vec pos = viewer->camera()->pointUnderPixel(d->picked_pixel, found);
        if(found)
        {
          d->rf_last_pos = pos;
          d->remodel_frame->setPosition(pos);
        }
        for(int i=0; i<8; ++i)
          for(int j=0; j<3; ++j)
            d->last_pool[i][j] = d->vertices[i][j];
        d->selection_on = true;
        if(type == 0)
        {
          d->selected_vertices.push_back(&d->vertices[picked]);
          d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE);
          d->remodel_frame->setConstraint(&d->constraint);
        }
        else if(type == 1)
        {
          d->selected_vertices.push_back(d->edges[picked].source);
          d->selected_vertices.push_back(d->edges[picked].target);
          Kernel::Point_3 s(d->edges[picked].source->position()), t(d->edges[picked].target->position());

          CGAL::qglviewer::Vec normal(t.x()-s.x(), t.y()-s.y(), t.z()-s.z());
          d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::PLANE);
          d->constraint.setTranslationConstraintDirection(normal);
          d->remodel_frame->setConstraint(&d->constraint);
        }
        else if(type == 2)
        {
          for(int i=0; i<4; ++i)
            d->selected_vertices.push_back(d->faces[picked].vertices[i]);
          Kernel::Point_3 a1(d->faces[picked].vertices[1]->position()), a0(d->faces[picked].vertices[0]->position())
              ,a3(d->faces[picked].vertices[3]->position());
          QVector3D a(a1.x()-a0.x(), a1.y()-a0.y(),a1.z()-a0.z()),b(a3.x()-a0.x(), a3.y()-a0.y(),a3.z()-a0.z());
          QVector3D n = QVector3D::crossProduct(a,b);

          d->remodel_frame->setConstraint(&d->constraint);
          d->constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::AXIS);
          d->constraint.setTranslationConstraintDirection(CGAL::qglviewer::Vec(n.x(), n.y(), n.z()));
        }

        viewer->setManipulatedFrame(d->remodel_frame);
        viewer->setMouseBinding(
                    Qt::NoModifier,
                    Qt::LeftButton,
                    CGAL::qglviewer::FRAME,
                    CGAL::qglviewer::TRANSLATE);
      }
      else
      {
        d->reset_selection();
      }
    }
    return false;
  }
  else if(event->type() == QEvent::MouseMove)
  {
    QMouseEvent* e = static_cast<QMouseEvent*>(event);
    if(e->modifiers() == Qt::NoModifier)
    {
      if(d->selection_on)
      {
        d->remodel_frame->setOrientation(d->frame->orientation());
        CGAL::qglviewer::Vec td(d->remodel_frame->transformOf(d->remodel_frame->position() - d->rf_last_pos));
        QVector3D dir(td.x, td.y, td.z);
        d->remodel_box(dir);
      }
      d->ready_to_hl= true;
      d->picked_pixel = e->pos();
      QTimer::singleShot(0, this,
                         [this, viewer](){
        highlight(viewer);
      });
    }
    else if(e->modifiers() == Qt::ControlModifier &&
            e->buttons() == Qt::LeftButton)
    {
      QApplication::setOverrideCursor(d->rotate_cursor);
    }
    else if(d->selection_on)
    {
      d->reset_selection();
    }
    d->picked_pixel = e->pos();
    return false;
  }
  else if(event->type() == QEvent::MouseButtonRelease)
  {
    d->reset_selection();
    QApplication::setOverrideCursor(QCursor());
    viewer->setMouseBinding(
                Qt::NoModifier,
                Qt::LeftButton,
                CGAL::qglviewer::CAMERA,
                CGAL::qglviewer::ROTATE);
  }
  else if(event->type() == QEvent::KeyRelease)
  {
     QKeyEvent* e = static_cast<QKeyEvent*>(event);
     if(e->key() == Qt::Key_Control)
     {
       QApplication::setOverrideCursor(QCursor());
     }
  }
  return false;
}

void Scene_edit_box_item_priv::draw_picking(Viewer_interface* viewer)
{

  QMatrix4x4 f_matrix;
  for (int i=0; i<16; ++i){
    f_matrix.data()[i] = (float)frame->matrix()[i];
  }
  GLdouble d_mat[16];
  QMatrix4x4 mvp_mat;
  viewer->camera()->getModelViewProjectionMatrix(d_mat);
  for (int i=0; i<16; ++i)
    mvp_mat.data()[i] = GLfloat(d_mat[i]);
  mvp_mat = mvp_mat*f_matrix;
  QMatrix4x4 mv_mat;
  viewer->camera()->getModelViewMatrix(d_mat);
  for (int i=0; i<16; ++i)
    mv_mat.data()[i] = GLfloat(d_mat[i]);
  mv_mat = mv_mat*f_matrix;
  QVector4D light_pos(0.0f,0.0f,1.0f, 1.0f );
  light_pos = light_pos*f_matrix;

  if(item->renderingMode() == FlatPlusEdges)
  {
    item->vaos[P_Faces]->bind();
    program = item->getShaderProgram(Scene_item::PROGRAM_WITHOUT_LIGHT, viewer);
    item->attribBuffers(viewer, Scene_item::PROGRAM_WITHOUT_LIGHT);
    program->bind();
    program->setUniformValue("mvp_matrix", mvp_mat);
    program->setUniformValue("is_clipbox_on", false);
    viewer->glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(vertex_faces.size()/3));
    item->vaos[P_Faces]->release();
    program->release();
  }
  item->vaos[P_Spheres]->bind();
  pick_sphere_program.bind();
  pick_sphere_program.setUniformValue("mvp_matrix", mvp_mat);
  pick_sphere_program.setUniformValue("is_clipbox_on", GLboolean(false));
  viewer->glDrawArraysInstanced(GL_TRIANGLES, 0,
                                static_cast<GLsizei>(vertex_spheres.size()/3),
                                static_cast<GLsizei>(8));
  pick_sphere_program.release();
  item->vaos[P_Spheres]->release();

  item->vaos[P_Edges]->bind();
  if(!viewer->isOpenGL_4_3())
  {
    item->attribBuffers(viewer, Scene_item::PROGRAM_WITHOUT_LIGHT);
    program = item->getShaderProgram(Scene_item::PROGRAM_WITHOUT_LIGHT);
    program->bind();
    program->setUniformValue("is_clipbox_on", false);
  }
  else
  {
    item->attribBuffers(viewer, Scene_item::PROGRAM_SOLID_WIREFRAME);
    program = item->getShaderProgram(Scene_item::PROGRAM_SOLID_WIREFRAME);
    program->bind();
    QVector2D vp(viewer->width(), viewer->height());
    program->setUniformValue("viewport", vp);
    program->setUniformValue("near",(GLfloat)viewer->camera()->zNear());
    program->setUniformValue("far",(GLfloat)viewer->camera()->zFar());
    program->setUniformValue("width", 6.0f);
  }
  program->setUniformValue("f_matrix", f_matrix);
  viewer->glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(vertex_edges.size()/3));
  item->vaos[P_Edges]->release();
  program->release();
}

void Scene_edit_box_item_priv::remodel_box(const QVector3D &dir)
{
  CGAL::qglviewer::AxisPlaneConstraint::Type prev_cons = constraint.translationConstraintType();
  constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE);
  Q_FOREACH(Scene_edit_box_item::vertex*  selected_vertex, selected_vertices )
  {
    int id = selected_vertex->id;
    *selected_vertex->x = applyX(id, last_pool[id][0], dir.x());
    *selected_vertex->y = applyY(id, last_pool[id][1], dir.y());
    *selected_vertex->z = applyZ(id, last_pool[id][2], dir.z());
    for( int i=0; i<3; ++i)
      relative_center_[i] =(pool[i]+pool[i+3])/2 - center_[i];
    for( int i=0; i<3; ++i)
      center_[i] =(pool[i]+pool[i+3])/2;
    frame->translate(frame->inverseTransformOf(relative_center_));
  }
  item->invalidateOpenGLBuffers();
  constraint.setTranslationConstraintType(prev_cons);
}

double Scene_edit_box_item_priv::applyX(int id, double x, double dirx)
{
  switch(id)
  {
  case 0:
  case 1:
  case 4:
  case 5:
    if(x+dirx < pool[3])
      return x+dirx;
    else
      return pool[3];
  case 2:
  case 3:
  case 6:
  case 7:
    if(x+dirx > pool[0])
      return x+dirx;
    else
      return pool[0];
  default:
    return 0;
  }
  return 0;
}

double Scene_edit_box_item_priv::applyY(int id, double y, double diry)
{
  switch(id)
  {
  case 0:
  case 1:
  case 2:
  case 3:
    if(y+diry < pool[4])
      return y+diry;
    else
      return pool[4];
  case 4:
  case 5:
  case 6:
  case 7:
    if(y+diry > pool[1])
      return y+diry;
    else
      return pool[1];
  default:
    return 0;
  }
  return 0;
}

double Scene_edit_box_item_priv::applyZ(int id, double z, double dirz)
{
  switch(id)
  {
  case 1:
  case 2:
  case 5:
  case 6:
    if(z+dirz < pool[5])
      return z+dirz;
    else
      return pool[5];
  case 0:
  case 3:
  case 4:
  case 7:
    if(z+dirz > pool[2])
      return z+dirz;
    else
      return pool[2];
  default:
    return 0;
  }
  return 0;
}

//type : 0 = vertex, 1 = edge, 2 = face
void Scene_edit_box_item_priv::picking(int& type, int& id, Viewer_interface *viewer)
{
  viewer->makeCurrent();
  type = -1;
  id = -1;
  int deviceWidth = viewer->camera()->screenWidth();
  int deviceHeight = viewer->camera()->screenHeight();
  QOpenGLFramebufferObject* fbo = new QOpenGLFramebufferObject(deviceWidth, deviceHeight,QOpenGLFramebufferObject::Depth);
  fbo->bind();
  viewer->glEnable(GL_DEPTH_TEST);
  viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  QColor bgColor(viewer->backgroundColor());
  //draws the image in the fbo
  viewer->setBackgroundColor(::Qt::white);
  draw_picking(viewer);

  int rowLength = deviceWidth * 4; // data asked in RGBA,so 4 bytes.
  const static int dataLength = rowLength * deviceHeight;
  GLubyte* buffer = new GLubyte[dataLength];
  // Qt uses upper corner for its origin while GL uses the lower corner.
  viewer->glReadPixels(picked_pixel.x(), deviceHeight-1-picked_pixel.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  //decode ID and pick (don't forget the case nothing is picked
  if(!(buffer[0]==buffer[1] && buffer[1]==buffer[2]))
  {
    int r(std::ceil((buffer[0]-10)/20)), g(std::ceil((buffer[1]-10)/20)), b(std::ceil((buffer[2]-10)/20));
    id = (std::max)(r,g);
    id = (std::max)(id,b);
    if(buffer[0] > 0)
    {
      if(id <8)
        type = 0 ;
    }
    else if(buffer[1] > 0)
    {
      if(id <12)
      {
        type = 1;
      }
    }
    else if(buffer[2] > 0)
    {
      if(id <6)
      {
        type = 2;
      }
    }
  }
  delete[] buffer;
  viewer->setBackgroundColor(bgColor);
  fbo->release();
  delete fbo;
}

void Scene_edit_box_item::drawHl(Viewer_interface* viewer)const
{
  GLfloat offset_factor;
  GLfloat offset_units;
  QMatrix4x4 f_matrix;
  for (int i=0; i<16; ++i){
    f_matrix.data()[i] = (float)d->frame->matrix()[i];
  }
  GLdouble d_mat[16];
  QMatrix4x4 mvp_mat;
  viewer->camera()->getModelViewProjectionMatrix(d_mat);
  for (int i=0; i<16; ++i)
    mvp_mat.data()[i] = GLfloat(d_mat[i]);
  mvp_mat = mvp_mat*f_matrix;
  QMatrix4x4 mv_mat;
  viewer->camera()->getModelViewMatrix(d_mat);
  for (int i=0; i<16; ++i)
    mv_mat.data()[i] = GLfloat(d_mat[i]);
  mv_mat = mv_mat*f_matrix;
  QVector4D light_pos(0.0f,0.0f,1.0f, 1.0f );
  light_pos = light_pos*f_matrix;
  QVector4D ambient(0.4f, 0.4f, 0.4f, 0.4f);
  // Diffuse
  QVector4D diffuse(1.0f, 1.0f, 1.0f, 1.0f);
  // Specular
  QVector4D specular(0.0f, 0.0f, 0.0f, 1.0f);

  if(d->hl_type == Scene_edit_box_item_priv::VERTEX)
  {
    vaos[Scene_edit_box_item_priv::S_Spheres]->bind();
    attribBuffers(viewer, PROGRAM_SPHERES);
    d->program = getShaderProgram(PROGRAM_SPHERES, viewer);
    d->program->bind();
    d->program->setUniformValue("mvp_matrix", mvp_mat);
    d->program->setUniformValue("mv_matrix", mv_mat);
    d->program->setUniformValue("light_pos", light_pos);
    d->program->setAttributeValue("colors", QColor(Qt::yellow));
    double radius =std::sqrt(
        (point(6,0) - point(0,0)) * (point(6,0) - point(0,0)) +
        (point(6,1) - point(0,1)) * (point(6,1) - point(0,1)) +
        (point(6,2) - point(0,2)) * (point(6,2) - point(0,2))) *0.02 ;
    d->program->setAttributeValue("radius", radius);
    d->program->setUniformValue("is_clipbox_on", false);
    viewer->glDrawArraysInstanced(GL_TRIANGLES, 0,
                                  static_cast<GLsizei>(d->vertex_spheres.size()/3),
                                  static_cast<GLsizei>(d->hl_vertex.size()/3));

    d->program->release();
    vaos[Scene_edit_box_item_priv::S_Spheres]->release();
  }
  else if(d->hl_type == Scene_edit_box_item_priv::EDGE)
  {
    vaos[Scene_edit_box_item_priv::S_Edges]->bind();
    if(!viewer->isOpenGL_4_3())
    {
      d->program = getShaderProgram(PROGRAM_WITHOUT_LIGHT);
      attribBuffers(viewer, PROGRAM_WITHOUT_LIGHT);
      d->program->bind();
      d->program->setUniformValue("is_clipbox_on", false);
    }
    else
    {
      attribBuffers(viewer, PROGRAM_SOLID_WIREFRAME);
      d->program = getShaderProgram(PROGRAM_SOLID_WIREFRAME);
      d->program->bind();
      QVector2D vp(viewer->width(), viewer->height());
      d->program->setUniformValue("viewport", vp);
      d->program->setUniformValue("near",(GLfloat)viewer->camera()->zNear());
      d->program->setUniformValue("far",(GLfloat)viewer->camera()->zFar());
      d->program->setUniformValue("width", 6.0f);
    }
    d->program->setUniformValue("f_matrix", f_matrix);
    d->program->setAttributeValue("colors", QColor(Qt::yellow));
    viewer->glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(d->hl_vertex.size()/3));
    vaos[Scene_edit_box_item_priv::S_Edges]->release();
    d->program->release();
  }
  else if(d->hl_type == Scene_edit_box_item_priv::FACE)
  {
    viewer->glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &offset_factor);
    viewer->glGetFloatv(GL_POLYGON_OFFSET_UNITS, &offset_units);
    viewer->glEnable(GL_BLEND);
    viewer->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    vaos[Scene_edit_box_item_priv::S_Faces]->bind();
    d->program = &d->transparent_face_program;
    d->program->bind();
    d->program->setUniformValue("mvp_matrix", mvp_mat);
    d->program->setUniformValue("mv_matrix", mv_mat);
    d->program->setUniformValue("light_pos", light_pos);
    d->program->setUniformValue("light_diff",diffuse);
    d->program->setUniformValue("light_spec", specular);
    d->program->setUniformValue("light_amb", ambient);
    d->program->setUniformValue("spec_power", 51.8f);
    d->program->setUniformValue("is_clipbox_on", false);
    d->program->setAttributeValue("colors", QColor(128,128,0,128));
    viewer->glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(d->hl_vertex.size()/3));
    vaos[Scene_edit_box_item_priv::S_Faces]->release();
    d->program->release();
    viewer->glPolygonOffset(offset_factor, offset_units);
    viewer->glDisable(GL_BLEND);

  }
}
void Scene_edit_box_item::drawTransparent(CGAL::Three::Viewer_interface*viewer)const
{
  if (!are_buffers_filled)
  {
    d->computeElements();
    d->initializeBuffers(viewer);
  }
  QMatrix4x4 f_matrix;
  for (int i=0; i<16; ++i){
    f_matrix.data()[i] = (float)d->frame->matrix()[i];
  }

  GLdouble d_mat[16];
  QMatrix4x4 mvp_mat;
  viewer->camera()->getModelViewProjectionMatrix(d_mat);
  for (int i=0; i<16; ++i)
    mvp_mat.data()[i] = GLfloat(d_mat[i]);
  mvp_mat = mvp_mat*f_matrix;
  QMatrix4x4 mv_mat;
  viewer->camera()->getModelViewMatrix(d_mat);
  for (int i=0; i<16; ++i)
    mv_mat.data()[i] = GLfloat(d_mat[i]);
  mv_mat = mv_mat*f_matrix;
  QVector4D light_pos(0.0f,0.0f,1.0f, 1.0f );
  light_pos = light_pos*f_matrix;
  QVector4D ambient(0.4f, 0.4f, 0.4f, 0.4f);
  // Diffuse
  QVector4D diffuse(1.0f, 1.0f, 1.0f, 1.0f);
  // Specular
  QVector4D specular(0.0f, 0.0f, 0.0f, 1.0f);

  viewer->glEnable(GL_BLEND);
  viewer->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  vaos[Scene_edit_box_item_priv::Faces]->bind();
  d->program = &d->transparent_face_program;
  d->program->bind();
  d->program->setUniformValue("mvp_matrix", mvp_mat);
  d->program->setUniformValue("mv_matrix", mv_mat);
  d->program->setUniformValue("light_pos", light_pos);
  d->program->setUniformValue("light_diff",diffuse);
  d->program->setUniformValue("light_spec", specular);
  d->program->setUniformValue("light_amb", ambient);
  d->program->setUniformValue("spec_power", 51.8f);
  d->program->setUniformValue("is_clipbox_on", false);
  d->program->setAttributeValue("colors", QColor(128,128,128,128));
  viewer->glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(d->vertex_faces.size()/3));
  vaos[Scene_edit_box_item_priv::Faces]->release();
  d->program->release();
  viewer->glDisable(GL_BLEND);
}
