#include <QApplication>
#include <QMainWindow>
#include <QAction>
#include <QVector>
#include <QMessageBox>
#include <QBitmap>
#include <QTabBar>

#include <CGAL/boost/graph/helpers.h>
#include <CGAL/boost/graph/Euler_operations.h>
#include <CGAL/boost/graph/generators.h>

#include <CGAL/subdivision_method_3.h>
#include <CGAL/Kernel_traits.h>

#include <CGAL/Polygon_mesh_processing/triangulate_hole.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>

#include <CGAL/Three/Scene_item.h>
#include <CGAL/Three/Viewer_interface.h>
#include <CGAL/Three/CGAL_Lab_plugin_helper.h>
#include <CGAL/Three/Three.h>

#include "Scene_surface_mesh_item.h"
#include "Scene_points_with_normal_item.h"
#include "Scene_polylines_item.h"

#include "ui_Basic_generator_widget.h"

struct Face : public std::array<int,3>
{
  Face(int i, int j, int k)
  {
    (*this)[0] = i;
    (*this)[1] = j;
    (*this)[2] = k;
  }
};

class GeneratorWidget :
    public QDockWidget,
    public Ui::BasicGenerator
{
  Q_OBJECT
public:
  GeneratorWidget(QString name, QWidget *parent)
    :QDockWidget(name,parent)
  {
    setupUi(this);
  }
};

typedef Kernel::Point_3 Point;

namespace euler =  CGAL::Euler;
using namespace CGAL::Three;
namespace params = CGAL::parameters;

class Q_DECL_EXPORT Basic_generator_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" FILE "basic_generator_plugin.json")
public :
  void init(QMainWindow* mainWindow,
            CGAL::Three::Scene_interface* scene_interface,
            Messages_interface*)
  {
    this->scene = scene_interface;
    this->mw = mainWindow;
    for(int i=0; i<=POLYLINE; ++i)
      nbs[i]=0;


    QMenu* menuFile = mw->findChild<QMenu*>("menuFile");

    QMenu* menu = menuFile->findChild<QMenu*>("menuGenerateObject");
    if(!menu){
      QAction* actionLoad = mw->findChild<QAction*>("actionLoadPlugin");
      menu = new QMenu(tr("Generate &Object"), menuFile);
      menu->setObjectName("menuGenerateObject");
      menuFile->insertMenu(actionLoad, menu);
    }


    QAction* actionPrism       = new QAction("P&rism", mw);
    QAction* actionSphere      = new QAction("&Sphere", mw);
    QAction* actionPyramid     = new QAction("Py&ramid", mw);
    QAction* actionHexahedron  = new QAction("&Hexahedron", mw);
    QAction* actionTetrahedron = new QAction("&Tetrahedron", mw);
    QAction* actionGrid        = new QAction("&Grid", mw);
    QAction* actionPointSet    = new QAction("Po&int Set", mw);
    QAction* actionPolyline    = new QAction("Po&lyline", mw);




    connect(actionPrism, SIGNAL(triggered()),
            this, SLOT(on_actionPrism_triggered()));
    _actions << actionPrism;

    connect(actionSphere, SIGNAL(triggered()),
            this, SLOT(on_actionSphere_triggered()));
    _actions << actionSphere;

    connect(actionPyramid, SIGNAL(triggered()),
            this, SLOT(on_actionPyramid_triggered()));
    _actions << actionPyramid;

    connect(actionHexahedron, SIGNAL(triggered()),
            this, SLOT(on_actionHexahedron_triggered()));
    _actions << actionHexahedron;

    connect(actionTetrahedron, SIGNAL(triggered()),
            this, SLOT(on_actionTetrahedron_triggered()));
    _actions << actionTetrahedron;

    connect(actionGrid, SIGNAL(triggered()),
            this, SLOT(on_actionGrid_triggered()));
    _actions << actionGrid;

    connect(actionPointSet, SIGNAL(triggered()),
            this, SLOT(on_actionPointSet_triggered()));
    _actions << actionPointSet;

    connect(actionPolyline, SIGNAL(triggered()),
            this, SLOT(on_actionPolyline_triggered()));
    _actions << actionPolyline;

    for(QAction* action : _actions)
    {
      menu->addAction(action);
    }
    dock_widget = new GeneratorWidget("Basic Objects", mw);
    dock_widget->setVisible(false); // do not show at the beginning
    addDockWidget(dock_widget);
    connect(dock_widget->generateButton, &QAbstractButton::clicked,
            this, &Basic_generator_plugin::on_generate_clicked);
    connect(dock_widget->selector_tabWidget, &QTabWidget::currentChanged,
            this, &Basic_generator_plugin::on_tab_changed);
    connect(dock_widget, &GeneratorWidget::visibilityChanged,
            this, &Basic_generator_plugin::on_tab_changed);
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
    connect(dock_widget->prismCheckBox, &QCheckBox::checkStateChanged,
            this, &Basic_generator_plugin::on_tab_changed);
    connect(dock_widget->pyramidCheckBox, &QCheckBox::checkStateChanged,
            this, &Basic_generator_plugin::on_tab_changed);
#else
    connect(dock_widget->prismCheckBox, &QCheckBox::stateChanged,
            this, &Basic_generator_plugin::on_tab_changed);
    connect(dock_widget->pyramidCheckBox, &QCheckBox::stateChanged,
            this, &Basic_generator_plugin::on_tab_changed);
#endif
    connect(dock_widget->polygon_checkBox, SIGNAL(toggled(bool)),
            dock_widget->fill_checkBox, SLOT(setEnabled(bool)));
  }

  bool applicable(QAction*) const
  {
    //not in the Operations menu
    return false;
  }
  QList<QAction*> actions() const {
    return QList<QAction*>();
  }
public Q_SLOTS:

  void on_actionPrism_triggered();
  void on_actionSphere_triggered();
  void on_actionPyramid_triggered();
  void on_actionHexahedron_triggered();
  void on_actionTetrahedron_triggered();
  void on_actionGrid_triggered();
  void on_actionPointSet_triggered();
  void on_actionPolyline_triggered();
  void on_generate_clicked();
  void on_tab_changed();
  void closure(){ dock_widget->hide(); }
private:
  QList<QAction*> _actions;
  GeneratorWidget* dock_widget;
  enum VolumeTab
  {
    PRISM=0,
    SPHERE,
    PYRAMID,
    HEXAHEDRON,
    TETRAHEDRON,
    GRID,
    POINT_SET,
    POLYLINE
  };

  int nbs[POLYLINE+1];
  template<class Facegraph_item>
  void generateCube();
  template<class Facegraph_item>
  void generatePrism();
  template<class Facegraph_item>
  void generatePyramid();
  template<class Facegraph_item>
  void generateSphere();
  template<class Facegraph_item>
  void generateTetrahedron();
  void generatePoints();
  void generateLines();
  template<class Facegraph_item>
  void generateGrid();
}; //end of class Basic_generator_plugin

//show the widget
void Basic_generator_plugin::on_actionPrism_triggered()
{
  dock_widget->show(); dock_widget->raise();
  dock_widget->selector_tabWidget->tabBar()->setCurrentIndex(PRISM);
}
void Basic_generator_plugin::on_actionSphere_triggered()
{
  dock_widget->show(); dock_widget->raise();
  dock_widget->selector_tabWidget->tabBar()->setCurrentIndex(SPHERE);
}
void Basic_generator_plugin::on_actionPyramid_triggered()
{
  dock_widget->show(); dock_widget->raise();
  dock_widget->selector_tabWidget->tabBar()->setCurrentIndex(PYRAMID);
}
void Basic_generator_plugin::on_actionHexahedron_triggered()
{
  dock_widget->show(); dock_widget->raise();
  dock_widget->selector_tabWidget->tabBar()->setCurrentIndex(HEXAHEDRON);
}
void Basic_generator_plugin::on_actionTetrahedron_triggered()
{
  dock_widget->show(); dock_widget->raise();
  dock_widget->selector_tabWidget->tabBar()->setCurrentIndex(TETRAHEDRON);
}
void Basic_generator_plugin::on_actionGrid_triggered()
{
  dock_widget->show(); dock_widget->raise();
  dock_widget->selector_tabWidget->tabBar()->setCurrentIndex(GRID);
}
void Basic_generator_plugin::on_actionPointSet_triggered()
{
  dock_widget->show(); dock_widget->raise();
  dock_widget->selector_tabWidget->tabBar()->setCurrentIndex(POINT_SET);
}
void Basic_generator_plugin::on_actionPolyline_triggered()
{
  dock_widget->show(); dock_widget->raise();
  dock_widget->selector_tabWidget->tabBar()->setCurrentIndex(POLYLINE);
}
void Basic_generator_plugin::on_tab_changed()
{
  QString name;
  int nb = 0;
  switch(dock_widget->selector_tabWidget->currentIndex())
  {
  case PRISM:
  {
    name = QString("Prism");
    nb = nbs[PRISM];
    QPixmap pic;
    if(dock_widget->prismCheckBox->isChecked())
      pic = QPixmap(":/cgal/Lab/resources/prism.png");
    else
      pic = QPixmap(":/cgal/Lab/resources/prism-open.png");
    dock_widget->prism_picLabel->setPixmap(pic);
    dock_widget->prism_picLabel->show();
  }
    break;
  case SPHERE:
    name = QString("Sphere");
    nb = nbs[SPHERE];
    break;

  case PYRAMID:
  {
    name = QString("Pyramid");
    nb = nbs[PYRAMID];
    QPixmap pic;
    if(dock_widget->pyramidCheckBox->isChecked())
      pic = QPixmap(":/cgal/Lab/resources/pyramid.png");
    else
      pic = QPixmap(":/cgal/Lab/resources/pyramid-open.png");
    dock_widget->pyramid_picLabel->setPixmap(pic);
    dock_widget->pyramid_picLabel->show();
  }
    break;

  case HEXAHEDRON:
    name = QString("Hexahedron");
    nb = nbs[HEXAHEDRON];
    break;

  case TETRAHEDRON:
    name = QString("Tetrahedron");
    nb = nbs[TETRAHEDRON];
    break;
  case GRID:
    name = QString("Grid");
    nb = nbs[GRID];
    break;
  case POINT_SET:
    name = QString("Point_set");
    nb = nbs[POINT_SET];
    break;

  case POLYLINE:
    name = QString("Polyline");
    nb = nbs[POLYLINE];
    break;
  default:
    break;
  };
  dock_widget->name_lineEdit->setText((nb==0)?name:QString(name).append(QString("%1").arg(nb)));
}
//generate
void Basic_generator_plugin::on_generate_clicked()
{
  switch(dock_widget->selector_tabWidget->currentIndex())
  {
  case PRISM:
    generatePrism<Scene_surface_mesh_item>();
    ++nbs[PRISM];
    break;

  case SPHERE:
    generateSphere<Scene_surface_mesh_item>();
    ++nbs[SPHERE];
    break;

  case PYRAMID:
    generatePyramid<Scene_surface_mesh_item>();
    ++nbs[PYRAMID];
    break;

  case HEXAHEDRON:
    generateCube<Scene_surface_mesh_item>();
    ++nbs[HEXAHEDRON];
    break;

  case TETRAHEDRON:
    generateTetrahedron<Scene_surface_mesh_item>();
    ++nbs[TETRAHEDRON];
    break;
  case GRID:
    generateGrid<Scene_surface_mesh_item>();
    ++nbs[GRID];
    break;
  case POINT_SET:
    generatePoints();
    ++nbs[POINT_SET];
    break;

  case POLYLINE:
    generateLines();
    ++nbs[POLYLINE];
    break;
  default:
    break;
  };
  on_tab_changed();
}
//make a non triangle hexahedron
template<class Facegraph_item>
void Basic_generator_plugin::generateCube()
{
  typename Facegraph_item::Face_graph cube;
  if(dock_widget->tabWidget_2->currentIndex() == 0)
  {
    QString point_texts[8];
    Point points[8];
    point_texts[0] = dock_widget->cubeP0->text(); point_texts[4] = dock_widget->cubeP4->text();
    point_texts[1] = dock_widget->cubeP1->text(); point_texts[5] = dock_widget->cubeP5->text();
    point_texts[2] = dock_widget->cubeP2->text(); point_texts[6] = dock_widget->cubeP6->text();
    point_texts[3] = dock_widget->cubeP3->text(); point_texts[7] = dock_widget->cubeP7->text();

    for(int i=0; i<8; ++i)
    {
      QStringList list = point_texts[i].split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
      if (list.isEmpty()) return;
      if (list.size()!=3){
        QMessageBox *msgBox = new QMessageBox;
        msgBox->setWindowTitle("Error");
        msgBox->setText("ERROR : Input should consist of 3 doubles.");
        msgBox->exec();
        return;
      }
      double coords[3];
      for(int j=0; j<3; ++j)
      {
        bool ok;
        coords[j] = list.at(j).toDouble(&ok);
        if(!ok)
        {
            QMessageBox *msgBox = new QMessageBox;
            msgBox->setWindowTitle("Error");
            msgBox->setText("ERROR : Coordinates are invalid.");
            msgBox->exec();
            return;
        }
      }
      points[i] = Point(coords[0], coords[1], coords[2]);
    }

    CGAL::make_hexahedron(
          points[0],
        points[1],
        points[2],
        points[3],

        points[4],
        points[5],
        points[6],
        points[7],
        cube);
  }
  else
  {
    QString text = dock_widget->extremaEdit->text();
    QStringList list = text.split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
    if (list.isEmpty()) return;
    if (list.size()!=6){
      QMessageBox *msgBox = new QMessageBox;
      msgBox->setWindowTitle("Error");
      msgBox->setText("ERROR : Input should consist of 6 doubles.");
      msgBox->exec();
      return;
    }

    for(int i=0; i<6; ++i)
    {
      bool ok;
      list.at(i).toDouble(&ok);
      if(!ok)
      {
        QMessageBox *msgBox = new QMessageBox;
        msgBox->setWindowTitle("Error");
        msgBox->setText("ERROR : Coordinates are invalid.");
        msgBox->exec();
        return;
      }
    }
    CGAL::make_hexahedron(
          Point(list.at(0).toDouble(),list.at(1).toDouble(),list.at(5).toDouble()),
          Point(list.at(3).toDouble(),list.at(1).toDouble(),list.at(5).toDouble()),
          Point(list.at(3).toDouble(),list.at(1).toDouble(),list.at(2).toDouble()),
          Point(list.at(0).toDouble(),list.at(1).toDouble(),list.at(2).toDouble()),

          Point(list.at(0).toDouble(),list.at(4).toDouble(),list.at(2).toDouble()),
          Point(list.at(0).toDouble(),list.at(4).toDouble(),list.at(5).toDouble()),
          Point(list.at(3).toDouble(),list.at(4).toDouble(),list.at(5).toDouble()),
          Point(list.at(3).toDouble(),list.at(4).toDouble(),list.at(2).toDouble()),
          cube);
  }
  Facegraph_item* cube_item = new Facegraph_item(cube);
  cube_item->setName(dock_widget->name_lineEdit->text());
  Scene_interface::Item_id id = scene->addItem(cube_item);
  scene->setSelectedItem(id);
}
//make a prism
template<class Facegraph_item>
void Basic_generator_plugin::generatePrism()
{
  //gets the precision parameter
  int nb_vertices = dock_widget->prismSpinBox->value();
  double height(dock_widget->prismHeightSpinBox->value()),
      radius(dock_widget->prismBaseSpinBox->value());
  bool is_closed = dock_widget->prismCheckBox->isChecked();

  QString text = dock_widget->prism_lineEdit->text();
  QStringList list = text.split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
  if (list.isEmpty()) return;
  if (list.size()!=3){
    QMessageBox *msgBox = new QMessageBox;
    msgBox->setWindowTitle("Error");
    msgBox->setText("ERROR : Input should consist of 3 doubles.");
    msgBox->exec();
    return;
  }
  double coords[3];
  for(int i=0; i<3; ++i)
  {
    bool ok;
    coords[i] = list.at(i).toDouble(&ok);
    if(!ok)
    {
      QMessageBox *msgBox = new QMessageBox;
      msgBox->setWindowTitle("Error");
      msgBox->setText("ERROR : Coordinates are invalid.");
      msgBox->exec();
      return;
    }
  }

  typename Facegraph_item::Face_graph prism;
  make_regular_prism(nb_vertices,
                     prism,
                     Point(coords[0], coords[1], coords[2]),
                     height,
                     radius,
                     is_closed);

  Facegraph_item* prism_item = new Facegraph_item(prism);
  prism_item->setName(dock_widget->name_lineEdit->text());
  Scene_interface::Item_id id = scene->addItem(prism_item);
  scene->setSelectedItem(id);
}
//make a pyramid
template<class Facegraph_item>
void Basic_generator_plugin::generatePyramid()
{
  int nb_vertices = dock_widget->pyramidSpinBox->value();
  double height(dock_widget->pyramidHeightSpinBox->value()),
      radius(dock_widget->pyramidBaseSpinBox->value());
  bool is_closed = dock_widget->pyramidCheckBox->isChecked();

  QString text = dock_widget->pyramid_lineEdit->text();
  QStringList list = text.split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
  if (list.isEmpty()) return;
  if (list.size()!=3){
    QMessageBox *msgBox = new QMessageBox;
    msgBox->setWindowTitle("Error");
    msgBox->setText("ERROR : Input should consist of 3 doubles.");
    msgBox->exec();
    return;
  }
  double coords[3];
  for(int i=0; i<3; ++i)
  {
    bool ok;
    coords[i] = list.at(i).toDouble(&ok);
    if(!ok)
    {
      QMessageBox *msgBox = new QMessageBox;
      msgBox->setWindowTitle("Error");
      msgBox->setText("ERROR : Coordinates are invalid.");
      msgBox->exec();
      return;
    }
  }

  typename Facegraph_item::Face_graph pyramid;
  make_pyramid(nb_vertices,
               pyramid,
               Point(coords[0], coords[1], coords[2]),
               height,
               radius,
               is_closed);

  Facegraph_item* pyramid_item = new Facegraph_item(pyramid);
  pyramid_item->setName(dock_widget->name_lineEdit->text());
  Scene_interface::Item_id id = scene->addItem(pyramid_item);
  scene->setSelectedItem(id);
}
//make a sphere
template<class Facegraph_item>
void Basic_generator_plugin::generateSphere()
{
  int precision = dock_widget->SphereSpinBox->value();
  QString text = dock_widget->center_radius_lineEdit->text();
  QStringList list = text.split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
  if (list.isEmpty()) return;
  if (list.size()!=4){
    QMessageBox *msgBox = new QMessageBox;
    msgBox->setWindowTitle("Error");
    msgBox->setText("ERROR : Input should consist of four doubles.");
    msgBox->exec();
    return;
  }

  double radius = list.at(3).toDouble();
  Point center = Point(list.at(0).toDouble(), list.at(1).toDouble(), list.at(2).toDouble());
  typename Facegraph_item::Face_graph sphere;
  make_icosahedron(sphere, center, radius);
  typedef typename boost::property_map<typename Facegraph_item::Face_graph, CGAL::vertex_point_t>::type VPMap;
  if(precision !=0)
    CGAL::Subdivision_method_3::Sqrt3_subdivision(sphere,
                                                  params::number_of_iterations(precision));
  VPMap vpmap = get(CGAL::vertex_point, sphere);
  //emplace the points back on the sphere
  for(typename boost::graph_traits<typename Facegraph_item::Face_graph>::vertex_descriptor vd : vertices(sphere))
  {
    Kernel::Vector_3 vec(center, get(vpmap, vd));
    vec = radius*vec/CGAL::sqrt(vec.squared_length());
    put(vpmap, vd, Kernel::Point_3(center.x() + vec.x(),
                                   center.y() + vec.y(),
                                   center.z() + vec.z()));
  }

  Facegraph_item* sphere_item = new Facegraph_item(sphere);
  sphere_item->setName(dock_widget->name_lineEdit->text());
  Scene_interface::Item_id id = scene->addItem(sphere_item);
  scene->setSelectedItem(id);
}
//make a tetrahedron
template<class Facegraph_item>
void Basic_generator_plugin::generateTetrahedron()
{
  typename Facegraph_item::Face_graph tetrahedron;
  if (dock_widget->tabWidget->currentIndex() == 0)
  {
    QString point_texts[4];
    Point points[4];
    point_texts[0] = dock_widget->tetP0->text();
    point_texts[1] = dock_widget->tetP1->text();
    point_texts[2] = dock_widget->tetP2->text();
    point_texts[3] = dock_widget->tetP3->text();

    for (int i = 0; i < 4; ++i)
    {
      QStringList list = point_texts[i].split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
      if (list.isEmpty()) return;
      if (list.size() != 3) {
        QMessageBox* msgBox = new QMessageBox;
        msgBox->setWindowTitle("Error");
        msgBox->setText("ERROR : Input should consist of 3 doubles.");
        msgBox->exec();
        return;
      }
      double coords[3];
      for (int j = 0; j < 3; ++j)
      {
        bool ok;
        coords[j] = list.at(j).toDouble(&ok);
        if (!ok)
        {
          QMessageBox* msgBox = new QMessageBox;
          msgBox->setWindowTitle("Error");
          msgBox->setText("ERROR : Coordinates are invalid.");
          msgBox->exec();
          return;
        }
      }
      points[i] = Point(coords[0], coords[1], coords[2]);
    }
      CGAL::make_tetrahedron(points[0],
          points[1],
          points[2],
          points[3],
          tetrahedron);
  }
  else
  {
      QString text = dock_widget->point_textEdit_2->toPlainText();
      QStringList list = text.split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
      if (list.isEmpty()) return;
      if (list.size() != 12) {
          QMessageBox* msgBox = new QMessageBox;
          msgBox->setWindowTitle("Error");
          msgBox->setText("ERROR : Input should consist of 12 doubles.");
          msgBox->exec();
          return;
      }

      for (int i = 0; i < 12; ++i)
      {
          bool ok;
          list.at(i).toDouble(&ok);
          if (!ok)
          {
              QMessageBox* msgBox = new QMessageBox;
              msgBox->setWindowTitle("Error");
              msgBox->setText("ERROR : Coordinates are invalid.");
              msgBox->exec();
              return;
          }
      }
      CGAL::make_tetrahedron(
          Point(list.at(0).toDouble(), list.at(1).toDouble(), list.at(2).toDouble()),
          Point(list.at(3).toDouble(), list.at(4).toDouble(), list.at(5).toDouble()),
          Point(list.at(6).toDouble(), list.at(7).toDouble(), list.at(8).toDouble()),
          Point(list.at(9).toDouble(), list.at(10).toDouble(), list.at(11).toDouble()),
          tetrahedron);
  }

  Facegraph_item* tet_item = new Facegraph_item(tetrahedron);
  tet_item->setName(dock_widget->name_lineEdit->text());
  Scene_interface::Item_id id = scene->addItem(tet_item);
  scene->setSelectedItem(id);
}
//make a point set
void Basic_generator_plugin::generatePoints()
{
  QString text = dock_widget->point_textEdit->toPlainText();
  Scene_points_with_normal_item* item = new Scene_points_with_normal_item();
  QStringList list = text.split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
  int counter = 0;
  double coord[3];
  bool ok = true;
  if (list.isEmpty()) return;
  if (list.size()%3!=0){
    QMessageBox *msgBox = new QMessageBox;
    msgBox->setWindowTitle("Error");
    msgBox->setText("ERROR : Input should consist of triplets.");
    msgBox->exec();
    return;
  }

  for(QString s : list)
  {
    if(!s.isEmpty())
    {
      double res = s.toDouble(&ok);
      if(!ok)
      {
        QMessageBox *msgBox = new QMessageBox;
        msgBox->setWindowTitle("Error");
        msgBox->setText("ERROR : Coordinates are invalid.");
        msgBox->exec();
        break;
      }
      else
      {
        coord[counter] = res;
        counter++;
      }
    }
    if(counter == 3)
    {
      const Kernel::Point_3 p(coord[0], coord[1], coord[2]);
      item->point_set()->insert(p);
      counter =0;
    }
  }
  if(ok)
  {
    dock_widget->point_textEdit->clear();
    item->point_set()->unselect_all();
    item->setName(dock_widget->name_lineEdit->text());
    item->setColor(Qt::black);
    item->invalidateOpenGLBuffers();
    Scene_interface::Item_id id = scene->addItem(item);
    scene->setSelectedItem(id);
  }
}
//make a polyline
void Basic_generator_plugin::generateLines()
{
  QString text = dock_widget->line_textEdit->toPlainText();
  std::list<std::vector<Scene_polylines_item::Point_3> > polylines;

  auto read_polyline = [&polylines](const QStringList& list, bool is_2d, bool is_closed)
  {
    int counter = -1;
    double coord[3];
    bool ok = true;

    if(!is_2d && list.size()%3!=0){
      QMessageBox *msgBox = new QMessageBox;
      msgBox->setWindowTitle("Error");
      msgBox->setText("ERROR : Input should consist of triplets.");
      msgBox->exec();
      return false;
    }
    else if(is_2d && list.size()%2!=0){
      QMessageBox *msgBox = new QMessageBox;
      msgBox->setWindowTitle("Error");
      msgBox->setText("ERROR : Input should consist of pairs.");
      msgBox->exec();
      return false;
    }

    polylines.back().reserve(list.size()+(is_closed?1:0));

    for(QString s : list)
    {
      if(!s.isEmpty())
      {
        double res = s.toDouble(&ok);
        if(!ok)
        {
          QMessageBox *msgBox = new QMessageBox;
          msgBox->setWindowTitle("Error");
          msgBox->setText("ERROR : Coordinates are invalid.");
          msgBox->exec();
          return false;
        }
        else
        {
          coord[++counter] = res;
        }
      }
      if(!is_2d && counter == 2)
      {
        Scene_polylines_item::Point_3 p(coord[0], coord[1], coord[2]);
        polylines.back().push_back(p);
        counter=-1;
      }
      else if(is_2d && counter == 1)
      {
        Scene_polylines_item::Point_3 p(coord[0], coord[1], 0);
        polylines.back().push_back(p);
        counter=-1;
      }
    }
    if(is_closed)
    {
      if (polylines.back().back()!=polylines.back().front())
        polylines.back().push_back(polylines.back().front()); //close if not already closed
    }

    return true;
  };

  const bool is_2d = dock_widget->dim2_checkBox->isChecked();
  const bool is_closed = dock_widget->polygon_checkBox->isChecked();
  const bool shall_fill = is_closed && dock_widget->fill_checkBox->isChecked();
  const bool oneperline = dock_widget->oneperline_checkBox->isChecked();

  bool ok = true;
  if (oneperline)
  {
    QStringList poly_list = text.split(QRegularExpression("\\n"), CGAL_QT_SKIP_EMPTY_PARTS);
    if (poly_list.empty()) return;

    for(const QString& qs : poly_list)
    {
      QStringList list = qs.split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
      if (list.isEmpty()) continue;
      polylines.emplace_back();
      ok = read_polyline(list, is_2d, is_closed);
      if (!ok) return;
    }
  }
  else
  {
    QStringList list = text.split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
    if (list.isEmpty()) return;
    polylines.emplace_back();
    ok = read_polyline(list, is_2d, is_closed);
    if (!ok) return;
  }

  if(ok)
  {
    dock_widget->line_textEdit->clear();
    if(shall_fill)
    {
      CGAL::Three::Three::CursorScopeGuard guard(Qt::WaitCursor);
      QApplication::processEvents();

      SMesh* poly = new SMesh;
      for (const auto& polyline : polylines)
      {
        if(polyline.size() < 4) { // no triangle, skip it (needs at least 3 + 1 repeat)
          QMessageBox::warning(mw, "Warning", "Needs at least 3 points to triangulate. Aborting.");
          delete poly;
          return;
        }

        std::vector<Face> patch;
        CGAL::Polygon_mesh_processing::triangulate_hole_polyline(polyline,
                                                                 std::back_inserter(patch),
                                                                 CGAL::parameters::use_delaunay_triangulation(true));

        if(patch.empty()) {
            QMessageBox::warning(mw, "Warning", "Triangulation failed.");
            return;
        }

        CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(polyline,
                                                                    patch,
                                                                    *poly);
      }

      Scene_surface_mesh_item* poly_item = new Scene_surface_mesh_item(poly);
      poly_item->setName(dock_widget->name_lineEdit->text());
      poly_item->setRenderingMode(FlatPlusEdges);
      scene->setSelectedItem(scene->addItem(poly_item));
    }
    else
    {
      Scene_polylines_item* item = new Scene_polylines_item();
      item->polylines = polylines;
      item->invalidateOpenGLBuffers();
      item->setName(dock_widget->name_lineEdit->text());
      item->setColor(Qt::black);
      QStringList polylines_metadata;
      item->setProperty("polylines metadata", polylines_metadata);
      Scene_interface::Item_id id = scene->addItem(item);
      scene->setSelectedItem(id);
    }
  }
}

struct Point_generator
{
  std::size_t w,h;
  Point bl, ur;
  Point_generator(const std::size_t& w,
                  const std::size_t& h,
                  const Point& bl,
                  const Point& ur)
    :w(w), h(h), bl(bl), ur(ur)
  {}
  Point operator()(std::size_t i, std::size_t j) const
  {
    return Point(bl.x() + i*(ur.x()-bl.x())/(w-1),
                 bl.y() + j*(ur.y()-bl.y())/(h-1),
                 bl.z() + j*(ur.z()-bl.z())/(h-1));
  }
};
template<class Facegraph_item>
void Basic_generator_plugin::generateGrid()
{
  typedef typename Facegraph_item::Face_graph Face_graph;
  Face_graph grid;

  QString points_text;
  Point extrema[2];
  using size_type = typename boost::graph_traits<Face_graph>::vertices_size_type;
  size_type nb_cells[2];
  bool triangulated = dock_widget->grid_checkBox->isChecked();
  points_text= dock_widget->grid_lineEdit->text();

  QStringList list = points_text.split(QRegularExpression("\\s+"), CGAL_QT_SKIP_EMPTY_PARTS);
  if (list.isEmpty()) return;
  if (list.size()!=6){
    QMessageBox *msgBox = new QMessageBox;
    msgBox->setWindowTitle("Error");
    msgBox->setText("ERROR : Input should consist of 6 doubles.");
    msgBox->exec();
    return;
  }
  double coords[6];
  for(int j=0; j<6; ++j)
  {
    bool ok;
    coords[j] = list.at(j).toDouble(&ok);
    if(!ok)
    {
      QMessageBox *msgBox = new QMessageBox;
      msgBox->setWindowTitle("Error");
      msgBox->setText("ERROR : Coordinates are invalid.");
      msgBox->exec();
      return;
    }
  }
  extrema[0] = Point(coords[0], coords[1], coords[2]);
  extrema[1] = Point(coords[3], coords[4], coords[5]);
  nb_cells[0] = static_cast<size_type>(dock_widget->gridX_spinBox->value());
  nb_cells[1] = static_cast<size_type>(dock_widget->gridY_spinBox->value());

  //nb_points = nb_cells+1
  Point_generator point_gen(nb_cells[0]+1, nb_cells[1]+1, extrema[0], extrema[1]);

  CGAL::make_grid(nb_cells[0], nb_cells[1], grid, point_gen, triangulated);
  Facegraph_item* grid_item = new Facegraph_item(grid);
  grid_item->setName(dock_widget->name_lineEdit->text());
  Scene_interface::Item_id id = scene->addItem(grid_item);
  scene->setSelectedItem(id);
}
#include "Basic_generator_plugin.moc"
