#include "ui_Transformation_widget.h"
#include "ui_MeshOnGrid_dialog.h"

#include "Scene_aff_transformed_item.h"
#include "Scene_aff_transformed_point_set_item.h"
#include "Scene_aff_transformed_polygon_soup_item.h"
#include "Scene_aff_transformed_surface_mesh_item.h"
#include "Scene_points_with_normal_item.h"
#include "Scene_polygon_soup_item.h"
#include "Scene_surface_mesh_item.h"

#include <CGAL/Three/CGAL_Lab_plugin_helper.h>
#include <CGAL/Three/Point_container.h>
#include <CGAL/Three/Scene_item_rendering_helper.h>
#include <CGAL/Three/Three.h>

#include <CGAL/boost/graph/copy_face_graph.h>
#include <CGAL/Polygon_mesh_processing/transform.h>

#include <QAction>
#include <QApplication>
#include <QDialog>
#include <QDockWidget>
#include <QElapsedTimer>
#include <QMainWindow>
#include <QMessageBox>
#include <QMenu>
#include <QString>

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

using namespace CGAL::Three;

typedef Scene_surface_mesh_item::Face_graph FaceGraph;

class GridDialog
  : public QDialog,
    public Ui::GridDialog
{
  Q_OBJECT

public:
  explicit GridDialog(QWidget* = nullptr)
  {
    setupUi(this);
  }
};

class CGAL_Lab_affine_transform_plugin
  : public QObject,
    public CGAL_Lab_plugin_helper
{
  Q_OBJECT
  Q_INTERFACES(CGAL::Three::CGAL_Lab_plugin_interface)
  Q_PLUGIN_METADATA(IID "com.geometryfactory.CGALLab.PluginInterface/1.0")

  enum class Item_type { UNKNOWN, POINT_SET, POLYGON_SOUP, POLYGON_MESH };
  using Generic_scene_aff_transformed_item = Scene_aff_transformed_item; // just for clarity

private:
  CGAL::Three::Scene_interface* scene = nullptr;

  QDockWidget* dockWidget = nullptr;
  Ui::TransformationWidget ui;

  QAction* actionTransformItem = nullptr;
  QAction* actionGenerateItemGrid = nullptr;

  Generic_scene_aff_transformed_item* aff_transformed_item = nullptr;
  Item_type aff_transformed_item_type = Item_type::UNKNOWN;

  double scaling[3];
  double lastScaling[3];
  QMatrix4x4 lastMatrix;

public:
  QList<QAction*> actions() const
  {
    return QList<QAction*>() << actionTransformItem
                             << actionGenerateItemGrid;
  }

  bool started() const
  {
    return (aff_transformed_item != nullptr);
  }

  bool applicable(QAction* a) const
  {
    // do not allow multiple transformations at the same time
    if(started())
      return false;

    // only meshes for the grid, for now
    if(a == actionGenerateItemGrid)
      return qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));

    return qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()))
        || qobject_cast<Scene_polygon_soup_item*>(scene->item(scene->mainSelectionIndex()))
        || qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
  }

  void init(QMainWindow* _mw,
            CGAL::Three::Scene_interface* scene_interface,
            Messages_interface*)
  {
    mw = _mw;
    this->scene = scene_interface;

    actionGenerateItemGrid = new QAction(tr("Create a Grid of Surface Meshes"), mw);
    if(actionGenerateItemGrid)
      connect(actionGenerateItemGrid, SIGNAL(triggered()),this, SLOT(generateItemGrid()));

    actionTransformItem = new QAction(tr("Affine Transformation"), mw);
    if(actionTransformItem)
      connect(actionTransformItem, SIGNAL(triggered()), this, SLOT(transformItem()));

    dockWidget = new QDockWidget(tr("Affine Transformation"), mw);
    ui.setupUi(dockWidget);
    dockWidget->setWindowTitle(tr("Affine Transformation"));
    addDockWidget(dockWidget);
    dockWidget->hide();

    QList<QLineEdit*> lineEdits;
    lineEdits << ui.lineEditA << ui.lineEditX << ui.lineEditY << ui.lineEditZ;
    for(QLineEdit* widget : lineEdits)
    {
      QSizePolicy sp_retain = widget->sizePolicy();
      sp_retain.setRetainSizeWhenHidden(true);
      widget->setSizePolicy(sp_retain);
    }

    // initial state is Translation: no need for this one
    ui.lineEditA->hide();

    connect(ui.applyTransfo_Button, &QPushButton::clicked,
            this, &CGAL_Lab_affine_transform_plugin::applySingleTransformation);
    connect(ui.transfo_ComboBox, SIGNAL(currentIndexChanged(int)),
            this, SLOT(updateSingleTransfoValues(int)));
    connect(ui.resetMatrix_Button, &QPushButton::clicked,
            this, &CGAL_Lab_affine_transform_plugin::resetTransformMatrix);
    connect(ui.clearButton, &QPushButton::clicked,
            this, &CGAL_Lab_affine_transform_plugin::clear);
    connect(ui.undoButton, &QPushButton::clicked,
            this, &CGAL_Lab_affine_transform_plugin::undo);
    connect(ui.validatePushButton, &QPushButton::clicked,
            this, &CGAL_Lab_affine_transform_plugin::end);

    std::fill(std::begin(scaling), std::end(scaling), 1.);
    std::fill(std::begin(lastScaling), std::end(lastScaling), 1.);
    lastMatrix.setToIdentity();

    resetTransformedItem();
  }

  template <typename SceneTransformedItem, typename SceneItem>
  void start(SceneItem*);

  void endPointSet(const QMatrix4x4& transform_matrix);
  void endPolygonSoup(const QMatrix4x4& transform_matrix);
  void endPolygonMesh(const QMatrix4x4& transform_matrix);
  void end();

  void closure()
  {
    dockWidget->hide();
  }

private:
  void transformMatrix(double* /*double[16]*/) const;

  void normalize();

public Q_SLOTS:
  void generateItemGrid();
  void transformItem();

  void killTransformedItem()
  {
    if(aff_transformed_item)
      scene->erase(scene->item_id(aff_transformed_item));
  }

  void updateUiMatrix();

  void resetTransformMatrix();

  void updateSingleTransfoValues(int);

  void applySingleTransformation();

  void resetTransformedItem()
  {
    aff_transformed_item = nullptr;
  }

  void clear()
  {
    ui.lineEditA->clear();
    ui.lineEditX->clear();
    ui.lineEditY->clear();
    ui.lineEditZ->clear();
  }

  void undo()
  {
    if(!started())
      return;

    double matrix[16];
    for(short i = 0; i<16; ++i)
      matrix[i] = double(lastMatrix.data()[i]);

    for(short i = 0; i< 3; ++i)
      scaling[i] = lastScaling[i];

    aff_transformed_item->manipulatedFrame()->setFromMatrix(matrix);
    aff_transformed_item->itemChanged();
  }
};

void
CGAL_Lab_affine_transform_plugin::
transformMatrix(double* res) const
{
  QMatrix4x4 manipulatedMatrix, scalingMatrix;
  for(int i=0; i<16; ++i)
  {
    manipulatedMatrix.data()[i] = aff_transformed_item->manipulatedFrame()->matrix()[i];
    scalingMatrix.data()[i] = 0;
  }

  scalingMatrix.data()[0] = scaling[0];
  scalingMatrix.data()[5] = scaling[1];
  scalingMatrix.data()[10] = scaling[2];
  scalingMatrix.data()[15] = 1.;

  QMatrix4x4 tMatrix = manipulatedMatrix * scalingMatrix;
  for(int i=0; i<16; ++i)
    res[i] = double(tMatrix.data()[i]);
}

void
CGAL_Lab_affine_transform_plugin::
updateUiMatrix()
{
  if(!started())
    return;

  double tmatrix[16];
  transformMatrix(&tmatrix[0]);
  aff_transformed_item->setFMatrix(tmatrix);

  // this matrix is not mandatory but it clarifies the code to use one.
  QMatrix4x4 matrix;
  for(int i=0; i<16; ++i)
    matrix.data()[i] = tmatrix[i];

  matrix(0,3) -= aff_transformed_item->center().x;
  matrix(1,3) -= aff_transformed_item->center().y;
  matrix(2,3) -= aff_transformed_item->center().z;

  const CGAL::qglviewer::Vec& offset = Three::mainViewer()->offset();

  matrix.data()[12] -= offset.x;
  matrix.data()[13] -= offset.y;
  matrix.data()[14] -= offset.z;

  ui.matrix_00->setText(QString("%1").arg(matrix(0,0)));
  ui.matrix_01->setText(QString("%1").arg(matrix(0,1)));
  ui.matrix_02->setText(QString("%1").arg(matrix(0,2)));
  ui.matrix_03->setText(QString("%1").arg(matrix(0,3)));

  ui.matrix_10->setText(QString("%1").arg(matrix(1,0)));
  ui.matrix_11->setText(QString("%1").arg(matrix(1,1)));
  ui.matrix_12->setText(QString("%1").arg(matrix(1,2)));
  ui.matrix_13->setText(QString("%1").arg(matrix(1,3)));

  ui.matrix_20->setText(QString("%1").arg(matrix(2,0)));
  ui.matrix_21->setText(QString("%1").arg(matrix(2,1)));
  ui.matrix_22->setText(QString("%1").arg(matrix(2,2)));
  ui.matrix_23->setText(QString("%1").arg(matrix(2,3)));

  ui.matrix_30->setText(QString("%1").arg(matrix(3,0)));
  ui.matrix_31->setText(QString("%1").arg(matrix(3,1)));
  ui.matrix_32->setText(QString("%1").arg(matrix(3,2)));
  ui.matrix_33->setText(QString("%1").arg(matrix(3,3)));
}

void
CGAL_Lab_affine_transform_plugin::
resetTransformMatrix()
{
  if(!started())
    return;

  scaling[0] = scaling[1] = scaling[2] = 1.;

  double matrix[16] = {0};
  matrix[0] = 1.;
  matrix[5] = 1.;
  matrix[10] = 1.;
  matrix[15] = 1.;
  matrix[12] = aff_transformed_item->center().x;
  matrix[13] = aff_transformed_item->center().y;
  matrix[14] = aff_transformed_item->center().z;

  const CGAL::qglviewer::Vec& offset = Three::mainViewer()->offset();
  aff_transformed_item->manipulatedFrame()->setFromMatrix(matrix);
  aff_transformed_item->manipulatedFrame()->translate(offset);
  aff_transformed_item->itemChanged();
}

void
CGAL_Lab_affine_transform_plugin::
updateSingleTransfoValues(int index)
{
  ui.lineEditZ->setToolTip("Value along the z-axis.");
  switch(index)
  {
    case 0:
      ui.lineEditA->show();
      ui.lineEditX->show();
      ui.lineEditY->show();
      ui.lineEditZ->show();
      ui.transfo_ComboBox->setToolTip("Angle, axis coordinates");
      break;
    case 1:
      ui.lineEditA->hide();
      ui.lineEditX->show();
      ui.lineEditY->show();
      ui.lineEditZ->show();
      ui.transfo_ComboBox->setToolTip("Axis coordinates");
      break;
    case 2:
      ui.lineEditA->hide();
      ui.lineEditX->show();
      ui.lineEditY->show();
      ui.lineEditZ->show();
      ui.transfo_ComboBox->setToolTip("Scaling along each axis");
      break;
    default:
      ui.lineEditA->hide();
      ui.lineEditX->hide();
      ui.lineEditY->hide();
      ui.lineEditZ->hide();
      ui.transfo_ComboBox->setToolTip("Scales coordinates between [0...1] ");
      break;
  }
}

void
CGAL_Lab_affine_transform_plugin::
applySingleTransformation()
{
  if(!started())
    return;

  // save the matrix before the change
  for(short i=0; i<3; ++i)
    lastScaling[i] = scaling[i];

  double currentMatrix[16];
  transformMatrix(&currentMatrix[0]);
  for(short i = 0; i < 16; ++i)
    lastMatrix.data()[i] = float(currentMatrix[i]);

  switch(ui.transfo_ComboBox->currentIndex())
  {
    // rotation
    case 0:
    {
      CGAL::qglviewer::Vec axis(ui.lineEditX->text().toDouble(),
                                ui.lineEditY->text().toDouble(),
                                ui.lineEditZ->text().toDouble());

      aff_transformed_item->manipulatedFrame()->rotate(CGAL::qglviewer::Quaternion(axis, ui.lineEditA->text().toDouble()*CGAL_PI/180.0));

      break;
    }
    // translation
    case 1:
    {
      aff_transformed_item->manipulatedFrame()->translate(CGAL::qglviewer::Vec(ui.lineEditX->text().toDouble() ,
                                                                           ui.lineEditY->text().toDouble() ,
                                                                           ui.lineEditZ->text().toDouble()));

      break;
    }
    // scaling
    case 2:
    {
      scaling[0] = ui.lineEditX->text().isEmpty() ? 1. : scaling[0]*ui.lineEditX->text().toDouble();
      scaling[1] = ui.lineEditY->text().isEmpty() ? 1. : scaling[1]*ui.lineEditY->text().toDouble();
      scaling[2] = ui.lineEditZ->text().isEmpty() ? 1. : scaling[2]*ui.lineEditZ->text().toDouble();
      break;
    }
    // normalizing
    case 3:
    {
      resetTransformMatrix();
      normalize();
      break;
    }
    default:
    {
      break;
    }
  }

  updateUiMatrix();

  aff_transformed_item->compute_bbox();
  aff_transformed_item->itemChanged();
}

void
CGAL_Lab_affine_transform_plugin::
normalize()
{
  // Get the scale factor for the item's coordinates to be in [0..1]
  double max = (std::max)({ aff_transformed_item->bbox().x_span(),
                            aff_transformed_item->bbox().y_span(),
                            aff_transformed_item->bbox().z_span() });
  QVector3D v_bil = QVector3D(aff_transformed_item->bbox().xmin(),
                              aff_transformed_item->bbox().ymin(),
                              aff_transformed_item->bbox().zmin());
  QMatrix4x4 frameMat = QMatrix4x4();
  QVector3D center(aff_transformed_item->center().x,
                   aff_transformed_item->center().y,
                   aff_transformed_item->center().z);
  frameMat.translate(-(v_bil - center) / max);
  double d_mat[16];
  for(int i=0; i<16; ++i)
    d_mat[i] = double(frameMat.data()[i]);

  scaling[0] = scaling[1] = scaling[2] = 1. / max;
  aff_transformed_item->manipulatedFrame()->setFromMatrix(d_mat);
}

void
CGAL_Lab_affine_transform_plugin::
generateItemGrid()
{
  Scene_surface_mesh_item* item = qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
  if(!item)
    return;

  const FaceGraph& sm = *item->face_graph();
  const Scene_item::Bbox& b = item->bbox();

  double x_t(1.001 * ((b.max)(0)- (b.min)(0))),
         y_t(1.001 * ((b.max)(1)- (b.min)(1))),
         z_t(1.001 * ((b.max)(2)- (b.min)(2)));

  GridDialog dialog(mw);
  dialog.x_space_doubleSpinBox->setValue(x_t);
  dialog.y_space_doubleSpinBox->setValue(y_t);
  dialog.z_space_doubleSpinBox->setValue(z_t);

  if(!dialog.exec())
    return;

  QApplication::setOverrideCursor(Qt::WaitCursor);

  int i_max = dialog.x_spinBox->value(),
      j_max = dialog.y_spinBox->value(),
      k_max = dialog.z_spinBox->value();

  x_t = dialog.x_space_doubleSpinBox->value();
  y_t = dialog.y_space_doubleSpinBox->value();
  z_t = dialog.z_space_doubleSpinBox->value();

  const bool single = dialog.singleItemCheckBox->isChecked();

  Scene_surface_mesh_item* t_item = nullptr;
  if(single)
  {
    t_item = new Scene_surface_mesh_item();
    t_item->setName(tr("%1 %2x%3x%4")
                   .arg(item->name())
                   .arg(i_max).arg(j_max).arg(k_max));
    scene->addItem(t_item);
  }

  for(int i=0; i<i_max; ++i)
  {
    for(int j=0; j<j_max; ++j)
    {
      for(int k=0; k<k_max; ++k)
      {
        Kernel::Aff_transformation_3 transformation(CGAL::TRANSLATION, Kernel::Vector_3(i*x_t, j*y_t, k*z_t));

        FaceGraph t_sm;
        CGAL::copy_face_graph(sm, t_sm);
        CGAL::Polygon_mesh_processing::transform(transformation, t_sm);

        if(!single)
        {
          t_item = new Scene_surface_mesh_item(t_sm);
          t_item->setName(tr("%1 %2,%3,%4")
                          .arg(item->name())
                          .arg(i).arg(j).arg(k));
          scene->addItem(t_item);
        }
        else
        {
          CGAL::copy_face_graph(t_sm, *t_item->face_graph());
        }
      }
    }
  }

  if(single && t_item)
    t_item->invalidateOpenGLBuffers();

  QApplication::restoreOverrideCursor();
}

template <typename SceneTransformedItem, typename SceneItem>
void
CGAL_Lab_affine_transform_plugin::
start(SceneItem* item)
{
  CGAL_precondition(item && !aff_transformed_item);

  dockWidget->show();
  dockWidget->raise();

  ui.validatePushButton->setEnabled(true);

  const typename SceneItem::Bbox& bbox = item->bbox();
  const double x = (bbox.xmin() + bbox.xmax()) / 2;
  const double y = (bbox.ymin() + bbox.ymax()) / 2;
  const double z = (bbox.zmin() + bbox.zmax()) / 2;

  lastMatrix.setToIdentity();
  lastMatrix.data()[12] = x;
  lastMatrix.data()[13] = y;
  lastMatrix.data()[14] = z;

  SceneTransformedItem* stfi = new SceneTransformedItem(item, CGAL::qglviewer::Vec(x,y,z));
  aff_transformed_item = static_cast<Generic_scene_aff_transformed_item*>(stfi);

  aff_transformed_item->setManipulatable(true);
  aff_transformed_item->setColor(Qt::green);
  aff_transformed_item->setRenderingMode(item->renderingMode());
  aff_transformed_item->setName(tr("Affine Transformation"));

  connect(aff_transformed_item->manipulatedFrame(), &CGAL::qglviewer::ManipulatedFrame::modified,
          this, &CGAL_Lab_affine_transform_plugin::updateUiMatrix);

  connect(aff_transformed_item, SIGNAL(applyTransformation()),
          this, SLOT(transformItem()));

  connect(aff_transformed_item, &SceneTransformedItem::aboutToBeDestroyed,
          dockWidget, &QDockWidget::hide);
  connect(aff_transformed_item, &SceneTransformedItem::aboutToBeDestroyed,
          this, &CGAL_Lab_affine_transform_plugin::resetTransformedItem);
  connect(aff_transformed_item, &SceneTransformedItem::aboutToBeDestroyed,
          [](){ QApplication::restoreOverrideCursor(); });

  // delete this transformed item if the item it was created for is deleted
  connect(item, &SceneItem::aboutToBeDestroyed,
          this, &CGAL_Lab_affine_transform_plugin::killTransformedItem);

  Scene_interface::Item_id transformed_item_index = scene->addItem(aff_transformed_item);
  scene->setSelectedItem(transformed_item_index);

  resetTransformMatrix();
}

void
CGAL_Lab_affine_transform_plugin::
transformItem()
{
  if(started())
    return end();

  Scene_item* item = scene->item(scene->mainSelectionIndex());
  CGAL_assertion(item);

  Scene_points_with_normal_item* pts_item = qobject_cast<Scene_points_with_normal_item*>(item);
  if(pts_item)
  {
    aff_transformed_item_type = Item_type::POINT_SET;
    return start<Scene_aff_transformed_point_set_item>(pts_item);
  }

  Scene_polygon_soup_item* ps_item = qobject_cast<Scene_polygon_soup_item*>(item);
  if(ps_item)
  {
    aff_transformed_item_type = Item_type::POLYGON_SOUP;
    return start<Scene_aff_transformed_polygon_soup_item>(ps_item);
  }

  Scene_surface_mesh_item* sm_item = qobject_cast<Scene_surface_mesh_item*>(item);
  if(sm_item)
  {
    aff_transformed_item_type = Item_type::POLYGON_MESH;
    return start<Scene_aff_transformed_surface_mesh_item>(sm_item);
  }
}

void
CGAL_Lab_affine_transform_plugin::
endPointSet(const QMatrix4x4& transform_matrix)
{
  Scene_aff_transformed_point_set_item* aff_transformed_pts_item = static_cast<Scene_aff_transformed_point_set_item*>(aff_transformed_item);
  Scene_points_with_normal_item* new_item = new Scene_points_with_normal_item(*(aff_transformed_pts_item->item()));
  Point_set* new_ps = new_item->point_set();
  CGAL::qglviewer::Vec c = aff_transformed_item->center();

  QMatrix3x3 normal_matrix = transform_matrix.normalMatrix();

  for(Point_set::Index idx : *new_ps)
  {
    QVector3D vec = transform_matrix.map(QVector3D(new_ps->point(idx).x() - c.x,
                                                   new_ps->point(idx).y() - c.y,
                                                   new_ps->point(idx).z() - c.z));
    new_ps->point(idx) = Kernel::Point_3(vec.x(), vec.y(), vec.z());
    if (new_ps->has_normal_map()) {
      QVector3D n(new_ps->normal(idx).x(), new_ps->normal(idx).y(), new_ps->normal(idx).z());
      new_ps->normal(idx) = Kernel::Vector_3(normal_matrix(0, 0) * n[0] + normal_matrix(0, 1) * n[1] + normal_matrix(0, 2) * n[2],
                                             normal_matrix(1, 0) * n[0] + normal_matrix(1, 1) * n[1] + normal_matrix(1, 2) * n[2],
                                             normal_matrix(2, 0) * n[0] + normal_matrix(2, 1) * n[1] + normal_matrix(2, 2) * n[2]);
    }
  }

  new_item->setName(aff_transformed_item->name());
  Q_EMIT new_item->itemChanged();
  scene->replaceItem(scene->item_id(aff_transformed_item), new_item, true /*emit about to be destroyed*/);

  delete aff_transformed_item;
  aff_transformed_item = nullptr;
}

void
CGAL_Lab_affine_transform_plugin::
endPolygonSoup(const QMatrix4x4& transform_matrix)
{
  Scene_aff_transformed_polygon_soup_item* aff_transformed_ps_item = static_cast<Scene_aff_transformed_polygon_soup_item*>(aff_transformed_item);
  Scene_polygon_soup_item* new_item = new Scene_polygon_soup_item();
  const auto& old_points = aff_transformed_ps_item->item()->points();

  auto new_points = old_points;
  CGAL::qglviewer::Vec c = aff_transformed_item->center();

  for(Point_3& p : new_points)
  {
    QVector3D vec = transform_matrix.map(QVector3D(p.x() - c.x,
                                                   p.y() - c.y,
                                                   p.z() - c.z));
    p = Kernel::Point_3(vec.x(), vec.y(), vec.z());
  }

  new_item->load(new_points, aff_transformed_ps_item->item()->polygons());
  new_item->setName(aff_transformed_item->name());
  new_item->invalidateOpenGLBuffers();
  Q_EMIT new_item->itemChanged();
  scene->replaceItem(scene->item_id(aff_transformed_item), new_item, true /*emit about to be destroyed*/);

  delete aff_transformed_item;
  aff_transformed_item = nullptr;
}

void
CGAL_Lab_affine_transform_plugin::
endPolygonMesh(const QMatrix4x4& transform_matrix)
{
  Scene_aff_transformed_surface_mesh_item* aff_transformed_sm_item = static_cast<Scene_aff_transformed_surface_mesh_item*>(aff_transformed_item);
  Scene_surface_mesh_item* new_item = new Scene_surface_mesh_item(*(aff_transformed_sm_item->item()));
  new_item->setName(aff_transformed_item->name());

  const CGAL::qglviewer::Vec& c = aff_transformed_item->center();
  auto transformation = [&c, &transform_matrix](const Kernel::Point_3& p) -> Kernel::Point_3
  {
    QVector3D vec = transform_matrix.map(QVector3D(p.x() - c.x,
                                                   p.y() - c.y,
                                                   p.z() - c.z));
    return { vec.x(), vec.y(), vec.z() };
  };

  FaceGraph* new_sm = new_item->face_graph();
  CGAL::Polygon_mesh_processing::transform(transformation, *new_sm);
  new_item->invalidateOpenGLBuffers();
  Q_EMIT new_item->itemChanged();
  scene->replaceItem(scene->item_id(aff_transformed_item), new_item, true  /*emit about to be destroyed*/);

  delete aff_transformed_item;
  aff_transformed_item = nullptr;
}

void
CGAL_Lab_affine_transform_plugin::
end()
{
  ui.validatePushButton->setEnabled(false);

  QApplication::setOverrideCursor(Qt::WaitCursor);

  double matrix[16];
  transformMatrix(&matrix[0]);

  const CGAL::qglviewer::Vec& offset = Three::mainViewer()->offset();
  matrix[12] -= offset.x;
  matrix[13] -= offset.y;
  matrix[14] -= offset.z;

  resetTransformMatrix();

  QMatrix4x4 transform_matrix;
  for(int i=0; i<16; ++i)
    transform_matrix.data()[i] = float(matrix[i]);

  switch(aff_transformed_item_type)
  {
    case Item_type::POINT_SET:
      endPointSet(transform_matrix);
    break;
    case Item_type::POLYGON_SOUP:
      endPolygonSoup(transform_matrix);
    break;
    case Item_type::POLYGON_MESH:
      endPolygonMesh(transform_matrix);
    break;
    default:
      CGAL_assertion(false);
  }

  dockWidget->hide();
  QApplication::restoreOverrideCursor();
}

#include "Affine_transform_plugin.moc"
