// Copyright(c) 2019  Foundation for Research and Technology-Hellas (Greece).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v5.4/Voronoi_diagram_2/include/CGAL/draw_voronoi_diagram_2.h $
// $Id: draw_voronoi_diagram_2.h 2bbcabe 2021-11-11T17:23:37+01:00 Guillaume Damiand
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s)     : Jasmeet Singh <jasmeet.singh.mec11@iitbhu.ac.in>

#ifndef CGAL_DRAW_VORONOI_DIAGRAM_2_H
#define CGAL_DRAW_VORONOI_DIAGRAM_2_H

#include <CGAL/Qt/Basic_viewer_qt.h>
#include <CGAL/license/Voronoi_diagram_2.h>

#ifdef CGAL_USE_BASIC_VIEWER

#include <CGAL/Qt/init_ogl_context.h>
#include <CGAL/Random.h>
#include <CGAL/Triangulation_utils_2.h>
#include <CGAL/Voronoi_diagram_2/Face.h>
#include <CGAL/Voronoi_diagram_2/Handle_adaptor.h>
#include <CGAL/Voronoi_diagram_2/Vertex.h>
#include <CGAL/Voronoi_diagram_2/basic.h>
#include <CGAL/Voronoi_diagram_2/Accessor.h>

namespace CGAL {

// Default color functor; user can change it to have its own face color
struct DefaultColorFunctorV2
{
  template <typename V2>
  static CGAL::IO::Color run(const V2 &, const typename V2::Face_iterator /*fh*/) {
    //CGAL::Random random((unsigned int)(std::size_t)(&*fh));
    //return get_random_color(random);
    return CGAL::IO::Color(73, 250, 117);
  }
};

// Viewer for Voronoi diagram
template <class V2, class ColorFunctor>
class SimpleVoronoiDiagram2ViewerQt : public Basic_viewer_qt
{
  typedef Basic_viewer_qt                                       Base;
  typedef typename V2::Vertex_iterator                          Vertex_const_handle;
  typedef typename V2::Delaunay_vertex_handle                   Delaunay_vertex_const_handle;
  typedef typename V2::Delaunay_graph::Finite_vertices_iterator Dual_vertices_iterator;

  typedef typename V2::Halfedge_iterator                        Halfedge_const_handle;
  typedef typename V2::Ccb_halfedge_circulator                  Ccb_halfedge_circulator;
  typedef typename V2::Halfedge_handle                          Halfedge_handle;

  typedef typename V2::Face_iterator                            Face_const_handle;

  typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;

public:
  /// Construct the viewer.
  /// @param av2 the voronoi diagram to view
  /// @param title the title of the window
  /// @param anofaces if true, do not draw faces (faces are not computed; this
  /// can be useful for very big object where this time could be long)
  SimpleVoronoiDiagram2ViewerQt(QWidget *parent, const V2 &av2,
                                const char *title = "Basic Voronoi Viewer",
                                bool anofaces = false,
                                bool draw_voronoi_vertices = true,
                                bool draw_delaunay_vertices = true,
                                const ColorFunctor &fcolor = ColorFunctor())
      : // First draw: vertices; half-edges; faces; multi-color; no inverse
        // normal
        Base(parent, title, true, true, true, false, false, true, true),
        v2(av2), m_nofaces(anofaces),
        m_draw_voronoi_vertices(draw_voronoi_vertices),
        m_draw_dual_vertices(draw_delaunay_vertices), m_fcolor(fcolor) {
    // Add custom key description (see keyPressEvent)
    setKeyDescription(::Qt::Key_R, "Toggles rays display");
    setKeyDescription(::Qt::Key_D, "Toggles dual vertices display");
    setKeyDescription(::Qt::Key_V, "Toggles voronoi vertices display");

    compute_elements();
  }

protected:

  void compute_vertex(Vertex_const_handle vh) { add_point(vh->point()); }

  void compute_dual_vertex(Dual_vertices_iterator vi)
  {
    add_point(vi->point(), CGAL::IO::Color(50, 100, 180));
  }

  void add_segments_and_update_bounding_box(Halfedge_handle he)
  {
    if (he->is_segment()) {
      add_segment(he->source()->point(), he->target()->point());
    } else {
      Delaunay_vertex_const_handle v1 = he->up();
      Delaunay_vertex_const_handle v2 = he->down();

      Kernel::Vector_2 direction(v1->point().y() - v2->point().y(),
                                 v2->point().x() - v1->point().x());
      if (he->is_ray()) {
        Kernel::Point_2 end_point;
        if (he->has_source()) {
          end_point = he->source()->point();
          update_bounding_box_for_ray(end_point, direction);
        }
      } else if (he->is_bisector()) {
        Kernel::Point_2 pointOnLine((v1->point().x() + v2->point().x()) / 2,
                                    (v1->point().y() + v2->point().y()) / 2);
        Kernel::Vector_2 perpendicularDirection(
            v2->point().x() - v1->point().x(),
            v2->point().y() - v1->point().y());
        update_bounding_box_for_line(pointOnLine, direction,
                                     perpendicularDirection);
      }
    }
  }

  Local_kernel::Point_2 get_second_point(Halfedge_handle ray)
  {
    Delaunay_vertex_const_handle v1 = ray->up();
    Delaunay_vertex_const_handle v2 = ray->down();

    // calculate direction of ray and its inverse
    Kernel::Vector_2 v(v1->point().y() - v2->point().y(),
                       v2->point().x() - v1->point().x());
    Local_kernel::Vector_2 inv(1 / v.x(), 1 / v.y());

    // origin of the ray
    Kernel::Point_2 p;
    if (ray->has_source()) {
      p = ray->source()->point();
    } else {
      p = ray->target()->point();
    }

    // get the bounding box of the viewer
    Local_kernel::Vector_2 boundsMin(m_bounding_box.xmin(),
                                     m_bounding_box.zmin());
    Local_kernel::Vector_2 boundsMax(m_bounding_box.xmax(),
                                     m_bounding_box.zmax());
    // calculate intersection
    double txmax, txmin, tymax, tymin;

    if (inv.x() >= 0) {
      txmax = (boundsMax.x() - p.x()) * inv.x();
      txmin = (boundsMin.x() - p.x()) * inv.x();
    } else {
      txmax = (boundsMin.x() - p.x()) * inv.x();
      txmin = (boundsMax.x() - p.x()) * inv.x();
    }

    if (inv.y() >= 0) {
      tymax = (boundsMax.y() - p.y()) * inv.y();
      tymin = (boundsMin.y() - p.y()) * inv.y();
    } else {
      tymax = (boundsMin.y() - p.y()) * inv.y();
      tymin = (boundsMax.y() - p.y()) * inv.y();
    }

    if (tymin > txmin)
      txmin = tymin;
    if (tymax < txmax)
      txmax = tymax;

    Local_kernel::Point_2 p1;
    if (v.x() == 0) {
      p1 = Local_kernel::Point_2(p.x(), p.y() + tymax * v.y());
    } else if (v.y() == 0) {
      p1 = Local_kernel::Point_2(p.x() + txmax * v.x(), p.y());
    } else {
      p1 = Local_kernel::Point_2(p.x() + txmax * v.x(), p.y() + tymax * v.y());
    }
    return p1;
  }

  void compute_rays_and_bisectors(Halfedge_const_handle he)
  {
    Delaunay_vertex_const_handle v1 = he->up();
    Delaunay_vertex_const_handle v2 = he->down();

    Kernel::Vector_2 direction(v1->point().y() - v2->point().y(),
                               v2->point().x() - v1->point().x());
    if (he->is_ray()) {
      if (he->has_source()) {
        // add_ray_segment(he->source()->point(), get_second_point(he));
        add_ray(he->source()->point(), direction, CGAL::IO::Color(100, 0, 0));
      }
    } else if (he->is_bisector()) {
      Kernel::Point_2 pointOnLine((v1->point().x() + v2->point().x()) / 2,
                                  (v1->point().y() + v2->point().y()) / 2);
      add_line(pointOnLine, direction);
    }
  }

  void compute_face(Face_const_handle fh)
  {
    CGAL::IO::Color c = m_fcolor.run(v2, fh);

    Ccb_halfedge_circulator ec_start = fh->ccb();
    Ccb_halfedge_circulator ec = ec_start;

    if (!fh->is_unbounded()) {
      face_begin(c);
      do {
        add_point_in_face(ec->source()->point());
      } while (++ec != ec_start);
      face_end();
    }
    // Test: for unbounded faces
    //      else {
    //            do{
    //                if( ec->has_source() ){
    //                    add_point_in_face(ec->source()->point());
    //                }
    //                else{
    //                    add_point_in_face(get_second_point(ec->twin()));
    //                }
    //            } while(++ec != ec_start);
    //        }
  }

  void compute_elements()
  {
    clear();

    // Draw the voronoi vertices
    if (m_draw_voronoi_vertices) {
      for (typename V2::Vertex_iterator it = v2.vertices_begin();
           it != v2.vertices_end(); ++it) {
        compute_vertex(it);
      }
    }

    // Draw the dual vertices
    if (m_draw_dual_vertices) {
      for (Dual_vertices_iterator it = v2.dual().finite_vertices_begin();
           it != v2.dual().finite_vertices_end(); ++it) {
        compute_dual_vertex(it);
      }
    }

    // Add segments and update bounding box
    for (typename V2::Halfedge_iterator it = v2.halfedges_begin();
         it != v2.halfedges_end(); ++it) {
      add_segments_and_update_bounding_box(it);
    }

    for (typename V2::Halfedge_iterator it = v2.halfedges_begin();
         it != v2.halfedges_end(); ++it) {
      compute_rays_and_bisectors(it);
    }

    if (!m_nofaces) {
      for (typename V2::Face_iterator it = v2.faces_begin();
           it != v2.faces_end(); ++it) {
        compute_face(it);
      }
    }
  }

  virtual void keyPressEvent(QKeyEvent *e)
  {
    /// [Keypress]
    const ::Qt::KeyboardModifiers modifiers = e->modifiers();
    if ((e->key() == ::Qt::Key_R) && (modifiers == ::Qt::NoButton)) {
      m_draw_rays = !m_draw_rays;
      displayMessage(
          QString("Draw rays=%1.").arg(m_draw_rays ? "true" : "false"));
      update();
    } else if ((e->key() == ::Qt::Key_V) && (modifiers == ::Qt::NoButton)) {
      m_draw_voronoi_vertices = !m_draw_voronoi_vertices;
      displayMessage(
          QString("Voronoi vertices=%1.").arg(m_draw_voronoi_vertices? "true" : "false"));
      compute_elements();
      redraw();
    } else if ((e->key() == ::Qt::Key_D) && (modifiers == ::Qt::NoButton)) {
      m_draw_dual_vertices = !m_draw_dual_vertices;
      displayMessage(QString("Dual vertices=%1.")
                         .arg(m_draw_dual_vertices ? "true" : "false"));
      compute_elements();
      redraw();
    } else {
      // Call the base method to process others/classicals key
      Base::keyPressEvent(e);
    }
    /// [Keypress]
  }

protected:
  const V2 &v2;
  bool m_nofaces;
  bool m_draw_voronoi_vertices;
  bool m_draw_dual_vertices;
  const ColorFunctor &m_fcolor;
};

// Specialization of draw function.
#define CGAL_VORONOI_TYPE CGAL::Voronoi_diagram_2 <DG, AT, AP>

template<class DG,
         class AT,
         class AP>
void draw(const CGAL_VORONOI_TYPE &av2,
          const char *title="2D Voronoi Diagram Basic Viewer",
          bool nofill = false,
          bool draw_voronoi_vertices = true,
          bool draw_dual_vertices = true)
{
#if defined(CGAL_TEST_SUITE)
  bool cgal_test_suite = true;
#else
  bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE");
#endif

  if (!cgal_test_suite) {
    CGAL::Qt::init_ogl_context(4,3);
    int argc = 1;
    const char *argv[2] = {"voronoi_2_viewer", nullptr};
    QApplication app(argc, const_cast<char **>(argv));
    DefaultColorFunctorV2 fcolor;
    SimpleVoronoiDiagram2ViewerQt<CGAL_VORONOI_TYPE, DefaultColorFunctorV2>
        mainwindow(app.activeWindow(), av2, title, nofill,
                   draw_voronoi_vertices, draw_dual_vertices, fcolor);
    mainwindow.show();
    app.exec();
  }
}

} // End namespace CGAL

#endif // CGAL_USE_BASIC_VIEWER

#endif // CGAL_DRAW_VORONOI_DIAGRAM_2_H
