//#define CGAL_PMP_REMESHING_VERBOSE
//#define CGAL_PMP_REMESHING_DEBUG
//#define CGAL_PMP_REMESHING_VERY_VERBOSE
//#define CGAL_PMP_REMESHING_VERBOSE_PROGRESS

#include <QtCore/qglobal.h>

#include <CGAL/Three/CGAL_Lab_plugin_interface.h>


#include "Scene_surface_mesh_item.h"

#include "Scene_polyhedron_selection_item.h"

#include <CGAL/iterator.h>
#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/Polygon_mesh_processing/Adaptive_sizing_field.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/utility.h>
#include <CGAL/property_map.h>

#include <boost/graph/graph_traits.hpp>

#include <QElapsedTimer>
#include <QAction>
#include <QMainWindow>
#include <QApplication>
#include <QString>
#include <QDialog>
#include <QtPlugin>
#include <QMessageBox>

#include <vector>
#include <algorithm>
#include <queue>
#include <sstream>
#include <cmath>
#include <unordered_set>

#ifdef CGAL_LINKED_WITH_TBB
#include "tbb/parallel_for.h"
#include "tbb/blocked_range.h"
#include "tbb/partitioner.h"
#endif

#include "ui_Isotropic_remeshing_dialog.h"


typedef Scene_surface_mesh_item Scene_facegraph_item;
typedef Scene_facegraph_item::Face_graph FaceGraph;
typedef boost::graph_traits<FaceGraph>::face_descriptor face_descriptor;


// give a halfedge and a target edge length, put in `out` points
// which the edge equally spaced such that splitting the edge
// using the sequence of points make the edges shorter than
// `target_length`
template <class TriangleMesh, class PointPMap, class PointOutputIterator>
PointOutputIterator
sample_edge(
  typename boost::graph_traits<TriangleMesh>::halfedge_descriptor hd,
  TriangleMesh& triangle_mesh,
  double target_length,
  const PointPMap& pmap,
  PointOutputIterator out)
{
  typedef typename boost::property_traits<PointPMap>::value_type Point_3;
  typedef typename CGAL::Kernel_traits<Point_3>::Kernel::Vector_3 Vector_3;
  typename boost::property_traits<PointPMap>::reference src=get(pmap, source(hd,triangle_mesh) );
  typename boost::property_traits<PointPMap>::reference tgt=get(pmap, target(hd,triangle_mesh) );

  double length = std::sqrt( CGAL::squared_distance(src, tgt) );
  if ( length <= target_length ) return out;

  double nb_points = std::floor( length / target_length );
  Vector_3 unit = (tgt-src) / (nb_points+1);

  for(double i=0; i<nb_points; ++i)
    *out++=src+unit*(i+1);

  return out;
}

// given a set of points that are expected to be on an edge, split
// that edge and retriangulate the face incident to the edge
// Points are sorted so that they are sorted from the source to the target
// of the edge (the sequence does not contains edge endpoints)
template <class TriangleMesh, class PointPMap, class PointRange, class EdgeOutputIterator>
EdgeOutputIterator
split_identical_edges(
  typename boost::graph_traits<TriangleMesh>::halfedge_descriptor hd,
  TriangleMesh& tm,
  const PointPMap& pmap,
  const PointRange& points,
  EdgeOutputIterator out)
{
  typedef typename PointRange::value_type Point_3;
  typedef boost::graph_traits<TriangleMesh> GT;
  typedef typename GT::halfedge_descriptor halfedge_descriptor;

  for(const Point_3& p : points)
  {
    // split the edge
    halfedge_descriptor new_hd=CGAL::Euler::split_edge(hd,tm);
    // set the vertex point
    put(pmap, target(new_hd, tm), p);
    *out++=edge(new_hd, tm);
  }
  *out++=edge(hd, tm);
  return out;
}

// HedgeRange is expected to be a range with value type being
// std::pair<halfedge_descriptor, TriangleMesh*>
// Given a set of halfedges representing different edges
// but with identical endpoints, and a target edge length
// we split all edges identically so that subedges are
// or length <= length
template <class HedgeRange, class Edges_to_protect>
void split_long_duplicated_edge(const HedgeRange& hedge_range,
                                double target_length,
                                Edges_to_protect& edges_to_protect)
{
  typedef typename HedgeRange::value_type Pair;
  typedef typename Pair::first_type halfedge_descriptor;
  typedef std::remove_pointer_t<
    typename Pair::second_type> TriangleMesh;
  typedef typename boost::property_map<TriangleMesh,
    CGAL::vertex_point_t>::type PointPMap;
  typedef typename boost::property_traits<PointPMap>::value_type Point_3;

  if (hedge_range.empty()) return;

  const Pair& p = *hedge_range.begin();
  PointPMap pmap = get(boost::vertex_point, *p.second);

  std::vector<Point_3> points;
  halfedge_descriptor hd = p.first;

  // collect points to be add inside the edges
  sample_edge(hd, *p.second, target_length, pmap, std::back_inserter(points) );

  CGAL_assertion_code(Point_3 src = get(pmap, source(hd, *p.second));)
  CGAL_assertion_code(Point_3 tgt = get(pmap, target(hd, *p.second));)

  // split the edges and collect faces to triangulate
  for(const Pair& h_and_p : hedge_range)
  {
    halfedge_descriptor hc=h_and_p.first;
    TriangleMesh* polyc = h_and_p.second;

    PointPMap pmap_2 = get(boost::vertex_point, *polyc);
    //make sure halfedge are consistently oriented
    CGAL_assertion( get(pmap_2, source(hc, *polyc)) == src );
    CGAL_assertion( get(pmap_2, target(hc, *polyc)) == tgt );

    typedef typename Edges_to_protect::value_type::second_type Edge_set;
    Edge_set& edge_set = edges_to_protect[polyc];

    // now split the halfedge and incident faces
    split_identical_edges(hc,*polyc,pmap_2, points,
                          std::inserter( edge_set, edge_set.begin()));
  }
}

using namespace CGAL::Three;
class CGAL_Lab_isotropic_remeshing_plugin :
  public QObject,
  public CGAL_Lab_plugin_interface
{
  Q_OBJECT
  Q_INTERFACES(CGAL::Three::CGAL_Lab_plugin_interface)
  Q_PLUGIN_METADATA(IID "com.geometryfactory.CGALLab.PluginInterface/1.0" FILE "isotropic_remeshing_plugin.json")

  typedef boost::graph_traits<FaceGraph>::edge_descriptor edge_descriptor;
  typedef boost::graph_traits<FaceGraph>::halfedge_descriptor halfedge_descriptor;
  typedef boost::graph_traits<FaceGraph>::face_descriptor face_descriptor;

  typedef std::unordered_set<edge_descriptor>    Edge_set;
  typedef Scene_polyhedron_selection_item::Is_constrained_map<Edge_set> Edge_constrained_pmap;

  struct Selection_updater_visitor
    : public CGAL::Polygon_mesh_processing::Hole_filling::Default_visitor
  {
    typedef typename Scene_polyhedron_selection_item::Selection_set_facet Container;
    Container& faces;

    Selection_updater_visitor(Container& container)
      : faces(container)
    {}

    void before_subface_creations(face_descriptor fd)
    {
      Container::iterator it = faces.find(fd);
      faces.erase(it);
    }
    void after_subface_created(face_descriptor fd)
    {
      faces.insert(fd);
    }
    void after_subface_creations(){}
  };

public:
  void init(QMainWindow* mainWindow, Scene_interface* scene_interface, Messages_interface*)
  {
    this->scene = scene_interface;
    this->mw = mainWindow;

    actionIsotropicRemeshing_ = new QAction("Isotropic Remeshing", mw);
    actionIsotropicRemeshing_->setProperty("subMenuName", "Polygon Mesh Processing");
    if (actionIsotropicRemeshing_) {
      connect(actionIsotropicRemeshing_, SIGNAL(triggered()),
        this, SLOT(isotropic_remeshing()));
    }
  }

  QList<QAction*> actions() const {
    return QList<QAction*>() << actionIsotropicRemeshing_;
  }

  bool applicable(QAction*) const
  {
    if (scene->selectionIndices().size() == 1)
    {
    return qobject_cast<Scene_facegraph_item*>(scene->item(scene->mainSelectionIndex()))
    || qobject_cast<Scene_polyhedron_selection_item*>(scene->item(scene->mainSelectionIndex()));
    }

    bool ok(true), found_poly(false);

    for(int index : scene->selectionIndices())
    {
      if (!qobject_cast<Scene_facegraph_item*>(scene->item(index)))
        ok = false;
      else
        found_poly=true;
    }
    return ok && found_poly;
  }

  typedef boost::property_map<FaceGraph, CGAL::face_patch_id_t<int> >::type Patch_id_pmap;

  void detect_and_split_duplicates(std::vector<Scene_facegraph_item*>& selection,
                                   std::map<FaceGraph*,Edge_set>& edges_to_protect,
                                   double target_length)
  {
    typedef EPICK::Point_3 Point_3;
    typedef std::pair<Point_3,Point_3> Segment_3;

    typedef std::map< Segment_3,
                      std::vector< std::pair<halfedge_descriptor, FaceGraph*> > > MapType;
    typedef boost::property_map<FaceGraph,
      CGAL::vertex_point_t>::type PointPMap;
    MapType duplicated_edges;

    for(Scene_facegraph_item* poly_item : selection){
      FaceGraph& pmesh = *poly_item->polyhedron();
      PointPMap pmap = get(boost::vertex_point, pmesh);
      for(edge_descriptor ed : edges(pmesh)){
        halfedge_descriptor hd = halfedge(ed,pmesh);
        Point_3 p = get(pmap, source(hd,pmesh)), q = get(pmap, target(hd,pmesh));
        Segment_3 s = CGAL::make_sorted_pair(p,q);
        if (s.first==q) hd=opposite(hd,pmesh); // make sure the halfedges are consistently oriented

        duplicated_edges[s].push_back( std::make_pair(hd,&pmesh) );
      }
    }

    // consistently split duplicate edges and triangulate incident faces
    typedef std::pair<face_descriptor, FaceGraph*> Face_and_poly;
    std::set< Face_and_poly > faces_to_triangulate;
    for(const MapType::value_type& p : duplicated_edges)
      if (p.second.size()>1){
        //collect faces to retriangulate
        typedef std::pair<halfedge_descriptor, FaceGraph*> Pair_type;
        for(const Pair_type& h_and_p : p.second)
        {
          halfedge_descriptor hc=h_and_p.first;
          FaceGraph* polyc = h_and_p.second;

          if ( !is_border(hc, *polyc) )
            faces_to_triangulate.insert( Face_and_poly(face(hc,*polyc), polyc) );
          if ( !is_border(opposite(hc, *polyc), *polyc) )
            faces_to_triangulate.insert(
              Face_and_poly(face(opposite(hc, *polyc),*polyc), polyc) );
        }
        // split the edges
        split_long_duplicated_edge(p.second, target_length, edges_to_protect);
      }
    // now retriangulate
    namespace PMP=CGAL::Polygon_mesh_processing;
    for(Face_and_poly f_and_p : faces_to_triangulate)
      PMP::triangulate_face(f_and_p.first, *f_and_p.second);
  }

  void do_split_edges(const int edge_sizing_type,
                      Scene_polyhedron_selection_item* selection_item,
                      SMesh& pmesh,
                      const double target_length,
                      const double error_tol,
                      const double min_length,
                      const double max_length,
                      const double curv_ball_r)
  {
    std::vector<edge_descriptor> p_edges;
    for(edge_descriptor e : edges(pmesh))
    {
      if(get(selection_item->constrained_edges_pmap(), e))
        p_edges.push_back(e);
    }
    for(face_descriptor f : selection_item->selected_facets)
    {
      for(halfedge_descriptor he : halfedges_around_face(halfedge(f, pmesh), pmesh))
      {
        if (selection_item->selected_facets.find(face(opposite(he, pmesh), pmesh))
            == selection_item->selected_facets.end())
          p_edges.push_back(edge(he, pmesh));
      }
    }
    if (!p_edges.empty())
    {
      if (edge_sizing_type == 0)
      {
        CGAL::Polygon_mesh_processing::split_long_edges(
              p_edges
              , target_length
              , *selection_item->polyhedron()
              , CGAL::parameters::geom_traits(EPICK())
              .edge_is_constrained_map(selection_item->constrained_edges_pmap()));
      }
      else if (edge_sizing_type == 1)
      {
        std::pair<double, double> edge_min_max{min_length, max_length};
        CGAL::Polygon_mesh_processing::Adaptive_sizing_field<FaceGraph> adaptive_sizing(
              error_tol
              , edge_min_max
              , faces(*selection_item->polyhedron())
              , *selection_item->polyhedron()
              , CGAL::parameters::ball_radius(curv_ball_r));
        CGAL::Polygon_mesh_processing::split_long_edges(
          p_edges
          , adaptive_sizing
          , *selection_item->polyhedron()
          , CGAL::parameters::geom_traits(EPICK())
            .edge_is_constrained_map(selection_item->constrained_edges_pmap()));
      }
    }
    else
      std::cout << "No selected or boundary edges to be split" << std::endl;
  }

public Q_SLOTS:
  void isotropic_remeshing()
  {
    if (scene->selectionIndices().size() > 1)
    {
      isotropic_remeshing_of_several_polyhedra();
      return;
    }
    const Scene_interface::Item_id index = scene->mainSelectionIndex();

    Scene_facegraph_item* poly_item =
      qobject_cast<Scene_facegraph_item*>(scene->item(index));

    Scene_polyhedron_selection_item* selection_item =
      qobject_cast<Scene_polyhedron_selection_item*>(scene->item(index));

    if (poly_item || selection_item)
    {
      if(selection_item && selection_item->selected_edges.empty() && selection_item->selected_facets.empty())
      {
        QMessageBox::warning(mw, "Empty Edges", "There are no selected edges. Aborting.");
        return;
      }
      // Create dialog box
      QDialog dialog(mw);
      initialize_remeshing_dialog(&dialog, poly_item, selection_item);

      // Get values
      int i = dialog.exec();
      if (i == QDialog::Rejected)
      {
        std::cout << "Remeshing aborted" << std::endl;
        return;
      }

      int edge_sizing_type = ui.edgeSizing_type_combo_box->currentIndex();
      bool edges_only = ui.splitEdgesOnly_checkbox->isChecked();
      bool preserve_duplicates = ui.preserveDuplicates_checkbox->isChecked();
      double target_length = ui.edgeLength_dspinbox->value();
      double error_tol = ui.errorTol_edit->value();
      double min_length = ui.minEdgeLength_edit->value();
      double max_length = ui.maxEdgeLength_edit->value();
      unsigned int nb_iter = ui.nbIterations_spinbox->value();
      unsigned int nb_smooth = ui.nbSmoothing_spinbox->value();
      bool protect = ui.protect_checkbox->isChecked();
      bool smooth_features = ui.smooth1D_checkbox->isChecked();
      double curv_ball_r = ui.curvSmoothBallR_edit->value();

      // wait cursor
      QApplication::setOverrideCursor(Qt::WaitCursor);

      QElapsedTimer time;
      time.start();

      typedef boost::graph_traits<FaceGraph>::edge_descriptor edge_descriptor;
      typedef boost::graph_traits<FaceGraph>::face_descriptor face_descriptor;

      FaceGraph& pmesh = (poly_item != nullptr)
        ? *poly_item->polyhedron()
        : *selection_item->polyhedron();

     Patch_id_pmap fpmap = get(CGAL::face_patch_id_t<int>(), pmesh);
     bool fpmap_valid = false;
     {
       for(face_descriptor f : faces(pmesh))
       {
         if (get(fpmap, f) != 1)
         {
           fpmap_valid = true;
           break;/*1 is the default value for both Surface_mesh and Polyhedron*/
         }
       }
     }

      if (selection_item)
      {
        if (edges_only)
        {
          do_split_edges(edge_sizing_type, selection_item, pmesh,
                         target_length, error_tol, min_length, max_length, curv_ball_r);
        }
        else //not edges_only
        {
            if(protect &&
               !CGAL::Polygon_mesh_processing::internal::constraints_are_short_enough(
                 *selection_item->polyhedron(),
                 selection_item->constrained_edges_pmap(),
                 CGAL::Constant_property_map<face_descriptor, std::size_t>(1),
                 CGAL::Polygon_mesh_processing::Uniform_sizing_field( 4. / 3. * target_length, pmesh)))
            {
              QApplication::restoreOverrideCursor();
              //If facets are selected, splitting edges will add facets that won't be selected, and it will mess up the rest.
              //If there is only edges, it will work fine because new edges are dealt with in the code, so we can directly
              //split and continue.
              // Possibility todo: check if the barycenter of a new face is inside an old selected face to
              //select it again.
              if(!selection_item->selected_facets.empty())
              {
                QMessageBox::warning(mw, tr("Error"),
                                      tr("Isotropic remeshing : protect_constraints cannot be set to"
                                         " true with constraints larger than 4/3 * target_edge_length."
                                         " Aborting."));
                return;
              }
              else if(QMessageBox::question(mw, tr("Error"),
                                            tr("Isotropic remeshing : protect_constraints cannot be set to"
                                               " true with constraints larger than 4/3 * target_edge_length."
                                               " Do you wish to split the constrained edges ?")) !=
                      QMessageBox::Yes)
              {
                return;
              }
              else
              {
                do_split_edges(edge_sizing_type, selection_item, pmesh,
                               target_length, error_tol, min_length, max_length, curv_ball_r);
              }
            }

            if (selection_item->selected_facets.empty() && !selection_item->isEmpty())
            {
              if (!CGAL::is_triangle_mesh(pmesh))
              {
                QApplication::restoreOverrideCursor();
                if (QMessageBox::Ok ==
                    QMessageBox::question(mw, tr("Error - Triangulate Faces?"),
                      tr("The input mesh is not a triangulated surface mesh.\n"
                         "Do you wish to triangulate faces first, or cancel remeshing ?"),
                      (QMessageBox::Ok | QMessageBox::Cancel),
                      QMessageBox::Ok))
                {
                  QApplication::setOverrideCursor(Qt::WaitCursor);
                  CGAL::Polygon_mesh_processing::triangulate_faces(pmesh);
                }
                else
                {
                  return;
                }
              }

              if (edge_sizing_type == 0)
              {
                if (fpmap_valid)
                  CGAL::Polygon_mesh_processing::isotropic_remeshing(faces(*selection_item->polyhedron())
                     , target_length
                     , *selection_item->polyhedron()
                     , CGAL::parameters::number_of_iterations(nb_iter)
                     .protect_constraints(protect)
                     .edge_is_constrained_map(selection_item->constrained_edges_pmap())
                     .relax_constraints(smooth_features)
                     .number_of_relaxation_steps(nb_smooth)
                     .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())
                     .face_patch_map(fpmap));
                else
                  CGAL::Polygon_mesh_processing::isotropic_remeshing(faces(*selection_item->polyhedron())
                     , target_length
                     , *selection_item->polyhedron()
                     , CGAL::parameters::number_of_iterations(nb_iter)
                     .protect_constraints(protect)
                     .edge_is_constrained_map(selection_item->constrained_edges_pmap())
                     .relax_constraints(smooth_features)
                     .number_of_relaxation_steps(nb_smooth)
                     .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())
                                                                     );
              }
              else if (edge_sizing_type == 1)
              {
                std::pair<double, double> edge_min_max{min_length, max_length};
                PMP::Adaptive_sizing_field<Face_graph> adaptive_sizing_field(error_tol
                                                               , edge_min_max
                                                               , faces(*selection_item->polyhedron())
                                                               , *selection_item->polyhedron()
                                                               , CGAL::parameters::ball_radius(curv_ball_r));
                if (fpmap_valid)
                  CGAL::Polygon_mesh_processing::isotropic_remeshing(faces(*selection_item->polyhedron())
                     , adaptive_sizing_field
                     , *selection_item->polyhedron()
                     , CGAL::parameters::number_of_iterations(nb_iter)
                     .protect_constraints(protect)
                     .edge_is_constrained_map(selection_item->constrained_edges_pmap())
                     .relax_constraints(smooth_features)
                     .number_of_relaxation_steps(nb_smooth)
                     .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())
                     .face_patch_map(fpmap));
                else
                  CGAL::Polygon_mesh_processing::isotropic_remeshing(faces(*selection_item->polyhedron())
                     , adaptive_sizing_field
                     , *selection_item->polyhedron()
                     , CGAL::parameters::number_of_iterations(nb_iter)
                     .protect_constraints(protect)
                     .edge_is_constrained_map(selection_item->constrained_edges_pmap())
                     .relax_constraints(smooth_features)
                     .number_of_relaxation_steps(nb_smooth)
                     .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())
                                                                     );
              }
            }
            else //selected_facets not empty
            {
              for (auto f : selection_item->selected_facets)
              {
                if (!CGAL::is_triangle(halfedge(f, pmesh), pmesh))
                {
                  QApplication::restoreOverrideCursor();
                  if(QMessageBox::Ok ==
                     QMessageBox::question(mw, tr("Error - Triangulate Faces?"),
                       tr("The input faces selected for remeshing are not all triangle faces.\n"
                          "Do you wish to triangulate faces first, or cancel remeshing ?"),
                       (QMessageBox::Ok | QMessageBox::Cancel),
                       QMessageBox::Ok))
                  {
                    Selection_updater_visitor visitor(selection_item->selected_facets);
                    CGAL::Polygon_mesh_processing::triangulate_faces(selection_item->selected_facets,
                      pmesh,
                      CGAL::parameters::visitor(visitor));
                    break;
                  }
                  else
                  {
                    return;
                  }
                }
              }

              if (edge_sizing_type == 0)
              {
                if (fpmap_valid)
                  CGAL::Polygon_mesh_processing::isotropic_remeshing(selection_item->selected_facets
                     , target_length
                     , *selection_item->polyhedron()
                     , CGAL::parameters::number_of_iterations(nb_iter)
                     .protect_constraints(protect)
                     .edge_is_constrained_map(selection_item->constrained_edges_pmap())
                     .relax_constraints(smooth_features)
                     .number_of_relaxation_steps(nb_smooth)
                     .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())
                     .face_patch_map(fpmap));
                else
                  CGAL::Polygon_mesh_processing::isotropic_remeshing(selection_item->selected_facets
                     , target_length
                     , *selection_item->polyhedron()
                     , CGAL::parameters::number_of_iterations(nb_iter)
                     .protect_constraints(protect)
                     .edge_is_constrained_map(selection_item->constrained_edges_pmap())
                     .relax_constraints(smooth_features)
                     .number_of_relaxation_steps(nb_smooth)
                     .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())
                                                                     );
              }
              else if (edge_sizing_type == 1)
              {
                std::pair<double, double> edge_min_max{min_length, max_length};
                PMP::Adaptive_sizing_field<Face_graph> adaptive_sizing_field(error_tol
                                                               , edge_min_max
                                                               , faces(*selection_item->polyhedron())
                                                               , *selection_item->polyhedron()
                                                               , CGAL::parameters::ball_radius(curv_ball_r));
                if (fpmap_valid)
                  CGAL::Polygon_mesh_processing::isotropic_remeshing(selection_item->selected_facets
                     , adaptive_sizing_field
                     , *selection_item->polyhedron()
                     , CGAL::parameters::number_of_iterations(nb_iter)
                     .protect_constraints(protect)
                     .edge_is_constrained_map(selection_item->constrained_edges_pmap())
                     .relax_constraints(smooth_features)
                     .number_of_relaxation_steps(nb_smooth)
                     .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())
                     .face_patch_map(fpmap));
                else
                  CGAL::Polygon_mesh_processing::isotropic_remeshing(selection_item->selected_facets
                     , adaptive_sizing_field
                     , *selection_item->polyhedron()
                     , CGAL::parameters::number_of_iterations(nb_iter)
                     .protect_constraints(protect)
                     .edge_is_constrained_map(selection_item->constrained_edges_pmap())
                     .relax_constraints(smooth_features)
                     .number_of_relaxation_steps(nb_smooth)
                     .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())
                                                                     );
              }
            }
        }

        selection_item->polyhedron_item()->setColor(
              selection_item->polyhedron_item()->color());
        if(fpmap_valid)
        {
          selection_item->polyhedron_item()->setItemIsMulticolor(true);
          selection_item->polyhedron_item()->computeItemColorVectorAutomatically(true);
        }
        else
        {
          selection_item->polyhedron_item()->setItemIsMulticolor(false);
        }
        selection_item->setKeepSelectionValid(Scene_polyhedron_selection_item::Edge);
        selection_item->polyhedron_item()->invalidateOpenGLBuffers();
        Q_EMIT selection_item->polyhedron_item()->itemChanged();
        selection_item->invalidateOpenGLBuffers();
        selection_item->setKeepSelectionValid(Scene_polyhedron_selection_item::None);
      }
      else if (poly_item)
      {
        boost::property_map<FaceGraph, CGAL::edge_is_feature_t>::type eif
          = get(CGAL::edge_is_feature, pmesh);
        if (edges_only)
        {
          std::vector<edge_descriptor> edges_to_split;
          for(edge_descriptor e : edges(pmesh))
          {
            if( is_border(e, pmesh) || get(eif, e) )
              edges_to_split.push_back(e);
          }

          if (!edges_to_split.empty())
          {
            if (fpmap_valid)
            {
              if (edge_sizing_type == 0)
              {
              CGAL::Polygon_mesh_processing::split_long_edges(
                edges_to_split
                , target_length
                , pmesh
                , CGAL::parameters::geom_traits(EPICK())
                . edge_is_constrained_map(eif)
                . face_patch_map(fpmap));
              }
              else if (edge_sizing_type == 1)
              {
                std::pair<double, double> edge_min_max{min_length, max_length};
                PMP::Adaptive_sizing_field<Face_graph> adaptive_sizing_field(error_tol
                                                                , edge_min_max
                                                                , faces(pmesh)
                                                                , pmesh
                                                                , CGAL::parameters::ball_radius(curv_ball_r));
                CGAL::Polygon_mesh_processing::split_long_edges(
                  edges_to_split
                  , target_length
                  , pmesh
                  , CGAL::parameters::geom_traits(EPICK())
                  . edge_is_constrained_map(eif)
                  . face_patch_map(fpmap));
              }
            }
            else
            {
              if (edge_sizing_type == 0)
              {
              CGAL::Polygon_mesh_processing::split_long_edges(
                edges_to_split
                , target_length
                , pmesh
                , CGAL::parameters::geom_traits(EPICK())
                . edge_is_constrained_map(eif));
              }
              else if (edge_sizing_type == 1)
              {
                std::pair<double, double> edge_min_max{min_length, max_length};
                PMP::Adaptive_sizing_field<Face_graph> adaptive_sizing_field(error_tol
                                                                             , edge_min_max
                                                                             , faces(pmesh)
                                                                             , pmesh
                                                                             , CGAL::parameters::ball_radius(curv_ball_r));
                CGAL::Polygon_mesh_processing::split_long_edges(
                  edges_to_split
                  , adaptive_sizing_field
                  , pmesh
                  , CGAL::parameters::geom_traits(EPICK())
                  . edge_is_constrained_map(eif));
              }
            }
          }
          else
            std::cout << "No border to be split" << std::endl;
        }
        else
        {
          // tricks to use the function detect_and_split_duplicates
          // that uses several poly items
          std::map<FaceGraph*, Edge_set > edges_to_protect_map;
          std::vector<Scene_facegraph_item*> poly_items(1, poly_item);
          Edge_set& edges_to_protect = edges_to_protect_map[poly_item->polyhedron()];
          if (preserve_duplicates)
          {
            detect_and_split_duplicates(poly_items, edges_to_protect_map, target_length);
          }
          Scene_polyhedron_selection_item::Is_constrained_map<Edge_set> ecm(&edges_to_protect);
          for(edge_descriptor e : edges(pmesh))
          {
            if (eif[e])
              edges_to_protect.insert(e);
          }

          if(protect &&
             !CGAL::Polygon_mesh_processing::internal::constraints_are_short_enough(
               pmesh,
               ecm,
               CGAL::Constant_property_map<face_descriptor, std::size_t>(1),
               CGAL::Polygon_mesh_processing::Uniform_sizing_field(4. / 3. * target_length, pmesh)))
          {
            QApplication::restoreOverrideCursor();
            QMessageBox::warning(mw, tr("Error"),
                                 tr("Isotropic remeshing : protect_constraints cannot be set to"
                                    " true with constraints larger than 4/3 * target_edge_length."
                                    " Aborting."));
            return;
          }

          if (!CGAL::is_triangle_mesh(pmesh))
          {
            QApplication::restoreOverrideCursor();
            if (QMessageBox::Ok ==
                QMessageBox::question(mw, tr("Error - Triangulate Faces?"),
                  tr("The input mesh is not a triangulated surface mesh.\n"
                     "Do you wish to triangulate faces first, or cancel remeshing ?"),
                  (QMessageBox::Ok | QMessageBox::Cancel), QMessageBox::Ok))
            {
              QApplication::setOverrideCursor(Qt::WaitCursor);
              CGAL::Polygon_mesh_processing::triangulate_faces(pmesh);
            }
            else
            {
              return;
            }
          }

          if (edge_sizing_type == 0)
          {
            if (fpmap_valid)
              CGAL::Polygon_mesh_processing::isotropic_remeshing(
                   faces(*poly_item->polyhedron())
                 , target_length
                 , *poly_item->polyhedron()
                 , CGAL::parameters::number_of_iterations(nb_iter)
                 .protect_constraints(protect)
                 .number_of_relaxation_steps(nb_smooth)
                 .edge_is_constrained_map(ecm)
                 .relax_constraints(smooth_features)
                 .face_patch_map(fpmap));
            else
              CGAL::Polygon_mesh_processing::isotropic_remeshing(
                   faces(*poly_item->polyhedron())
                 , target_length
                 , *poly_item->polyhedron()
                 , CGAL::parameters::number_of_iterations(nb_iter)
                 .protect_constraints(protect)
                 .number_of_relaxation_steps(nb_smooth)
                 .edge_is_constrained_map(ecm)
                 .relax_constraints(smooth_features));
          }
          else if (edge_sizing_type == 1)
          {
            std::pair<double, double> edge_min_max{min_length, max_length};
            PMP::Adaptive_sizing_field<Face_graph> adaptive_sizing_field(error_tol
                    , edge_min_max
                    , faces(*poly_item->polyhedron())
                    , *poly_item->polyhedron()
                    , CGAL::parameters::ball_radius(curv_ball_r));
            if (fpmap_valid)
            CGAL::Polygon_mesh_processing::isotropic_remeshing(
                 faces(*poly_item->polyhedron())
               , adaptive_sizing_field
               , *poly_item->polyhedron()
               , CGAL::parameters::number_of_iterations(nb_iter)
               .protect_constraints(protect)
               .number_of_relaxation_steps(nb_smooth)
               .edge_is_constrained_map(ecm)
               .relax_constraints(smooth_features)
               .face_patch_map(fpmap));
            else
              CGAL::Polygon_mesh_processing::isotropic_remeshing(
                   faces(*poly_item->polyhedron())
                 , adaptive_sizing_field
                 , *poly_item->polyhedron()
                 , CGAL::parameters::number_of_iterations(nb_iter)
                 .protect_constraints(protect)
                 .number_of_relaxation_steps(nb_smooth)
                 .edge_is_constrained_map(ecm)
                 .relax_constraints(smooth_features));
          }

          //recollect sharp edges
          for(edge_descriptor e : edges(pmesh))
            eif[e] = false;
          for(edge_descriptor e : edges_to_protect)
            eif[e] = true;
        }
        if (fpmap_valid)
        {
          poly_item->setItemIsMulticolor(true);
          poly_item->show_feature_edges(true);
        }
        else
          poly_item->setItemIsMulticolor(false);

        poly_item->invalidateOpenGLBuffers();

        Q_EMIT poly_item->itemChanged();
      }
      else{
        std::cout << "Can't remesh that type of thing" << std::endl;
      }
      std::cout << "ok (" << time.elapsed() << " ms)" << std::endl;
    }
    // default cursor
    QApplication::restoreOverrideCursor();
  }

  void isotropic_remeshing_of_several_polyhedra()
  {
    // Remeshing parameters
    bool edges_only = false, preserve_duplicates = false;
    int edge_sizing_type = 0;
    double target_length = 0.;
    double error_tol = 0.;
    double min_length = 0.;
    double max_length = 0.;
    unsigned int nb_iter = 1;
    bool protect = false;
    bool smooth_features = true;

    std::vector<Scene_facegraph_item*> selection;
    for(int index : scene->selectionIndices())
    {
      Scene_facegraph_item* poly_item =
        qobject_cast<Scene_facegraph_item*>(scene->item(index));

      if (poly_item == nullptr)
      {
        std::cout << scene->item(index)->name().data()
          << " is not a FaceGraph, remeshing skipped\n";
        continue;
      }
      else
      {
        selection.push_back(poly_item);

        if (target_length == 0.)//parameters have not been set yet
        {
        QDialog dialog(mw);
        initialize_remeshing_dialog(&dialog, poly_item);
        ui.objectName->setText(QString::number(scene->selectionIndices().size())
          .append(QString(" items to be remeshed")));
        int i = dialog.exec();
        if (i == QDialog::Rejected)
        {
          std::cout << "Remeshing aborted" << std::endl;
          return;
        }

        edges_only = ui.splitEdgesOnly_checkbox->isChecked();
        preserve_duplicates = ui.preserveDuplicates_checkbox->isChecked();
        edge_sizing_type = ui.edgeSizing_type_combo_box->currentIndex();
        target_length = ui.edgeLength_dspinbox->value();
        error_tol = ui.errorTol_edit->value();
        min_length = ui.minEdgeLength_edit->value();
        max_length = ui.maxEdgeLength_edit->value();
        nb_iter = ui.nbIterations_spinbox->value();
        protect = ui.protect_checkbox->isChecked();
        smooth_features = ui.smooth1D_checkbox->isChecked();
        }
      }
    }

    if(target_length == 0.)//parameters have not been set
    {                      // i.e. no item is a polyhedron
      std::cout << "Remeshing aborted" << std::endl;
      return;
    }


    //check non-triangulated surfaces
    for (Scene_facegraph_item* poly_item : selection)
    {
      if (!CGAL::is_triangle_mesh(*poly_item->polyhedron()))
      {
        if (QMessageBox::Ok == QMessageBox::question(mw,
              tr("Error - Triangulate Faces?"),
              tr("The input mesh ").append(poly_item->name())
               .append(tr(" is not a triangulated surface mesh.\n"
                "Do you wish to triangulate faces first, or cancel remeshing ?")),
              (QMessageBox::Ok | QMessageBox::Cancel),
              QMessageBox::Ok))
        {
          QApplication::setOverrideCursor(Qt::WaitCursor);
          CGAL::Polygon_mesh_processing::triangulate_faces(*poly_item->polyhedron());
          QApplication::restoreOverrideCursor();
        }
        else
        {
          QApplication::restoreOverrideCursor();
          return;
        }
      }
    }

    // wait cursor
    QApplication::setOverrideCursor(Qt::WaitCursor);
    int total_time = 0;

    std::map<FaceGraph*,Edge_set > edges_to_protect;

    if(preserve_duplicates)
      detect_and_split_duplicates(selection, edges_to_protect, target_length);

#ifdef CGAL_LINKED_WITH_TBB
    QElapsedTimer time;
    time.start();

      tbb::parallel_for(
        tbb::blocked_range<std::size_t>(0, selection.size()),
        Remesh_polyhedron_item_for_parallel_for<Remesh_polyhedron_item>(
                                                                        selection, edges_to_protect, edges_only
                                                                      , edge_sizing_type, target_length, error_tol
                                                                      , min_length , max_length, nb_iter
                                                                      , protect, smooth_features)
                                                                        );

    total_time = time.elapsed();

#else

    Remesh_polyhedron_item remesher(edges_only, edge_sizing_type,
      target_length, error_tol, min_length, max_length, nb_iter, protect, smooth_features);
    for(Scene_facegraph_item* poly_item : selection)
    {
      QElapsedTimer time;
      time.start();

      remesher(poly_item, edges_to_protect[poly_item->polyhedron()]);

      total_time += time.elapsed();
      std::cout << "Remeshing of " << poly_item->name().data()
                << " done in " << time.elapsed() << " ms" << std::endl;
    }
#endif
    std::cout << "Remeshing of all selected items done in "
      << total_time << " ms" << std::endl;

    for(Scene_facegraph_item* poly_item : selection)
    {
      //destroys the patch_id_map for the Surface_mesh_item to avoid assertions.
      poly_item->resetColors();
      poly_item->invalidateOpenGLBuffers();
      Q_EMIT poly_item->itemChanged();
    }

    // default cursor
    QApplication::restoreOverrideCursor();
  }

private:
  Scene_interface *scene;
  QMainWindow* mw;
  struct Remesh_polyhedron_item
  {
    typedef boost::graph_traits<FaceGraph>::edge_descriptor     edge_descriptor;
    typedef boost::graph_traits<FaceGraph>::halfedge_descriptor halfedge_descriptor;
    typedef boost::graph_traits<FaceGraph>::face_descriptor     face_descriptor;

    int edge_sizing_type_;
    bool edges_only_;
    double target_length_;
    double error_tol_;
    double min_length_;
    double max_length_;
    unsigned int nb_iter_;
    bool protect_;
    bool smooth_features_;
    double curv_ball_r_;

  protected:
    void remesh(Scene_facegraph_item* poly_item,
                Edge_set& edges_to_protect) const
    {
      //fill face_index property map

      if (edges_only_)
      {
        std::vector<halfedge_descriptor> border;
        CGAL::Polygon_mesh_processing::border_halfedges(
          faces(*poly_item->polyhedron())
          , *poly_item->polyhedron()
          , std::back_inserter(border));
        std::vector<edge_descriptor> border_edges;
        for(halfedge_descriptor h : border)
          border_edges.push_back(edge(h, *poly_item->polyhedron()));

        if (edge_sizing_type_ == 0)
        {
        CGAL::Polygon_mesh_processing::split_long_edges(
            border_edges
          , target_length_
          , *poly_item->polyhedron());
        }
        else if (edge_sizing_type_ == 1)
        {
          std::pair<double, double> edge_min_max{min_length_, max_length_};
          PMP::Adaptive_sizing_field<Face_graph> adaptive_sizing_field(error_tol_
                                                                     , edge_min_max
                                                                     , faces(*poly_item->polyhedron())
                                                                     , *poly_item->polyhedron()
                                                                     , CGAL::parameters::ball_radius(curv_ball_r_));
          CGAL::Polygon_mesh_processing::split_long_edges(
            border_edges
          , target_length_
          , *poly_item->polyhedron());
        }
      }
      else
      {
        std::cout << "Isotropic remeshing of "
          << poly_item->name().toStdString() << " started..." << std::endl;
        Scene_polyhedron_selection_item::Is_constrained_map<Edge_set> ecm(&edges_to_protect);
        if (edge_sizing_type_ == 0)
        {
          CGAL::Polygon_mesh_processing::isotropic_remeshing(
              faces(*poly_item->polyhedron())
            , target_length_
            , *poly_item->polyhedron()
            , CGAL::parameters::number_of_iterations(nb_iter_)
            .protect_constraints(protect_)
            .edge_is_constrained_map(ecm)
            .face_patch_map(get(CGAL::face_patch_id_t<int>(), *poly_item->polyhedron()))
            .relax_constraints(smooth_features_));
        }
        else
        {
          std::pair<double, double> edge_min_max{min_length_, max_length_};
          PMP::Adaptive_sizing_field<Face_graph> adaptive_sizing_field(error_tol_
                                                         , edge_min_max
                                                         , faces(*poly_item->polyhedron())
                                                         , *poly_item->polyhedron()
                                                         , CGAL::parameters::ball_radius(curv_ball_r_));
          CGAL::Polygon_mesh_processing::isotropic_remeshing(
              faces(*poly_item->polyhedron())
            , target_length_
            , *poly_item->polyhedron()
            , CGAL::parameters::number_of_iterations(nb_iter_)
            .protect_constraints(protect_)
            .edge_is_constrained_map(ecm)
            .face_patch_map(get(CGAL::face_patch_id_t<int>(), *poly_item->polyhedron()))
            .relax_constraints(smooth_features_));
        }
        std::cout << "Isotropic remeshing of "
          << poly_item->name().toStdString() << " done." << std::endl;
      }
    }

  public:
    Remesh_polyhedron_item(
      const bool edges_only,
      const int edge_sizing_type,
      const double target_length,
      const double error_tol,
      const double min_length,
      const double max_length,
      const unsigned int nb_iter,
      const bool protect,
      const bool smooth_features)
      : edge_sizing_type_(edge_sizing_type)
      , edges_only_(edges_only)
      , target_length_(target_length)
      , error_tol_(error_tol)
      , min_length_(min_length)
      , max_length_(max_length)
      , nb_iter_(nb_iter)
      , protect_(protect)
      , smooth_features_(smooth_features)
    {}

    Remesh_polyhedron_item(const Remesh_polyhedron_item& remesh)
      : edge_sizing_type_(remesh.edge_sizing_type_)
      , edges_only_(remesh.edges_only_)
      , target_length_(remesh.target_length_)
      , error_tol_(remesh.error_tol_)
      , min_length_(remesh.min_length_)
      , max_length_(remesh.max_length_)
      , nb_iter_(remesh.nb_iter_)
      , protect_(remesh.protect_)
      , smooth_features_(remesh.smooth_features_)
    {}

    void operator()(Scene_facegraph_item* poly_item,
                    Edge_set& edges_to_protect) const
    {
      remesh(poly_item, edges_to_protect);
    }
  };

#ifdef CGAL_LINKED_WITH_TBB
  template<typename RemeshFunctor>
  struct Remesh_polyhedron_item_for_parallel_for
    : RemeshFunctor
  {
    const std::vector<Scene_facegraph_item*>& selection_;
    std::map<FaceGraph*,Edge_set >& edges_to_protect_;

  public:
    // Constructor
    Remesh_polyhedron_item_for_parallel_for(
      const std::vector<Scene_facegraph_item*>& selection,
      std::map<FaceGraph*,Edge_set >& edges_to_protect,
      const bool edges_only,
      const int edge_sizing_type,
      const double target_length,
      const double error_tol,
      const double min_length,
      const double max_length,
      const unsigned int nb_iter,
      const bool protect,
      const bool smooth_features)
      : RemeshFunctor(edges_only, edge_sizing_type, target_length
                    , error_tol, min_length, max_length
                    , nb_iter, protect, smooth_features)
      , selection_(selection), edges_to_protect_(edges_to_protect)
    {}

    // Constructor
    Remesh_polyhedron_item_for_parallel_for(
      const Remesh_polyhedron_item_for_parallel_for &remesh)
      : RemeshFunctor(remesh)
      , selection_(remesh.selection_)
      , edges_to_protect_(remesh.edges_to_protect_)
    {}

    // operator()
    void operator()(const tbb::blocked_range<size_t>& r) const
    {
      for (size_t i = r.begin(); i != r.end(); ++i)
        RemeshFunctor::remesh(selection_[i], edges_to_protect_[selection_[i]->polyhedron()]);
    }
  };
#endif

public Q_SLOTS:
  void update_after_protect_checkbox_click()
  {
    if(ui.protect_checkbox->isChecked())
    {
      ui.smooth1D_label->setEnabled(false);
      ui.smooth1D_checkbox->setEnabled(false);
      ui.smooth1D_checkbox->setChecked(false);
    }
    else
    {
      ui.smooth1D_label->setEnabled(true);
      ui.smooth1D_checkbox->setEnabled(true);
    }
  }

  void update_after_splitEdgesOnly_click()
  {
    if(ui.splitEdgesOnly_checkbox->isChecked())
    {
      ui.nbIterations_label->setEnabled(false);
      ui.nbIterations_spinbox->setEnabled(false);
      ui.nbSmoothing_label->setEnabled(false);
      ui.nbSmoothing_spinbox->setEnabled(false);

      ui.protect_label->setEnabled(false);
      ui.protect_checkbox->setEnabled(false);
      ui.protect_checkbox->setChecked(false);

      ui.smooth1D_label->setEnabled(false);
      ui.smooth1D_checkbox->setEnabled(false);
      ui.smooth1D_checkbox->setChecked(false);
    }
    else
    {
      ui.nbIterations_label->setEnabled(true);
      ui.nbIterations_spinbox->setEnabled(true);
      ui.nbSmoothing_label->setEnabled(true);
      ui.nbSmoothing_spinbox->setEnabled(true);

      ui.protect_label->setEnabled(true);
      ui.protect_checkbox->setEnabled(true);

      ui.smooth1D_label->setEnabled(true);
      ui.smooth1D_checkbox->setEnabled(true);
    }
  }

  void on_edgeSizing_type_combo_box_changed(int index)
  {
    if (index == 0)
    {
      ui.edgeLength_label->show();
      ui.edgeLength_dspinbox->show();
      ui.errorTol_label->hide();
      ui.errorTol_edit->hide();
      ui.minEdgeLength_label->hide();
      ui.minEdgeLength_edit->hide();
      ui.maxEdgeLength_label->hide();
      ui.maxEdgeLength_edit->hide();
      ui.curvSmooth_checkbox->hide();
      ui.curvSmooth_label->hide();
      ui.curvSmoothBallR_edit->hide();
      ui.curvSmoothBallR_label->hide();
    }
    else if (index == 1)
    {
      ui.edgeLength_label->hide();
      ui.edgeLength_dspinbox->hide();
      ui.errorTol_label->show();
      ui.errorTol_edit->show();
      ui.minEdgeLength_label->show();
      ui.minEdgeLength_edit->show();
      ui.maxEdgeLength_label->show();
      ui.maxEdgeLength_edit->show();
      ui.curvSmooth_checkbox->show();
      ui.curvSmooth_label->show();
      ui.curvSmoothBallR_edit->show();
      ui.curvSmoothBallR_label->show();
    }
  }

  void update_after_curvSmooth_click()
  {
    if (ui.curvSmooth_checkbox->isChecked())
    {
      ui.curvSmoothBallR_label->setEnabled(true);
      ui.curvSmoothBallR_edit->setEnabled(true);
    }
    else
    {
      ui.curvSmoothBallR_label->setEnabled(false);
      ui.curvSmoothBallR_edit->setValue(-1);
      ui.curvSmoothBallR_edit->setEnabled(false);
    }
  }

public:
  void
  initialize_remeshing_dialog(QDialog* dialog,
                              Scene_facegraph_item* poly_item,
                              Scene_polyhedron_selection_item* selection_item = nullptr)
  {
    ui.setupUi(dialog);
    connect(ui.buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
    connect(ui.buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));

    //connect checkbox to spinbox
    connect(ui.preserveDuplicates_checkbox, SIGNAL(toggled(bool)),
            ui.protect_checkbox, SLOT(setChecked(bool)));
    connect(ui.preserveDuplicates_checkbox, SIGNAL(toggled(bool)),
            ui.protect_checkbox, SLOT(setDisabled(bool)));

    connect(ui.protect_checkbox, SIGNAL(clicked(bool)), this, SLOT(update_after_protect_checkbox_click()));
    connect(ui.splitEdgesOnly_checkbox, SIGNAL(clicked(bool)), this, SLOT(update_after_splitEdgesOnly_click()));
    connect(ui.edgeSizing_type_combo_box, SIGNAL(currentIndexChanged(int)),
      this, SLOT(on_edgeSizing_type_combo_box_changed(int)));
    connect(ui.curvSmooth_checkbox, SIGNAL(clicked(bool)), this, SLOT(update_after_curvSmooth_click()));

    //Set default parameters
    Scene_interface::Bbox bbox = poly_item != nullptr ? poly_item->bbox()
      : (selection_item != nullptr ? selection_item->bbox()
        : scene->bbox());
    ui.objectName->setText(poly_item != nullptr ? poly_item->name()
      : (selection_item != nullptr ? selection_item->name()
        : QString("Remeshing parameters")));

    ui.objectNameSize->setText(
      tr("Object bbox size (w,h,d):  <b>%1</b>,  <b>%2</b>,  <b>%3</b>")
      .arg(bbox.xmax()-bbox.xmin(), 0, 'g', 3)
      .arg(bbox.ymax()-bbox.ymin(), 0, 'g', 3)
      .arg(bbox.zmax()-bbox.zmin(), 0, 'g', 3));

    double diago_length = CGAL::sqrt((bbox.xmax()-bbox.xmin())*(bbox.xmax()-bbox.xmin())
                                   + (bbox.ymax()-bbox.ymin())*(bbox.ymax()-bbox.ymin())
                                   + (bbox.zmax()-bbox.zmin())*(bbox.zmax()-bbox.zmin()));


    ui.edgeLength_dspinbox->setValue(0.05 * diago_length);
    ui.errorTol_edit->setValue(0.001 * diago_length);
    ui.minEdgeLength_edit->setValue(0.001 * diago_length);
    ui.maxEdgeLength_edit->setValue(0.5 * diago_length);

    std::string diag_general_info = "Diagonal length of the Bbox of the selection to remesh is "
                                    + std::to_string(diago_length) + ".\n";
    std::string specific_info;
    specific_info = "Default is 5% of it\n";
    ui.edgeLength_dspinbox->setToolTip(QString::fromStdString(diag_general_info + specific_info));
    specific_info = "Default is 0.1% of it\n";
    ui.errorTol_edit->setToolTip(QString::fromStdString(diag_general_info + specific_info));
    specific_info = "Default is 0.1% of it\n";
    ui.minEdgeLength_edit->setToolTip(QString::fromStdString(diag_general_info + specific_info));
    specific_info = "Default is 50% of it\n";
    ui.maxEdgeLength_edit->setToolTip(QString::fromStdString(diag_general_info + specific_info));

    ui.nbIterations_spinbox->setSingleStep(1);
    ui.nbIterations_spinbox->setRange(1/*min*/, 1000/*max*/);
    ui.nbIterations_spinbox->setValue(1);

    ui.edgeSizing_type_combo_box->setCurrentIndex(0);
    on_edgeSizing_type_combo_box_changed(0);
    ui.protect_checkbox->setChecked(false);
    ui.smooth1D_checkbox->setChecked(true);
    ui.curvSmooth_checkbox->setChecked(false);
    ui.curvSmoothBallR_label->setEnabled(false);
    ui.curvSmoothBallR_edit->setEnabled(false);
    ui.curvSmoothBallR_edit->setValue(-1);

    if (nullptr != selection_item)
    {
      //do not preserve duplicates in selection mode
      ui.preserveDuplicates_checkbox->setDisabled(true);
      ui.preserveDuplicates_checkbox->setChecked(false);
    }
  }

private:
  QAction* actionIsotropicRemeshing_;
  Ui::Isotropic_remeshing_dialog ui;
}; // end CGAL_Lab_isotropic_remeshing_plugin

#include "Isotropic_remeshing_plugin.moc"
