#ifdef __APPLE__
#   include <OpenGL/gl.h>
#   include <OpenGL/glu.h>
#   include <GLUT/glut.h>
#else
#   ifdef _WIN32
#       define NOMINMAX // disables min/max marco of WinDef.h
#       include <windows.h>
#       include <GL/glew.h>
#       include <GL/glut.h>
#   endif
#   include <GL/gl.h>
#   include <GL/glu.h>
#endif

#include <Eigen/Geometry>

#include "PickingPlugin.h"
#include "PluginManager.h"
#include "FileDialog.h"
#include "ViewerTrackball.h"

#include "DeformLocallyInjective.h"

#include "igl/xml/ReAntTweakBarXMLSerialization.h"
#include "igl/ReAntTweakbar.h"
#define IGL_HEADER_ONLY
#include "igl/rgb_to_hsv.h"
#include "igl/hsv_to_rgb.h"

#include <queue>
#include <algorithm>
#include <set>
#ifdef _WIN32
  #include <unordered_set>
#else
  #include <tr1/unordered_set>
#endif

#define MAXNUMREGIONS 100

void TW_CALL SaveSelectionCB(void *clientData)
{
  PickingPlugin* pnd = (PickingPlugin*)clientData;
  pnd->saveSelection();
}

void TW_CALL LoadSelectionCB(void *clientData)
{
  PickingPlugin* pnd = (PickingPlugin*)clientData;
  pnd->loadSelection();
}

void TW_CALL ResetCB(void *clientData)
{
  PickingPlugin* pnd = (PickingPlugin*)clientData;
  pnd->reset_mesh();
}

//bfs for connected components
void bfs(const std::vector<int> &vertices, const int start_index, const std::vector< std::vector<IndexType > > &edges, int r, std::vector<int> &V )
{
  int start_vertex = vertices[start_index];
  std::queue<int> Q;
  Q.push(start_vertex);
  V[start_index] = r;
  while(!Q.empty())
  {
    int i = Q.front();
    // get the tail element from queue
    Q.pop();
    for(std::vector<unsigned int>::const_iterator it = edges[i].begin(); it != edges[i].end(); ++it)
    {
      unsigned long ii = std::find(vertices.begin(), vertices.end(), *it) - vertices.begin();
      if(ii<vertices.size() && !V[ii])
      {
        V[ii] = r;
        Q.push(vertices[ii]);
      }
    }
  }
}

//predicate for determining if a picked vertex has already been assigned a component
bool is0 (int i)
{
  return !(i);
}

//connected components on the selected vertices
int connected_components (const std::vector<int> &vertices, // indices of the selected vertices
                          const std::vector<std::vector<IndexType > > &edges, // neighbors of *all* the vertices (i.e. also contains neighborhood info for vertices outside the selected set, and edges[i] != edges[vertices[i]])
                          std::vector<int> &component //connected component assigned to each vertex (1-based, 0 indicates no assigned component)
                          )
{
  long index = 0;
  unsigned long start_index = 0;
  while (start_index<vertices.size())
  {
    bfs(vertices,start_index, edges, ++index, component);
    std::vector<int >::iterator it = find_if (component.begin(), component.end(), is0);
    start_index = it - component.begin();
  }
  return index;
  
}


//determine whether (x,y) is inside/outside of a polyline
//1=inside, 0=outside
// from http://www.visibone.com/inpoly/
int inpoly ( const std::vector<std::vector<unsigned int > >&poly, //polygon points, [0]=x, [1]=y. Polyline need not be closed (i.e. first point != last point), the line segment between last and first selected points is constructed within this function.
            const unsigned int xt, //x (horizontal) of target point
            const unsigned int yt) //y (vertical) of target point
{
  int npoints= poly.size();
  unsigned int xnew,ynew;
  unsigned int xold,yold;
  unsigned int x1,y1;
  unsigned int x2,y2;
  int i;
  int inside=0;
  
  if (npoints < 3) {
    return(0);
  }
  xold=poly[npoints-1][0];
  yold=poly[npoints-1][1];
  for (i=0 ; i < npoints ; i++) {
    xnew=poly[i][0];
    ynew=poly[i][1];
    if (xnew > xold) {
      x1=xold;
      x2=xnew;
      y1=yold;
      y2=ynew;
    }
    else {
      x1=xnew;
      x2=xold;
      y1=ynew;
      y2=yold;
    }
    if ((xnew < xt) == (xt <= xold)          /* edge "open" at one end */
        && ((long)yt-(long)y1)*(long)(x2-x1)
        < ((long)y2-(long)y1)*(long)(xt-x1)) {
      inside=!inside;
    }
    xold=xnew;
    yold=ynew;
  }
  return(inside);
}

void adjacency_list(
                    const FaceMatrixType& F,
                    std::vector<std::vector<IndexType> >& A)
{
  A.clear();
  A.resize(F.maxCoeff()+1);
  
  // Loop over faces
  for(int i = 0;i<F.rows();i++)
  {
    // Loop over this face
    for(int j = 0;j<F.cols();j++)
    {
      // Get indices of edge: s --> d
      int s = F(i,j);
      int d = F(i,(j+1)%F.cols());
      A.at(s).push_back(d);
      A.at(d).push_back(s);
    }
  }
  
  // Remove duplicates
  for(int i=0; i<(int)A.size();++i)
  {
    std::sort(A[i].begin(), A[i].end());
    A[i].erase(std::unique(A[i].begin(), A[i].end()), A[i].end());
  }
}

// Declare the single entity of the plugin
// This should be in all the plugin headers
PickingPlugin  PickingPluginInstance;
PickingPlugin&  PickingPluginInstance_(){ return PickingPluginInstance; }

PickingPlugin& PickingPlugin::GetReference()
{
  return PickingPluginInstance;
}

PickingPlugin::PickingPlugin()
{
  //check in with the manager
  PluginManager().register_plugin(this);
  
  bar = NULL;
  
  selFilename = std::string("");
  selection_id.clear();
  mousePoints.clear();
  translation[0] = 0.;
  translation[1] = 0.;
  translation[2] = 0.;
  rotation[0] = 0.;
  rotation[1] = 0.;
  rotation[2] = 0.;
  rotation[3] = 1.;
  axis_is_selected[0] = false;
  axis_is_selected[1] = false;
  axis_is_selected[2] = false;
  
  m_USE_DISPLAYED_AXES = false;
  m_UPDATE_WHILE_MOVING = true;
  
  selecting = false;
  vertexSelection = true;
  depth_at_mouse_pos = 0.0;
  original_vertices = NULL;
  previous_vertices = NULL;
  
  axes_length = 0;
  axes_radius = 0;

  // compute arbitary colors based on hue space not conflicting with material color
  region_colors.resize(MAXNUMREGIONS,3);
  random_region_colors.resize(MAXNUMREGIONS,3);
}

PickingPlugin::~PickingPlugin()
{
  delete previous_vertices;
  delete original_vertices;
  delete bar;

};

// initialization (runs every time a mesh is loaded or cleared)
void PickingPlugin::init(Preview3D *preview)
{
  PreviewPlugin::init(preview);
  
  if(bar == NULL)
  {
    // Create a tweak bar
    bar = new igl::ReTwBar;
    bar->TwNewBar("Pick&Drag");
    TwDefine(" Pick&Drag size='250 265' color='76 76 127' position='235 16' refresh=0.5"); // change default tweak bar size and color
    bar->TwAddButton("Load Selection", LoadSelectionCB, this,
                     " group='Save & Load' help='Loads current selection to file.'");
    bar->TwAddButton("Save Selection", SaveSelectionCB, this,
                     " group='Save & Load' help='Saves current selection to file.'");
    bar->TwAddButton("Reset", ResetCB, this,"group='Save & Load'");
    
    bar->TwAddVarRW("Vertex/Face Selection", TW_TYPE_BOOLCPP, &vertexSelection, "group='Options'");

    TwEnumVal MouseModeEV[NUM_MOUSE_MODE] =
    {
      {TRANSLATE_HANDLE,"TRANSLATE"},
      {ROTATE_HANDLE, "ROTATE"},
      {DELETE_SELECTION,"DESELECT"}
    };
    TwType MouseModeTW = igl::ReTwDefineEnum("MouseMode", MouseModeEV, NUM_MOUSE_MODE);
    bar->TwAddVarRW("Mouse Mode", MouseModeTW, &mouse_mode,"group='Options'");
    
    bar->TwAddVarRW("Use Axes", TW_TYPE_BOOLCPP, &m_USE_DISPLAYED_AXES, " group='Options'");
    bar->TwAddVarRW("Update in Motion", TW_TYPE_BOOLCPP, &m_UPDATE_WHILE_MOVING, " group='Options'");
    bar->TwAddVarRW("Random Colors", TW_TYPE_BOOLCPP, &random_colors, " group='Color'");
    bar->TwAddVarRW( "Handle Color", TW_TYPE_COLOR3F, &handle_color," group='Color' opened=true help='Select next handle color' colormode=hls");
  }
  
  /**** Assignment 5: Add any additional tweak variables here ****/
  
  selection_id.clear();
  selection_id.resize(m_preview->vertices->rows(),-1);
  while (!m_free_regions.empty())
    m_free_regions.pop();
  for(int i=MAXNUMREGIONS;i>=0;i--)
    m_free_regions.push(i);
  mousePoints.clear();
  m_currentRegion = -1;
  mouse_mode = TRANSLATE_HANDLE;
  
  m_meshIsLoaded = m_preview->vertices->rows() >0;
  translation[0] = 0.;
  translation[1] = 0.;
  translation[2] = 0.;
  rotation[0] = 0.;
  rotation[1] = 0.;
  rotation[2] = 0.;
  rotation[3] = 1.;
  
  if (m_meshIsLoaded)
  {
    adjacency_list(*(m_preview->faces),m_preview->vertex_to_vertices);
  }
  
  /**** Assignment 5: Do any other required initialization here (stuff that depends on the original mesh only) ****/
  
  delete original_vertices;
  original_vertices = new PointMatrixType(*m_preview->vertices);

  delete previous_vertices;
  previous_vertices = new PointMatrixType(*m_preview->vertices);

  // compute random selection color
  // find hue for current material color
  double hsv[3], rgb[3];
  rgb[0] = m_preview->g_MatDiffuse[0];
  rgb[1] = m_preview->g_MatDiffuse[1];
  rgb[2] = m_preview->g_MatDiffuse[2];
  igl::rgb_to_hsv(rgb,hsv);

  double golden_ratio_conjugate = 0.618033988749895;
  double curHue = 0;
  for(int i=0;i<MAXNUMREGIONS;i++)
  {
    double r,g,b;
    double hue = std::fmod(curHue, 1.0)*360;
    
    // find color not similar to yellow
    while(std::abs(hue - hsv[0]) < 15)
    {
      curHue += golden_ratio_conjugate;
      hue = std::fmod(curHue, 1.0)*360;
    }
    
    igl::hsv_to_rgb(hue,0.8,1.0,r,g,b);
    random_region_colors.row(i) << r,g,b;
    curHue += golden_ratio_conjugate;
  }

  random_colors = true;

  handle_color[0] = random_region_colors.coeff(0,0);
  handle_color[1] = random_region_colors.coeff(0,1);
  handle_color[2] = random_region_colors.coeff(0,2);

  activate = false;
}

bool PickingPlugin::Serialize(tinyxml2::XMLDocument* doc, tinyxml2::XMLElement* )
{
  return igl::save_ReAntTweakBar(bar,doc);
}

bool PickingPlugin::Deserialize(tinyxml2::XMLDocument* doc, const tinyxml2::XMLElement* )
{
  return igl::load_ReAntTweakBar(bar,doc);
}

/*void PickingPlugin::InitWithDefaultValue()
 {
 }*/

//paints a cylinder with radius r between points a and b
void PickingPlugin::paintCylinder(const Vector3 &a, const Vector3 &b, const double radius)
{
  glPushMatrix();
  
  GLUquadricObj *quadric=gluNewQuadric();          // Create A Pointer To The Quadric Object ( NEW )
  gluQuadricNormals(quadric, GLU_SMOOTH);   // Create Smooth Normals ( NEW )
  
  // This is the default direction for the cylinders to face in OpenGL
  Vector3 z = Vector3(0,0,1);
  // Get diff between two points you want cylinder along
  Vector3 p = (a - b);
  // Get CROSS product (the axis of rotation)
  Vector3 t = z.cross(p);
  
  // Get angle. LENGTH is magnitude of the vector
  double angle = 180 / M_PI * acos ((z.dot(p)) / p.norm());
  
  glTranslatef(b[0],b[1],b[2]);
  glRotated(angle,t[0],t[1],t[2]);
  
  gluQuadricOrientation(quadric,GLU_OUTSIDE);
  gluCylinder(quadric, radius, radius, p.norm(), 15, 15);
  
  gluDeleteQuadric(quadric);
  glPopMatrix();
}

//paints a cone given its bottom and top points and its bottom radius
void PickingPlugin::paintCone(const Vector3 &bottom, const Vector3 &top, const double radius)
{
  glPushMatrix();
  
  GLUquadricObj *quadric=gluNewQuadric();          // Create A Pointer To The Quadric Object ( NEW )
  gluQuadricNormals(quadric, GLU_SMOOTH);   // Create Smooth Normals ( NEW )
  
  // This is the default direction for the cylinders to face in OpenGL
  Vector3 z = Vector3(0,0,1);
  // Get diff between two points you want cylinder along
  Vector3 p = (top - bottom);
  // Get CROSS product (the axis of rotation)
  Vector3 t = z.cross(p);
  
  // Get angle. LENGTH is magnitude of the vector
  double angle = 180 / M_PI * acos ((z.dot(p)) / p.norm());
  
  glTranslatef(bottom[0],bottom[1],bottom[2]);
  glRotated(angle,t[0],t[1],t[2]);
  
  gluQuadricOrientation(quadric,GLU_OUTSIDE);
  //a cone is a cylinder with the one radius set to almost 0
  gluCylinder(quadric, radius, 1e-8, p.norm(), 15, 15);
  
  gluDeleteQuadric(quadric);
  glPopMatrix();
}


//paints an arrow between from and to with a given radius
void PickingPlugin::paintArrow(const Vector3 &from, const Vector3 &to, double radius)
{
  double length = (to - from).norm();
  Vector3 axis = (to-from).normalized();
  paintCylinder(from, to, radius);
  paintCone(to, to+0.1*length*axis, radius);
}


//paints a coordinate frame (3 axes x,y,z in the form of arrows colored red green and blue).
//selected: a 3-element boolean array. selected[i] = true signifies that the i-th axes is selected, in which case the painted arrow will be thicker
void PickingPlugin::paintCoordinateFrame(const Vector3&point, const double length, const double radius, const bool *selected)
{
  Matrix33 I = Matrix33::Identity();
  for (int i =0; i <3; ++i)
  {
    double color[3];
    color[0] = 0.;
    color[1] = 0.;
    color[2] = 0.;
    color[i] = 1.;
    glColor3dv(color);
    
    if(selected && selected[i])
      paintArrow(point, point + length*I.col(i), radius*2);
    else
      paintArrow(point, point + length*I.col(i), radius);
  }
}

//implements picking of one of the coordinate axes
int PickingPlugin::pick_axes(const Vector3 &point, //3d point corresponding to the center of the coordinate frame
                             const double length, //length of the arrows
                             const double radius, //radius of the arrows
                             const int &mouse_x, //mouse location
                             const int &mouse_y, //mouse location
                             GLint *viewport, //opengl viewport
                             const double *projection_matrix, //opengl projection matrix
                             const double *modelview_matrix, //opengl modelview matrix
                             const int w_x,  // optional: width of window around the mouse in which we will look for the selected axes
                             const int w_y  // optional: height of window around the mouse in which we will search for the selected axes
                             )
{
  long hits = 0;
  GLuint *selectBuf =new GLuint[100000];
  glSelectBuffer(100000, selectBuf);
  glRenderMode(GL_SELECT);
  glInitNames();
  
  /* Because LoadName() won't work with no names on the stack */
  glPushName(-1);
  
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  
  /*
   restrict the draw to an area around the cursor
   */
  int cx = mouse_x;
  int cy = viewport[3] - mouse_y;
  
  //the pick matrix
  gluPickMatrix(cx, cy, w_x, w_y, viewport);
  glMultMatrixd(projection_matrix);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glMultMatrixd(modelview_matrix);
  
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  Matrix33 I = Matrix33::Identity();
  //fake-draw the axes
  for (int i =0; i <3; ++i)
  {
    glLoadName(i);
    paintArrow(point, point + length*I.col(i), radius);
  }
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  hits = glRenderMode(GL_RENDER);
  
  std::vector< std::pair<double,unsigned int> > hit_face_list;
  hit_face_list.clear();
  GLuint *ptr = (GLuint *) selectBuf;
  for(long ii=0;ii<hits;ii++)
  {
    int numNames = *ptr++;
    float minZ = (float) *ptr++/0x7fffffff;
    hit_face_list.push_back( std::pair<double,unsigned int>(minZ,*(++ptr)));
    ptr+=numNames;
  }
  std::sort(hit_face_list.begin(),hit_face_list.end());
  delete [] selectBuf;
  
  int picked_axis = -1;
  if(hits)
    picked_axis = hit_face_list[0].second;
  
  return picked_axis;
}

//implements picking of a face and returns the index of the vertex in the face that was closest to the mouse point
int PickingPlugin::pick_face_and_vertex(const int &mouse_x, // mouse location
                                        const int &mouse_y,
                                        GLint *viewport, //viewport
                                        const double *projection_matrix, //opengl projection matrix
                                        const double *modelview_matrix, //opengl modelview matrix
                                        const PointMatrixType *vertices, //list of vertices
                                        const FaceMatrixType *faces, //list of faces
                                        std::vector< std::pair<double,IndexType> > &hit_face_list, // output: list of hit faces
                                        const std::vector<int > &face_indices, // optional: list of face indices within faces that should be tested for picking.
                                        const std::vector<int > &face_labels, // optional: labels of faces that should be tested for picking. Should be of the same size as face_indices.
                                        const int w_x, // optional: width of window around the mouse in which we will search for faces
                                        const int w_y, // optional: height of window around the mouse in which we will search for faces
                                        const int *center_x, // optional: center of the window around the mouse in which we will search for faces. If not provided, the mouse position will be used.
                                        const int *center_y)
{
  long hits = 0;
  GLuint *selectBuf =new GLuint[100000];
  glSelectBuffer(100000, selectBuf);
  glRenderMode(GL_SELECT);
  glInitNames();
  
  if (!(face_indices.empty()))
  {
    if (face_indices.size() != face_labels.size())
      exit(-1);
  }
  
  /* Because LoadName() won't work with no names on the stack */
  glPushName(-1);
  
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  
  /*
   restrict the draw to an area around the cursor
   */
  int cx,cy;
  if(!center_x)
    cx = mouse_x;
  else
    cx = *center_x;
  
  
  if(!center_y)
    cy = viewport[3] - mouse_y;
  else
    cy = viewport[3] - *center_y;
  
  
  //the pick matrix
  gluPickMatrix(cx, cy, w_x, w_y, viewport);
  glMultMatrixd(projection_matrix);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glMultMatrixd(modelview_matrix);
  
  int fcnt=0;
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  //fake-draw the faces
  if (face_indices.empty())
  {
    for(int fi=0;fi<faces->rows();++fi)
    {
      glLoadName(fcnt);
      glBegin(GL_POLYGON);
      for(int vit = 0; vit < faces->cols(); ++vit)
      {
        const ScalarType *vertex_data = vertices->data() + 3* (*faces)(fi,vit);
        glVertex3f(vertex_data[0], vertex_data[1], vertex_data[2]);
      }
      glEnd();
      fcnt++;
    }
  }
  else
  {
    for(unsigned long fi=0;fi<face_indices.size();++fi)
    {
      glLoadName(face_labels[fi]);
      glBegin(GL_POLYGON);
      for(int vit = 0; vit < faces->cols(); ++vit)
      {
        const ScalarType *vertex_data = vertices->data() + 3* (*faces)(face_indices[fi],vit);
        glVertex3f(vertex_data[0], vertex_data[1], vertex_data[2]);
      }
      glEnd();
      fcnt++;
    }
  }
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  hits = glRenderMode(GL_RENDER);
  
  hit_face_list.clear();
  GLuint *ptr = (GLuint *) selectBuf;
  for(long ii=0;ii<hits;ii++)
  {
    int numNames = *ptr++;
    float minZ = (float) *ptr++/0x7fffffff;
    hit_face_list.push_back( std::pair<double,IndexType>(minZ,*(++ptr)));
    ptr+=numNames;
  }
  std::sort(hit_face_list.begin(),hit_face_list.end());
  delete [] selectBuf;
  
  size_t picked_vertex = -1;
  //find closest vertex of the face (region under mouse click will be determined from that vertex)
  if(hits)
  {
    long fi = hit_face_list[0].second;
    double mindist = 1e50;
    for (int vi = 0; vi<faces->cols(); ++vi)
    {
      double x, y, z;
      gluProject((*vertices)((*faces)(fi,vi),0),
                 (*vertices)((*faces)(fi,vi),1),
                 (*vertices)((*faces)(fi,vi),2),
                 modelview_matrix,
                 projection_matrix,
                 viewport, &x, &y, &z);
      double dist = (x-mouse_x)*(x-mouse_x) + (y-viewport[3]+mouse_y)*(y-viewport[3]+mouse_y);
      if (mindist>dist)
      {
        mindist = dist;
        picked_vertex = (*faces)(fi,vi);
      }
    }
    
  }
  return picked_vertex;
}


//saves the current selection to a file
bool PickingPlugin::saveSelection()
{
  char fname[FILE_DIALOG_MAX_BUFFER];
  fname[0] = 0;
  get_save_file_path(fname);
  
  if(fname[0] == 0)
    return false;
  
  selFilename = std::string(fname);
  FILE *f = fopen(fname,"w");
  if(NULL==f)
  {
    printf("IOError: %s could not be opened for writing...",fname);
    return false;
  }
#if 0
  for(long i = 0;i<m_preview->vertices->rows();i++)
    fprintf(f,"%d\n", selection_id[i]);
#else
  fprintf(f,"%lu\n",m_constrained_vertices.size());
  for(unsigned long i = 0;i<m_constrained_vertices.size();i++)
    fprintf(f,"%d %d %.10f %.10f %.10f\n",
            m_constrained_vertices[i],
            selection_id[m_constrained_vertices[i]],
            m_constrained_vertex_positions(i,0),
            m_constrained_vertex_positions(i,1),
            m_constrained_vertex_positions(i,2));
#endif
  fclose(f);
  
  return true;
}


//loads a selection to a file
bool PickingPlugin::loadSelection()
{
  char fname[FILE_DIALOG_MAX_BUFFER];
  fname[0] = 0;
  get_open_file_path(fname);
  
  if(fname[0] == 0)
    return false;
  
  selFilename = std::string(fname);
  FILE *f = fopen(fname,"r");
  if(NULL==f)
  {
    printf("IOError: %s could not be opened for writing...",fname);
    return false;
  }

  selection_id.assign(m_preview->vertices->rows(), -1);
  long unsigned int num_constrained = 0;
  fscanf(f,"%lu",&num_constrained);
  m_constrained_vertices.resize(num_constrained);
  m_constrained_vertex_positions.resize(num_constrained,3);
  float x,y,z;
  for(unsigned long i = 0;i<m_constrained_vertices.size();i++)
  {
    int sel_id, vid;
    fscanf(f,"%d %d %f %f %f\n",
           &vid,
           &sel_id,
           &x, &y, &z);
    m_constrained_vertex_positions.row(i)<<x,y,z;
    m_constrained_vertices[i] = vid;
    selection_id[vid] = sel_id;
  }

  selection_changed();

  fclose(f);
  colorSelection();
  
  return true;
}

// keyboard callback
bool PickingPlugin::keyDownEvent(unsigned char key, int modifiers, int , int )
{
  if (m_meshIsLoaded)
  {
    switch (key)
    {
      case 24: // ctrl + x
      {
        if (modifiers & Preview3D::CTRL)//CTRL + X: delete all selections
        {
          selection_id.clear();
          selection_id.resize(m_preview->vertices->rows(),-1);
          while (!m_free_regions.empty())
            m_free_regions.pop();
          for(int i=MAXNUMREGIONS;i>=0;i--)
            m_free_regions.push(i);
          mousePoints.clear();
          colorSelection();
          return true;
        }
        break;
      }
      case 26: // ctrl + z
      {
        if (modifiers & Preview3D::CTRL)//CTRL + Z: reset the mesh to its initial coordinates
        {
          reset_mesh();
          return true;
        }
        break;
      }
      case 20: // ctrl + t
      {
        if (modifiers == (Preview3D::CTRL) )//CTRL + T: set mouse mode to translation
        {
          mouse_mode = TRANSLATE_HANDLE;
          return true;
        }
        break;
      }
      case 18: // ctrl + r
      {
        if (modifiers == (Preview3D::CTRL) )//CTRL + R: set mouse mode to rotation
        {
          mouse_mode = ROTATE_HANDLE;
          return true;
        }
        break;
      }
      case 4: // ctrl + d
      {
        if (modifiers == (Preview3D::CTRL) )//CTRL + D: set mouse mode to deletion
        {
          mouse_mode = DELETE_SELECTION;
          return true;
        }
        break;
      }
      default:
        break;
    }
  }
  return false;
}

void PickingPlugin::reset_mesh()
{
  *(m_preview->vertices) << *original_vertices;
  
  for(int i=0;i<selection_id.size();i++)
    selection_id[i] = -1;

  m_constrained_vertices.clear();
  
  for(int i=0;i<m_preview->vertices->rows();i++)
  {
      Eigen::Vector3d matColor = Eigen::Vector3d(m_preview->g_MatDiffuse[0],m_preview->g_MatDiffuse[1],m_preview->g_MatDiffuse[2]);
      m_preview->vertex_colors->row(i) = matColor;
  }
  
  m_preview->is_compiled = false;

  selection_changed();
}

//mouse callback
bool PickingPlugin::mouseDownEvent(int mouse_x, int mouse_y, int button, int modifiers)
{
  if (m_meshIsLoaded)
  {
    
    switch (button)
    {
      case 0: // left click
      {
        
        // save state before translation/rotation
        from_x = mouse_x;
        from_y = mouse_y;

        previous_m_cpoint3 << m_cpoint3;
        *previous_vertices << *m_preview->vertices;

        glReadPixels(mouse_x, m_preview->m_viewport[3] - mouse_y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth_at_mouse_pos);
        
        m_currentRegion = -1;
        
        activate = false;
        
        if (modifiers == (Preview3D::CTRL | Preview3D::SHIFT)  || modifiers == Preview3D::CTRL)
          // selection/undo selection mode : start tracking mouse path for deselecting enclosed vertices (lasso tool)
        {
          mousePoints.clear();
          //insert the first point
          mousePoints.push_back(std::make_pair(mouse_x, mouse_y));
          selecting = true;
          return true;
        }
        else if (mouse_mode == DELETE_SELECTION)
          // deletion of a selected region: check which region is under the mouse pointer
        {
          
          std::vector< std::pair<double,IndexType> > H;
          
          int best = pick_face_and_vertex(mouse_x,
                                          mouse_y,
                                          m_preview->m_viewport,
                                          m_preview->m_projection_matrix,
                                          m_preview->m_modelview_matrix,
                                          m_preview->vertices,
                                          m_preview->faces,
                                          H);

          if(best>=0)
          {
            if (selection_id[best]>=0) //if a region was found, unselect it
            {
              m_currentRegion = selection_id[best];
              //directly delete the selected region and update the display
              for (int vi = 0; vi<m_preview->vertices->rows(); ++vi)
              {
                if (selection_id[vi] == m_currentRegion)
                  selection_id[vi] = -1;
              }

              m_free_regions.push(m_currentRegion);
              colorSelection();

              if(random_colors)
              {
                int id = m_free_regions.top();
                handle_color[0] = random_region_colors.coeff(id,0);
                handle_color[1] = random_region_colors.coeff(id,1);
                handle_color[2] = random_region_colors.coeff(id,2);
              }

              get_constrained_positions(m_currentRegion,m_constrained_vertices,m_constrained_vertex_positions,true);
              selection_changed();

              return true;
            }
          }
          
          
        }
        else if (mouse_mode == TRANSLATE_HANDLE || mouse_mode == ROTATE_HANDLE)
        // translation, rotation of a selected region: check which region is under the mouse pointer
        {
          std::vector< std::pair<double,IndexType> > H;
          int best = pick_face_and_vertex(mouse_x,
                                          mouse_y,
                                          m_preview->m_viewport,
                                          m_preview->m_projection_matrix,
                                          m_preview->m_modelview_matrix,
                                          m_preview->vertices,
                                          m_preview->faces,
                                          H);
          if(best>=0 && selection_id[best]>=0)  //if a region was found, mark it for translation/rotation
          {
            m_currentRegion = selection_id[best];
            activate = true;
            return true;
          }
        }
        
        break;
      }

      case 1://right click: do nothing
      {
        break;
      }
    }
  }
  return false;
};

void PickingPlugin::computeRegionCentroid(const PointMatrixType *vertices,
                                          const std::vector<int> &labels,
                                          int r,
                                          RowVector3 &centroid)
{
  centroid = RowVector3::Zero();
  int num = 0;
  for (long vi = 0; vi<vertices->rows(); ++vi)
    if(labels[vi]==r)
    {
      centroid += vertices->row(vi);
      num++;
    }
  centroid/=num;
}

void PickingPlugin::computeRegionBoundingBox(const PointMatrixType *vertices,
                                             const std::vector<int> &labels,
                                             int r,
                                             RowVector3 &aabb,
                                             RowVector3 &AABB)
{
  aabb = RowVector3::Constant(vertices->maxCoeff());
  AABB = RowVector3::Constant(vertices->minCoeff());
  for (long vi = 0; vi<vertices->rows(); ++vi)
    if(labels[vi]==r)
    {
      aabb = aabb.cwiseMin(vertices->row(vi));
      AABB = aabb.cwiseMax(vertices->row(vi));
    }
}

//colors selection with different colors per region
void PickingPlugin::colorSelection()
{
  if(m_preview->vertex_colors->rows()!=m_preview->vertices->rows())
    m_preview->vertex_colors->resize(m_preview->vertices->rows(),3);
  m_preview->vertex_colors->col(0).setConstant(m_preview->g_MatDiffuse[0]);
  m_preview->vertex_colors->col(1).setConstant(m_preview->g_MatDiffuse[1]);
  m_preview->vertex_colors->col(2).setConstant(m_preview->g_MatDiffuse[2]);
  
  double golden_ratio_conjugate = 0.618033988749895;
  for(int i=0;i<m_preview->vertices->rows();i++)
  {
    int region_id = selection_id[i];
    if(region_id != -1)
      m_preview->vertex_colors->row(i) = region_colors.row(region_id);
  }

  m_preview->is_compiled = false;
}

bool PickingPlugin::mouseUpEvent(int mouse_x, int mouse_y, int , int modifiers)
{
  if (m_meshIsLoaded)
  {
    if(selecting) // if we are in selection mode: gather up any faces inside the bounding box of the lasso, and then check separately which of the vertices are inside the lasso polyline
    {
      //for selection and deselection: find vertices inside the polyline the user has drawn. Then mark/unmark them.
      //step 1: use the bounding box of the polyline and OpenGL picking to isolate potentially selected faces
      int minx = 1e5, maxx = -1, miny = 1e5, maxy = -1;
      for (unsigned long i = 0; i<mousePoints.size(); ++i)
      {
        minx = std::min(minx,mousePoints[i].first);
        maxx = std::max((float)maxx,(float)mousePoints[i].first);
        miny = std::min(miny,mousePoints[i].second);
        maxy = std::max(maxy,mousePoints[i].second);
      }
      int width = maxx - minx;
      int height = maxy - miny;
      int cx = minx + 0.5*width;
      int cy = miny + 0.5*height;
      
      std::vector< std::pair<double,IndexType> > H;
      pick_face_and_vertex(mouse_x,
                           mouse_y,
                           m_preview->m_viewport,
                           m_preview->m_projection_matrix,
                           m_preview->m_modelview_matrix,
                           m_preview->vertices,
                           m_preview->faces,
                           H,
                           std::vector<int>(0),
                           std::vector<int>(0),
                           maxx - minx,
                           maxy - miny,
                           &cx,
                           &cy);
      
      //step2: check all vertices of the selected faces and see if they fall inside the polyline or not
      std::vector<std::vector<IndexType > >poly;
      poly.resize(mousePoints.size());
      for(unsigned long i=0; i<mousePoints.size(); ++i)
      {
        poly[i].resize(2);
        poly[i][0] = mousePoints[i].first;
        poly[i][1] = m_preview->m_viewport[3] - mousePoints[i].second;
      }
      
      std::tr1::unordered_set<int> doneVertices;
      std::tr1::unordered_set<int> pv;
      std::vector<int > pickedVertices(0);
      
      for(unsigned long ii=0;ii<H.size();ii++)
      {
        IndexType fi = H[ii].second;
        double x, y, z;
        for(int vi = 0; vi<m_preview->faces->cols(); vi++)
        {
          int vertex = (*(m_preview->faces))(fi,vi);
          if (doneVertices.insert(vertex).second)
          {
            gluProject((*m_preview->vertices)(vertex,0),
                       (*m_preview->vertices)(vertex,1),
                       (*m_preview->vertices)(vertex,2),
                       m_preview->m_modelview_matrix,
                       m_preview->m_projection_matrix,
                       m_preview->m_viewport, &x, &y, &z);
            
            if (inpoly(poly, x, y))
            {
              if (pv.insert(vertex).second)
                pickedVertices.push_back(vertex);
              
              if(vertexSelection == false)
              {
                for(int jj = 0; jj<m_preview->vertex_to_vertices[vertex].size(); jj++)
                {
                  int vertex_ = m_preview->vertex_to_vertices[vertex][jj];
                  if (pv.insert(vertex_).second)
                    pickedVertices.push_back(vertex_);
                }
              }
              break;
            }
          }
        }
      }

      //ok now divide picked vertices into connected components. Each selected region will be a single component.
      std::vector<int> component(pickedVertices.size(), 0);
      int nComp = connected_components(pickedVertices, m_preview->vertex_to_vertices, component);

      if(modifiers == Preview3D::CTRL)
      {
        //mark/unmark the picked vertices
        std::vector<int> regionIds;
        for(int i=0;i<nComp;i++)
        {
          regionIds.push_back(m_free_regions.top());
          m_free_regions.pop();
        }

        //for marking, mark all possible regions (visible or not)
        bool changed = false;
        for(unsigned long vi = 0; vi<component.size(); vi++)
        {
          if (selection_id[pickedVertices[vi]] == -1)
          {
            int id = regionIds[component[vi]-1];
            selection_id[pickedVertices[vi]] = id;
            
            region_colors.row(id) = Eigen::Vector3d(handle_color[0],handle_color[1],handle_color[2]);
            
            changed = true;
          }
        }

        if(random_colors)
        {
          int id = m_free_regions.top();
          handle_color[0] = random_region_colors.coeff(id,0);
          handle_color[1] = random_region_colors.coeff(id,1);
          handle_color[2] = random_region_colors.coeff(id,2);
        }
        
        //constrained vertex index changes
        if(changed)
        {
          get_constrained_positions(m_currentRegion,m_constrained_vertices,m_constrained_vertex_positions,true);
          selection_changed();
        }
      }
      else
      {
        if (modifiers == Preview3D::SHIFT)
        {
          //for unmarking, sort component and only delete top one
          PointMatrixType pv(pickedVertices.size(), 3);
          for(unsigned long vi = 0; vi<pickedVertices.size(); vi++)
            pv.row(vi) = m_preview->vertices->row(pickedVertices[vi]);
          
          double min_z = 1e10;
          int closest_component = -1;
          for (int c = 0; c<nComp; ++c)
          {
            RowVector3 centroid;
            computeRegionCentroid(&pv, component, c+1, centroid);
            double x,y,z;
            gluProject(centroid(0),
                       centroid(1),
                       centroid(2),
                       m_preview->m_modelview_matrix,
                       m_preview->m_projection_matrix,
                       m_preview->m_viewport, &x, &y, &z);
            if(z <min_z)
            {
              closest_component = c;
              min_z = z;
            }
          }
          bool changed = false;
          int regionId = 0;
          for(unsigned long vi = 0; vi<component.size(); vi++)
          {
            if(component[vi] == closest_component+1)
            {
              regionId = selection_id[pickedVertices[vi]];
              selection_id[pickedVertices[vi]] = -1;
              changed = true;
            }
          }

          if(changed)
          {
            m_free_regions.push(regionId);
            selection_changed();
          }
        }
      }

      mousePoints.clear();
      colorSelection();
      selecting = false;
      return true;
    }
    
    switch (mouse_mode)
    {
      case TRANSLATE_HANDLE:
      case ROTATE_HANDLE:
      {
        if(!m_UPDATE_WHILE_MOVING && m_currentRegion>=0)
        {
          get_constrained_positions(m_currentRegion,m_constrained_vertices,m_constrained_vertex_positions,true);
          selected_vertices_moved();
        }
        break;
      }
      default:
        break;
        
    }
    
    axis_is_selected[0] = false;
    axis_is_selected[1] = false;
    axis_is_selected[2] = false;
    m_currentRegion = -1;
    translation[0] = 0.;
    translation[1] = 0.;
    translation[2] = 0.;
    rotation[0] = 0.;
    rotation[1] = 0.;
    rotation[2] = 0.;
    rotation[3] = 1.;
    m_currentRegion = -1;
    activate = false;
    
  }
  return false;
}

// calculate a rotation given the mouse location and the mouse location when the dragging began
void PickingPlugin::get_rotation(int mouse_x,
                                 int from_x,
                                 int mouse_y,
                                 int from_y,
                                 double depth,
                                 double *modelview_matrix,
                                 double *projection_matrix,
                                 int *viewport,
                                 float *rotation,
                                 bool *selected_axis //if this is set, we will only rotate along the axis i for which selected_axis[i] = true
                                 )
{
  GLfloat winX, winY;               // Holds Our X, Y Coordinates
  double x,y,z;
  
  winX = (float)mouse_x;                  // Holds The Mouse X Coordinate
  winY = (float)mouse_y;
  winY = (float)viewport[3] - winY;
  gluUnProject(winX, winY, depth, modelview_matrix,projection_matrix,viewport, &x, &y, &z);
  Vector3 p1;
  p1<<x,y,z;
  
  winX = (float)from_x;                  // Holds The Mouse X Coordinate
  winY = (float)from_y;
  winY = (float)viewport[3] - winY;
  gluUnProject(winX, winY, depth, modelview_matrix,projection_matrix,viewport, &x, &y, &z);
  Vector3 p2;
  p2<<x,y,z;
  
  //axis of rotation
  Vector3 axis = p1.cross(p2);
  //if we are only moving around a selected axis, remove any rotation around the rest of the axes
  for (int i = 0 ; i<3; ++i)
    if(selected_axis && !selected_axis[i])
      axis[i] = 0.;
  axis.normalize();
  
  float angle = M_PI*((mouse_x - from_x)*(mouse_x - from_x) + (mouse_y - from_y)*(mouse_y - from_y)) / std::max(viewport[2],viewport[3]) ;
  
  Eigen::Quaternion<ScalarType> rot(Eigen::AngleAxis<ScalarType>(angle,axis));
  
  if (!(rot.x() == rot.x()) ||
      !(rot.y() == rot.y()) ||
      !(rot.z() == rot.z()) ||
      !(rot.w() == rot.w()) )
  {
    /*std::cerr<<"p1: "<<p1.transpose()<<std::endl;
    std::cerr<<"p2: "<<p2.transpose()<<std::endl;
    std::cerr<<"axis: "<<axis.transpose()<<std::endl;
    std::cerr<<"angle: "<<angle<<std::endl;
    std::cerr<<"quaternion: "<<rot.x()<<" "<<rot.y()<<" "<<rot.z()<<" "<<rot.w()<<" "<<std::endl;*/
    rotation[0] = 0.;
    rotation[1] = 0.;
    rotation[2] = 0.;
    rotation[3] = 1.;
    return;
  }
  rotation[0] = rot.x();
  rotation[1] = rot.y();
  rotation[2] = rot.z();
  rotation[3] = rot.w();
  
  
}

// calculate a translation given the mouse location and the mouse location when the dragging began
void PickingPlugin::get_translation(int mouse_x,
                                    int from_x,
                                    int mouse_y,
                                    int from_y,
                                    double depth,
                                    double *modelview_matrix,
                                    double *projection_matrix,
                                    int *viewport,
                                    float *translation,
                                    bool *selected_axis  //if this is set, we will only rotate along the axis i for which selected_axis[i] = true
                                    )
{
  //translation
  Eigen::Vector3f from = m_preview->screen_to_world(from_x,from_y,depth);
  Eigen::Vector3f to = m_preview->screen_to_world(mouse_x,mouse_y,depth);

  translation[0] = to[0] - from[0];
  translation[1] = to[1] - from[1];
  translation[2] = to[2] - from[2];
  
  //if we are only moving along a selected axis, remove any translation along the rest of the axes
  for (int i = 0 ; i<3; ++i)
    if(selected_axis && !selected_axis[i])
      translation[i] = 0.;
}


bool PickingPlugin::mouseMoveEvent(int mouse_x, int mouse_y)
{
  if (m_meshIsLoaded)
  {
    
    if (selecting)//if in selection mode: lasso tool (add mouse location to the lasso polyline)
    {
      //keep tracking the mouse: gather up the points
      mousePoints.push_back(std::make_pair(mouse_x, mouse_y));
      return true;
    }
    switch (mouse_mode)
    {
      case TRANSLATE_HANDLE: // translation: calculate the current translation
      {
        if (!activate || m_currentRegion <0)
          break;
        
        get_translation(mouse_x,
                        from_x,
                        mouse_y,
                        from_y,
                        depth_at_mouse_pos,
                        m_preview->m_modelview_matrix,
                        m_preview->m_projection_matrix,
                        m_preview->m_viewport,
                        translation,
                        (m_USE_DISPLAYED_AXES)?axis_is_selected:0);
        
        if(m_UPDATE_WHILE_MOVING)
        {
          if (m_USE_DISPLAYED_AXES)
          {
            //move the center of the coordinate frame (required for the coordinate frame to be displayed correctly)
            m_cpoint3[0] = previous_m_cpoint3[0] + translation[0];
            m_cpoint3[1] = previous_m_cpoint3[1] + translation[1];
            m_cpoint3[2] = previous_m_cpoint3[2] + translation[2];
          }
          
          //update the constrained vertices (selected regions and handle), calculate the deformation and update the vertices
          get_constrained_positions(m_currentRegion,m_constrained_vertices,m_constrained_vertex_positions,true);
          selected_vertices_moved();
          
        }
        return true;
        break;
      }
      case ROTATE_HANDLE:
      {
        if (!activate || m_currentRegion <0)
          break;
        
        get_rotation(mouse_x,
                     from_x,
                     mouse_y,
                     from_y,
                     depth_at_mouse_pos,
                     m_preview->m_modelview_matrix,
                     m_preview->m_projection_matrix,
                     m_preview->m_viewport,
                     rotation,
                     (m_USE_DISPLAYED_AXES)?axis_is_selected:0);
        
        if (m_UPDATE_WHILE_MOVING)
        {
          //rotation by the centroid: center of the coordinate frame (if applicable) does not move
          //update the constrained vertices (selected regions and handle), calculate the deformation and update the vertices
          get_constrained_positions(m_currentRegion,m_constrained_vertices,m_constrained_vertex_positions,true);
          selected_vertices_moved();
        }
        return true;
        
        break;
      }
      case DELETE_SELECTION:
      {
        break;
      }
    }
    
    //if we are in axes selection mode, we need to check if the mouse is on a mesh region that is selected, and only then should we pick an axes
    if (m_USE_DISPLAYED_AXES && (!m_preview->down || !activate))
    {
      axis_is_selected[0] = false; axis_is_selected[1] = false; axis_is_selected[2] = false;
      
      
      std::vector< std::pair<double,IndexType> > H;
      int vertex_under_mouse = pick_face_and_vertex(mouse_x,
                                                    mouse_y,
                                                    m_preview->m_viewport,
                                                    m_preview->m_projection_matrix,
                                                    m_preview->m_modelview_matrix,
                                                    m_preview->vertices,
                                                    m_preview->faces,
                                                    H);
      //if on a selected region, pick the axis
      if(vertex_under_mouse>=0 && selection_id[vertex_under_mouse]>=0)
      {
        //
        m_currentRegion = selection_id[vertex_under_mouse];
        
        computeRegionCentroid(m_preview->vertices, selection_id, m_currentRegion, m_cpoint3);
        double x1,y1,z1;
        double x2,y2,z2;
        double xc1,yc1,zc1;
        double xc2,yc2,zc2;
        
        gluProject(m_cpoint3(0),
                   m_cpoint3(1),
                   m_cpoint3(2),
                   m_preview->m_modelview_matrix,
                   m_preview->m_projection_matrix,
                   m_preview->m_viewport, &xc1, &yc1, &zc1);
        
        RowVector3 aabb, AABB;
        computeRegionBoundingBox(m_preview->vertices, selection_id, m_currentRegion, aabb, AABB);
        //move the coordinate frame to the front of the scene
        gluProject(aabb(0),
                   aabb(1),
                   aabb(2),
                   m_preview->m_modelview_matrix,
                   m_preview->m_projection_matrix,
                   m_preview->m_viewport, &x1, &y1, &z1);
        
        gluProject(AABB(0),
                   AABB(1),
                   AABB(2),
                   m_preview->m_modelview_matrix,
                   m_preview->m_projection_matrix,
                   m_preview->m_viewport, &x2, &y2, &z2);
        
        gluUnProject(xc1, yc1, std::min(z1,z2)-0.01, m_preview->m_modelview_matrix, m_preview->m_projection_matrix, m_preview->m_viewport, &xc2, &yc2, &zc2);
        m_cpoint3<<xc2,yc2,zc2;
        
        int ax = pick_axes(m_cpoint3,
                           axes_length,
                           axes_radius,
                           mouse_x,
                           mouse_y,
                           m_preview->m_viewport,
                           m_preview->m_projection_matrix,
                           m_preview->m_modelview_matrix,
                           10,
                           10);
        
        axis_is_selected[ax] = true;
      }
      
    }
    
    
    
  }
  return false;
}


bool PickingPlugin::mouseScrollEvent(int /*mouse_x*/, int /*mouse_y*/, float /*delta*/) { return false; };

void PickingPlugin::preDraw(int currentTime)
{
};


//stuff that is drawn by the plugin after the previewer has displayed the mesh
//first draw 3d, then 2d
void PickingPlugin::postDraw(int currentTime)
{
  if(vertexSelection)
  {
    glDisable(GL_LIGHTING);

    glPushMatrix();

    float mat[4*4];
    Preview3D::ConvertQuaternionToMatrix(m_preview->g_Rotation, mat);
    glMultMatrixf(mat);
    glScaled(m_preview->g_Zoom, m_preview->g_Zoom, m_preview->g_Zoom);
    glScaled(m_preview->zoom, m_preview->zoom, m_preview->zoom);
    glTranslatef(m_preview->g_Translation[0],m_preview->g_Translation[1],m_preview->g_Translation[2]);

    double diameter = std::min(m_preview->diameter*0.004,m_preview->avg_edge_length*0.3);

    for(int i=0;i<m_preview->vertices->rows();i++)
    {
      int region_id = selection_id[i];

      if(region_id >= 0)
      {
        glPushMatrix();

        GLUquadricObj *quadric;
        quadric = gluNewQuadric();
        gluQuadricDrawStyle(quadric, GLU_FILL );

        glTranslatef(m_preview->vertices->coeff(i,0),m_preview->vertices->coeff(i,1),m_preview->vertices->coeff(i,2));
        Eigen::Vector3d color = region_colors.row(region_id);
        glColor3d(color[0],color[1],color[2]);
        gluSphere(quadric,diameter,7,3);

        gluDeleteQuadric(quadric);

        glPopMatrix();
      }
    }

    glPopMatrix();

    if(m_preview->use_lighting)
      glEnable(GL_LIGHTING);
    else
      glDisable(GL_LIGHTING);
  }
  
  if (m_meshIsLoaded)
  {
    axes_length = 0.6/m_preview->zoom;
    axes_radius = 0.006/m_preview->zoom;
    
    glMatrixMode (GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity ();
    glMultMatrixd(m_preview->m_projection_matrix);
    glMatrixMode (GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity ();
    glMultMatrixd(m_preview->m_modelview_matrix);
    
    if(!m_UPDATE_WHILE_MOVING && m_currentRegion >=0 && activate)
    {
      get_constrained_positions(m_currentRegion,m_constrained_vertices,m_constrained_vertex_positions,true);

      glEnable(GL_BLEND);
      glEnable(GL_POLYGON_OFFSET_FILL); // Avoid Stitching!
      glPolygonOffset(1.0, 1.0);
      
      if(m_preview->use_lighting)
        glEnable(GL_LIGHTING);
      else
        glDisable(GL_LIGHTING);
      
      // paint faces that have at least one picked vertex, with shading
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      glEnable(GL_COLOR_MATERIAL);
      float linewidth;
      glGetFloatv(GL_LINE_WIDTH,&linewidth);
      glLineWidth(2.);
      glLineStipple(1, 0xAAAA);
      glEnable(GL_LINE_STIPPLE);
      glColor3d(region_colors.coeff(m_currentRegion,0),region_colors.coeff(m_currentRegion,1),region_colors.coeff(m_currentRegion,2));
      
      glBegin(GL_TRIANGLES);
      for (int fi = 0; fi<m_preview->faces->rows(); ++fi)
      {
        int firstPickedVertex = -1;
        for(int vi = 0; vi<m_preview->faces->cols() ; ++vi)
          if (selection_id[(*(m_preview->faces))(fi,vi)] == m_currentRegion)
          {
            firstPickedVertex = vi;
            break;
          }
        if(firstPickedVertex==-1)
          continue;
        
        
        for(int vi = 0; vi<m_preview->faces->cols(); ++vi)
        {
          RowVector3 p0, p1;
          int vertex_id = (*(m_preview->faces))(fi,vi);
          if (selection_id[vertex_id] == m_currentRegion)
          {
            int index = std::find(m_constrained_vertices.begin(),
                                  m_constrained_vertices.end(),
                                  vertex_id) - m_constrained_vertices.begin();
            p0 = m_constrained_vertex_positions.row(index);
          }
          else
            p0= m_preview->vertices->row(vertex_id);
          
          glVertex3f(p0[0], p0[1], p0[2]);
        }
        
      }
      glEnd();
      glDisable(GL_LINE_STIPPLE);
      glLineWidth(linewidth);
      glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
      
      glDisable(GL_POLYGON_OFFSET_FILL);
    }
    
    if(m_currentRegion >=0 )
    {
      if(m_USE_DISPLAYED_AXES)
      {
        glEnable(GL_COLOR_MATERIAL);
        paintCoordinateFrame(m_cpoint3, axes_length, axes_radius, axis_is_selected);
      }
    }
    
    glPopMatrix();
    glMatrixMode (GL_PROJECTION);
    glPopMatrix();
    glMatrixMode (GL_MODELVIEW);
    
    
    //if in the process of selection, draw a dashed line with the lasso (2-d drawing)
    if (selecting)
    {
      if (mousePoints.size())
      {
        GLint viewport[4];
        glGetIntegerv(GL_VIEWPORT,viewport);
        
        glMatrixMode (GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity ();
        gluOrtho2D ((GLdouble) viewport[0], (GLdouble) viewport[2], (GLdouble) viewport[1], (GLdouble) viewport[3]);
        glMatrixMode (GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity ();
        
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_CULL_FACE);
        glDisable(GL_BLEND);
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_LIGHTING);
        glDisable (GL_LIGHT0);
        
        float linewidth;
        glGetFloatv(GL_LINE_WIDTH,&linewidth);
        glLineWidth(2.);
        glLineStipple(2, 0xAAAA);
        glEnable(GL_LINE_STIPPLE);
        glColor3d(0.5, 0.5, 0.5);
        glBegin(GL_LINE_STRIP);
        for (unsigned long i = 0; i <mousePoints.size(); ++i)
          glVertex2d(mousePoints[i].first,viewport[3] - mousePoints[i].second);
        glEnd();
        glDisable(GL_LINE_STIPPLE);
        glLineWidth(linewidth);
        
        //        glutSwapBuffers();
        
        glEnable(GL_DEPTH_TEST);
        
        glMatrixMode (GL_MODELVIEW);
        glPopMatrix();
        glMatrixMode (GL_PROJECTION);
        glPopMatrix();
        glMatrixMode (GL_MODELVIEW);
      }
    }
  }
  return;
}

void PickingPlugin::get_constrained_positions(int r,
                                              std::vector<IndexType > &constrained_vertices,
                                              Eigen::Matrix<ScalarType, Eigen::Dynamic, Eigen::Dynamic> &constrained_positions,
                                              bool overwrite_original_positions)
{
  ///vertices in constrained_vertices have to be ORDERED !!!
  int num_constrained_vertices = m_preview->vertices->rows() - std::count(selection_id.begin(), selection_id.end(), -1);
  constrained_positions = Eigen::Matrix<ScalarType, Eigen::Dynamic, Eigen::Dynamic>(num_constrained_vertices,3);
  constrained_vertices.resize(num_constrained_vertices);
  
  RowVector3 centroid;
  computeRegionCentroid(previous_vertices, selection_id, r, centroid);
  
  int count = 0;
  for (long vi = 0; vi<m_preview->vertices->rows(); ++vi)
    if(selection_id[vi] >=0)
    {
      
      constrained_vertices[count] = vi;
      RowVector3 goalPosition = previous_vertices->row(vi);
      if(overwrite_original_positions && selection_id[vi] == r)
      {
        if( mouse_mode == TRANSLATE_HANDLE)
        {
          goalPosition[0] += translation[0];
          goalPosition[1] += translation[1];
          goalPosition[2] += translation[2];
          
        }
        else if (mouse_mode == ROTATE_HANDLE)
        {
          goalPosition -= centroid;
          float pt[3] = {goalPosition[0], goalPosition[1], goalPosition[2]};
          quat_rotate_point(rotation, pt, pt);
          goalPosition[0] = pt[0] + centroid[0];
          goalPosition[1] = pt[1] + centroid[1];
          goalPosition[2] = pt[2] + centroid[2];
          
        }
      }
      constrained_positions.row(count) = goalPosition;
      count ++;
    }
  
  
}

void PickingPlugin::selection_changed()
{
  DeformLocallyInjective::GetReference().UpdatePositionalConstraints(m_constrained_vertices);
}

void PickingPlugin::selected_vertices_moved()
{
  DeformLocallyInjective::GetReference().UpdateConstraintVertexPositions(m_constrained_vertices, m_constrained_vertex_positions);
  
  /**** Assignment 5: Add your code for deformation here ****/
  
  // Simple deformation: the constrained vertices are moved to their target positions
  /*Eigen::Matrix<ScalarType, Eigen::Dynamic, Eigen::Dynamic> new_vertex_positions = *(m_preview->vertices);
  for (unsigned long i = 0; i<m_constrained_vertices.size(); ++i)
  {
    int vid = m_constrained_vertices[i];
    new_vertex_positions.row(vid) = m_constrained_vertex_positions.row(i);
  }
  
  *(m_preview->vertices) = new_vertex_positions;
  
  m_preview->is_compiled = false;*/
}
