// Copyright (c) 2008  Tel-Aviv University (Israel).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.0.1/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_polygon_validation.h $
// $Id: include/CGAL/Boolean_set_operations_2/Gps_polygon_validation.h 50cfbde3b84 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s): Baruch Zukerman <baruchzu@post.tau.ac.il>
//                 Ron Wein        <wein@post.tau.ac.il>
//                 Boris Kozorovitzky <boriskoz@post.tau.ac.il>
//                 Guy Zucker <guyzucke@post.tau.ac.il>

#ifndef CGAL_BSO_2_GPS_POLYGON_VALIDATION_2_H
#define CGAL_BSO_2_GPS_POLYGON_VALIDATION_2_H

#include <CGAL/license/Boolean_set_operations_2.h>

#include <CGAL/Boolean_set_operations_2/Gps_traits_adaptor.h>
#include <CGAL/Boolean_set_operations_2/Gps_default_dcel.h>
#include <CGAL/Boolean_set_operations_2/Gps_on_surface_base_2.h>
#include <CGAL/Arrangement_2/Arr_default_planar_topology.h>
#include <CGAL/Surface_sweep_2.h>
#include <CGAL/Surface_sweep_2/Default_visitor.h>
#include <CGAL/Arr_default_overlay_traits.h>
#include <CGAL/Arr_naive_point_location.h>

#include <iostream>
#include <list>
#include <iterator>

namespace CGAL {

namespace Ss2 = Surface_sweep_2;

template <typename Arrangement_2>
class Validation_overlay_traits :
  public CGAL::Arr_default_overlay_traits<Arrangement_2>
{
public:
  typedef CGAL::Arr_default_overlay_traits<Arrangement_2>       Base;
  typedef typename Base::Face_handle_A                          Face_handle_A;
  typedef typename Base::Face_handle_B                          Face_handle_B;
  typedef typename Base::Face_handle_R                          Face_handle_R;

  typedef typename Arrangement_2::Ccb_halfedge_const_circulator
    Ccb_halfedge_const_circulator;
  typedef typename Arrangement_2::Halfedge_const_handle
    Halfedge_const_handle;
  typedef typename Arrangement_2::Face_const_handle
    Face_const_handle;
  typedef typename Arrangement_2::Inner_ccb_const_iterator
    Inner_ccb_const_iterator;

  /* red faces source is the arrangement of holes. The blue faces (face) are
   * caused by the PWH's outer boundary
   */
  virtual void create_face(Face_handle_A red_face, Face_handle_B blue_face,
                           Face_handle_R /*r_face*/) const
  {
    if ((red_face->contained()==true) && (blue_face->contained()==false)) {
      hole_overlap = true;
    }
  }

public:
  Validation_overlay_traits() : hole_overlap(false) {}

  bool getHoleOverlap() { return hole_overlap; }

  void setHoleOverlap(bool b) { hole_overlap = b; }

private:
  mutable bool hole_overlap;
};

/*! \class
 * A visitor used for checking whether the edges of a polygon are
 * non-intersecting.
 */
template <typename GeometryTraits_2, typename Allocator_ = CGAL_ALLOCATOR(int)>
class Gps_polygon_validation_visitor :
  public Ss2::Default_visitor<Gps_polygon_validation_visitor<GeometryTraits_2,
                                                             Allocator_>,
                              GeometryTraits_2, Allocator_>
{
public:
  typedef GeometryTraits_2                              Geometry_traits_2;
  typedef Allocator_                                    Allocator;

private:
  typedef Geometry_traits_2                             Gt2;
  typedef Gps_polygon_validation_visitor<Gt2, Allocator>
                                                        Self;
  typedef Ss2::Default_visitor<Self, Gt2, Allocator>    Base;

public:
  typedef typename Gt2::X_monotone_curve_2              X_monotone_curve_2;
  typedef typename Gt2::Point_2                         Point_2;

  typedef typename Base::Event                          Event;
  typedef typename Base::Subcurve                       Subcurve;
  typedef typename Base::Status_line_iterator           SL_iterator;

  enum Error_code {
    ERROR_NONE = 0,
    ERROR_EDGE_INTERSECTION,
    ERROR_EDGE_VERTEX_INTERSECTION,
    ERROR_EDGE_OVERLAP,
    ERROR_VERTEX_INTERSECTION
  };

  Gps_polygon_validation_visitor(bool is_s_simple = true) :
    m_is_valid(true),
    m_is_s_simple(is_s_simple),
    m_error_code(ERROR_NONE)
  {}

  template <class XCurveIterator>
  void sweep(XCurveIterator begin, XCurveIterator end)
  {
    // Perform the sweep
    this->m_surface_sweep->sweep(begin, end);
  }

  bool after_handle_event(Event* event, SL_iterator, bool)
  {
    if (event->is_intersection()) {
      m_error_code = ERROR_EDGE_INTERSECTION;
      m_is_valid = false;
      this->m_surface_sweep->stop_sweep();
    }
    else if (event->is_weak_intersection()) {
      m_error_code = ERROR_EDGE_VERTEX_INTERSECTION;
      m_is_valid = false;
      this->m_surface_sweep->stop_sweep();
    }
    else if (event->is_overlap()) {
      m_error_code = ERROR_EDGE_OVERLAP;
      m_is_valid = false;
      this->m_surface_sweep->stop_sweep();
    }
    else {
      if (m_is_s_simple &&
          (event->number_of_right_curves() + event->number_of_left_curves()) !=
          2)
      {
        m_error_code = ERROR_VERTEX_INTERSECTION;
        m_is_valid = false;
        this->m_surface_sweep->stop_sweep();
      }
    }
    return true;
  }

  bool is_valid() const { return m_is_valid; }
  Error_code error_code() const { return m_error_code; }

protected:
  bool m_is_valid;
  bool m_is_s_simple; // is strictly simple

private:
  Error_code m_error_code;
};

//Traits_2 templates the General_polygon_set_2 Traits.
//These include types for polygon and PWH.
template <typename Traits_2>
bool is_closed_polygon(const typename Traits_2::Polygon_2& pgn,
                       const Traits_2& traits)
{
  typedef Gps_traits_adaptor<Traits_2>                  Traits_adapter_2;
  typedef typename Traits_2::Curve_const_iterator       Curve_const_iterator;
  typedef std::pair<Curve_const_iterator,Curve_const_iterator>    Cci_pair;
  typedef typename Traits_adapter_2::Construct_vertex_2 Construct_vertex_2;

  Cci_pair itr_pair = traits.construct_curves_2_object()(pgn);
  Curve_const_iterator begin = itr_pair.first;
  Curve_const_iterator end = itr_pair.second;

  if (begin == end) return true;  // An empty polygon is valid.

  Traits_adapter_2 traits_adapter(traits);
  typename Traits_2::Equal_2 equal_func = traits.equal_2_object();
  Curve_const_iterator curr, next;
  Construct_vertex_2 construct_vertex_func =
    traits_adapter.construct_vertex_2_object();
  curr = next = begin;
  ++next;

  if (next == end)
    return false; // A polygon cannot have just a single edge.

  while (next != end) {
    // Make sure that the current target equals the next source.
    if (equal_func(construct_vertex_func(*curr, 0),
                   construct_vertex_func(*curr, 1)))
      return false;

    if (! equal_func(construct_vertex_func(*curr, 1),
                     construct_vertex_func(*next, 0)))
      return false;

    // Move to the next pair of edges.
    curr = next;
    ++next;
  }

  // Make sure that the last target equals the first source.
  if (equal_func(construct_vertex_func (*curr, 0),
                 construct_vertex_func (*curr, 1)))
    return false;

  if (! equal_func(construct_vertex_func (*curr, 1),
                   construct_vertex_func (*begin, 0)))
    return false;

  return true;
}

// Previously known as is_strictly_simple
template <typename Traits_2>
bool is_simple_polygon(const typename Traits_2::Polygon_2& pgn,
                       const Traits_2& traits)
{
  typedef typename Traits_2::Curve_const_iterator       Curve_const_iterator;
  typedef std::pair<Curve_const_iterator,Curve_const_iterator>
                                                        Cci_pair;

  // Sweep the boundary curves and look for intersections.
  typedef Gps_polygon_validation_visitor<Traits_2>      Visitor;
  typedef Ss2::Surface_sweep_2<Visitor>                 Surface_sweep;

  Cci_pair itr_pair = traits.construct_curves_2_object()(pgn);
  Visitor visitor;
  Surface_sweep surface_sweep(&traits, &visitor);

  visitor.sweep(itr_pair.first, itr_pair.second);
  if (!visitor.is_valid()) {
    switch (visitor.error_code()) {
     case Visitor::ERROR_NONE: break;
     case Visitor::ERROR_EDGE_INTERSECTION:
      CGAL_warning_msg(false, "The polygon boundary self intersects at edges.");
      break;

     case Visitor::ERROR_EDGE_VERTEX_INTERSECTION:
      CGAL_warning_msg(false, "The polygon boundary self (weakly) intersects.");
      break;

     case Visitor::ERROR_EDGE_OVERLAP:
      CGAL_warning_msg(false, "The polygon boundary self overlaps.");
      break;

     case Visitor::ERROR_VERTEX_INTERSECTION:
      CGAL_warning_msg(false, "The polygon boundary intersects at vertices.");
      break;
    }
    return false;
  }
  return true;
}

template <typename Traits_2>
bool has_valid_orientation_polygon(const typename Traits_2::Polygon_2& pgn,
                                   const Traits_2& traits)
{

  typedef Gps_traits_adaptor<Traits_2>              Traits_adapter_2;
  typedef typename Traits_2::Curve_const_iterator    Curve_const_iterator;
  typedef std::pair<Curve_const_iterator,Curve_const_iterator>    Cci_pair;

  Cci_pair         itr_pair = traits.construct_curves_2_object()(pgn);
  Traits_adapter_2 traits_adapter(traits);

  if(itr_pair.first == itr_pair.second)
    return true; // empty polygon

  return
    (traits_adapter.orientation_2_object()(itr_pair.first, itr_pair.second) ==
     COUNTERCLOCKWISE);
}

/* A valid polygon is :
 * 1 - Closed or empty polygon
 * 2 - Simple (previously known as strictly simple)
 * 3 - Counterclockwise oriented
 */
template <typename Traits_2>
bool is_valid_polygon(const typename Traits_2::Polygon_2& pgn,
                      const Traits_2& traits)
{
  bool closed = is_closed_polygon(pgn,traits);
  CGAL_warning_msg (closed, "The polygon's boundary is not closed.");
  if (! closed)
    return false;

  bool simple = is_simple_polygon(pgn,traits);
  CGAL_warning_msg (simple, "The polygon is not simple.");
  if (!simple)
    return false;

  bool valid_orientation = has_valid_orientation_polygon(pgn,traits);
  CGAL_warning_msg (valid_orientation,
                    "The polygon has a wrong orientation.");
  if (! valid_orientation)
    return false;

  return true;
}

template <typename Traits_2>
bool
is_closed_polygon_with_holes(const typename Traits_2::Polygon_with_holes_2& pgn,
                             const Traits_2& traits)
{
  typedef typename Traits_2::Polygon_with_holes_2       Polygon_with_holes_2;
  if (! is_closed_polygon(pgn.outer_boundary(),traits)) return false;

  typename Polygon_with_holes_2::Hole_const_iterator    itr;
  for (itr = pgn.holes_begin(); itr != pgn.holes_end(); ++itr) {
    if (! is_closed_polygon(*itr,traits)) return false;
  }
  return true;
}

// templated point location version
template <typename Traits_2, typename PointLocation>
bool
is_crossover_outer_boundary(const typename Traits_2::Polygon_with_holes_2& pgn,
                            const Traits_2& traits, PointLocation& pl)
{
  typedef typename Traits_2::Curve_const_iterator    Curve_const_iterator;
  typedef std::pair<Curve_const_iterator,Curve_const_iterator>    Cci_pair;

  typedef typename Traits_2::Point_2                 Point_2;
  typedef typename Traits_2::Compare_endpoints_xy_2  Compare_endpoints_xy_2;
  typedef typename Traits_2::Construct_min_vertex_2  Construct_min_vertex_2;
  typedef typename Traits_2::Construct_max_vertex_2  Construct_max_vertex_2;
  typedef CGAL::Gps_default_dcel<Traits_2>           Dcel;

  // IMPORTATNT! TODO!
  // Currently the topology traits is the bounded planar traits. This
  // should be replaced with a templated topology traits!
  typedef typename Default_planar_topology<Traits_2, Dcel>::Traits
                                                     Topology_traits;
  typedef CGAL::Gps_on_surface_base_2<Traits_2, Topology_traits>
    Polygon_set_2;

  typedef typename Polygon_set_2::Arrangement_on_surface_2 Arrangement_2;
  typedef typename Arrangement_2::Halfedge_handle          Halfedge_handle;
  typedef typename Arrangement_2::Vertex_handle            Vertex_handle;
  typedef typename Arrangement_2::Vertex_const_handle      Vertex_const_handle;
  typedef typename Traits_2::Curve_const_iterator          Curve_const_iterator;

  typename std::list<Halfedge_handle>           he_path;
  typename std::list<Halfedge_handle>::iterator he_itr;
  //functors used throughout the function
  Construct_min_vertex_2 min_functor = traits.construct_min_vertex_2_object();
  Construct_max_vertex_2 max_functor = traits.construct_max_vertex_2_object();
  Compare_endpoints_xy_2 cmp_endpoints =  traits.compare_endpoints_xy_2_object();

  Cci_pair itr_pair = traits.construct_curves_2_object()(pgn.outer_boundary());
  Curve_const_iterator  begin = itr_pair.first;
  Curve_const_iterator  end = itr_pair.second;
  if (begin == end) return true;  // An empty polygon is valid.
  // handles to consecutive curves
  Curve_const_iterator curr, next;
  curr = next = begin;
  // handles to vertices for insert. one maintains the current curve (already
  // inserted) and next curve's joint vertex.
  // the other maintains the next curve's second vertex if it already exists in
  // the arrangement.
  Vertex_handle joint_ver, second_ver;
  // closed check guarantees polygon has more than 1 curve
  ++next;
  // halfedge handle whose target is always the joint vertex between next and
  // curr.
  Halfedge_handle last_he;

  Polygon_set_2 gps(traits);
  Arrangement_2& arr = gps.arrangement();
  pl.attach(arr);

  // insert first edge lexicographically to arrangement
  // compute the joint vertex and insert to the path list a halfedge whose
  // target is the joint vertex
  last_he = CGAL::insert_non_intersecting_curve(arr, *curr);
  if  (cmp_endpoints(*curr) == SMALLER) {
    // polygon's boundary first curve is in lexicographic direction
    joint_ver = last_he->target();
    he_path.push_back(last_he);
  }
  else { // polygon's boundary first curve not lexicographic
    joint_ver = last_he->source();
    he_path.push_back(last_he->twin());
  }

  /* insert the rest of the curves to the arrangement efficiently the previous
   * closed polygon check guarantees equal_func
   * (construct_vertex_func (*curr, 1), construct_vertex_func (*next, 0)))
   */
  while (next != end) {
    CGAL::Object obj;
    Vertex_const_handle cver;
    Point_2 second_point;
    if (cmp_endpoints(*next) == SMALLER) {
      // next curve's minimum is the joint vertex. Look if it's max exists in
      // the arrangement and insert lexicographically
      second_point = max_functor(*next);
      obj = pl.locate(second_point);
      if (CGAL::assign (cver, obj)) {
        // insert where both vertices exist
        second_ver = arr.non_const_handle(cver);
        last_he = arr.insert_at_vertices( *next, joint_ver, second_ver);
      }
      else // insert from left vertex
        last_he = arr.insert_from_left_vertex ( *next,joint_ver) ;
    } else {
      // next curve's maximum vertex is the joint vertex. try to locate the
      // min vertex, and insert from right or from both vertices
      second_point = min_functor(*next);
      obj = pl.locate(second_point);
      if (CGAL::assign (cver, obj))  {
        // insert where both vertices exist
        second_ver = arr.non_const_handle(cver);
        last_he = arr.insert_at_vertices( *next, joint_ver, second_ver);
      }
      else  // insert from right vertex
        last_he = arr.insert_from_right_vertex ( *next,joint_ver) ;
    }
    // Move to the next pair of edges.
    he_path.push_back(last_he);
    joint_ver=last_he->target();
    curr = next;
    ++next;
  } //end of while

  /* We created a path of halfedges that circulates the polygon
   * counterclockwise. The polygon should lay on the left of each of these
   * half edges. If the boundary is invalid, the unbounded face should be
   * on the left of one of more than one of the halfedges.
   * The unbounded face is always to the right of the halfedges. We check if
   * all faces that lay on the right of the halfedges are equal (to the
   *"unbounded" face).
   */
  typename Arrangement_2::Face_handle fh = (*he_path.begin())->twin()->face();
  for (he_itr = he_path.begin(); he_itr != he_path.end(); he_itr++) {
    if ((*he_itr)->twin()->face() != fh) return false;
  }
  return true;
}

template<typename Traits_2>
bool
is_crossover_outer_boundary(const typename Traits_2::Polygon_with_holes_2& pgn,
                            const Traits_2& traits)
{
  typedef CGAL::Gps_default_dcel<Traits_2>                      Dcel;
  // IMPORTATNT! TODO!
  // Currently the topology traits is the bounded planar traits. This
  // should be replaced with a templated topology traits!
  typedef typename Default_planar_topology<Traits_2, Dcel>::Traits
                                                                Topology_traits;

  typedef CGAL::Gps_on_surface_base_2<Traits_2, Topology_traits>
                                                                Polygon_set_2;
  typedef typename Polygon_set_2::Arrangement_on_surface_2      Arrangement_2;
  typedef CGAL::Arr_naive_point_location<Arrangement_2>         Naive_pl;

  Naive_pl pl;
  return is_crossover_outer_boundary(pgn, traits, pl);
}

template <typename Traits_2>
bool is_relatively_simple_polygon
(const typename Traits_2::Polygon_2& pgn, const Traits_2& traits)
{
  typedef typename Traits_2::Curve_const_iterator       Curve_const_iterator;
  typedef std::pair<Curve_const_iterator,Curve_const_iterator>
                                                        Cci_pair;
  typedef typename Traits_2::Construct_curves_2         Construct_curves_2;

  typedef typename Traits_2::X_monotone_curve_2         X_monotone_curve_2;
  typedef Gps_polygon_validation_visitor<Traits_2>      Visitor;
  typedef Ss2::Surface_sweep_2<Visitor>                 Surface_sweep;

  Construct_curves_2 construct_curves_func = traits.construct_curves_2_object();
  // Construct a container of all outer boundary curves.
  Cci_pair itr_pair = construct_curves_func (pgn);
  std::list<X_monotone_curve_2>  outer_curves;
  std::copy(itr_pair.first, itr_pair.second, std::back_inserter(outer_curves));
  // Create visitor and sweep to verify outer boundary is relatively simple
  Visitor relative_visitor(false);
  Surface_sweep surface_sweep(&traits, &relative_visitor);
  relative_visitor.sweep(outer_curves.begin(), outer_curves.end());
  if (!relative_visitor.is_valid()) {
    switch (relative_visitor.error_code()) {
     case Visitor::ERROR_NONE: break;
     case Visitor::ERROR_EDGE_INTERSECTION:
      CGAL_warning_msg(false, "Polygon self intersects at edges.");
      break;

     case Visitor::ERROR_EDGE_VERTEX_INTERSECTION:
      CGAL_warning_msg(false, "Polygon self (weakly) intersects.");
      break;

     case Visitor::ERROR_EDGE_OVERLAP:
      CGAL_warning_msg(false, "Polygon self overlaps.");
      break;

     case Visitor::ERROR_VERTEX_INTERSECTION:
      CGAL_warning_msg(false, "Polygon self intersects at vertices.");
      break;
    }
    return false;
  }

  return true;
}

// previously known as Simple
template <typename Traits_2>
bool is_relatively_simple_polygon_with_holes
(const typename Traits_2::Polygon_with_holes_2& pgn, const Traits_2& traits)
{
  bool outer_is_relatively_simple = is_relatively_simple_polygon(pgn.outer_boundary(), traits);
  if (!outer_is_relatively_simple)
  {
    CGAL_warning_msg(false, "Outer boundary is not relatively simple.");
    return false;
  }

  // Verify every hole is simple
  typedef typename Traits_2::X_monotone_curve_2         X_monotone_curve_2;
  typedef typename Traits_2::Polygon_with_holes_2       Polygon_with_holes_2;
  typename Polygon_with_holes_2::Hole_const_iterator  hoit;
  std::list<X_monotone_curve_2>  hole_curves;
  for (hoit = pgn.holes_begin(); hoit != pgn.holes_end(); ++hoit) {
    bool relatively_simple_hole = is_relatively_simple_polygon(*hoit, traits);
    if (!relatively_simple_hole)
    {
      CGAL_warning_msg(false, "A hole is not relatively simple.");
      return false;
    }
  }
  return true;
}

template <typename Traits_2>
bool has_valid_orientation_polygon_with_holes
(const typename Traits_2::Polygon_with_holes_2& pgn, const Traits_2& traits)
{
  typedef Gps_traits_adaptor<Traits_2>                  Traits_adapter_2;
  typedef typename Traits_2::Curve_const_iterator       Curve_const_iterator;
  typedef std::pair<Curve_const_iterator,Curve_const_iterator>
                                                        Cci_pair;
  typedef typename Traits_2::Construct_curves_2         Construct_curves_2;

  typedef typename Traits_adapter_2::Orientation_2      Check_orientation_2;
  typedef typename Traits_2::Polygon_with_holes_2       Polygon_with_holes_2;

  Traits_adapter_2 traits_adapter(traits);

  Construct_curves_2 construct_curves_func = traits.construct_curves_2_object();
  Check_orientation_2 check_orientation_func =
    traits_adapter.orientation_2_object();
  // Check the orientation of the outer boundary.
  Cci_pair itr_pair = construct_curves_func(pgn.outer_boundary());

  if ((itr_pair.first != itr_pair.second) &&
      (check_orientation_func(itr_pair.first, itr_pair.second) !=
       COUNTERCLOCKWISE))
  {
    return false;
  }

  // Check the orientation of each of the holes.
  typename Polygon_with_holes_2::Hole_const_iterator    hoit;

  for (hoit = pgn.holes_begin(); hoit != pgn.holes_end(); ++hoit) {
    itr_pair = construct_curves_func(*hoit);

    if ((itr_pair.first != itr_pair.second) &&
        (check_orientation_func(itr_pair.first, itr_pair.second) != CLOCKWISE))
    {
      return false;
    }
  }
  return true;
}

/* Verify holes do not intersect between themselves as well with the outer
 * boundary (except intersection on a vertex which is allowed).
 *
 * This efficient implementation utilizes the general poygon set for aggregated
 * join operations for N holes which should result in a GPS that contains N
 * independent PWH.
 * Executing a difference(gps, outer boundary) should result in an empty set if
 * no holes intersect the boundary.
 *
 * An iterative use of the difference free function while iterating over the
 * holes may have an advantage in case there are numerous holes that intersect
 * the boundary and the iterative loop will be stopped after a small number of
 * iterations.
 */
template <typename Traits_2>
bool are_holes_and_boundary_pairwise_disjoint
(const typename Traits_2::Polygon_with_holes_2& pwh, Traits_2& traits)
{
  typedef typename Traits_2::Curve_const_iterator       Curve_const_iterator;
  typedef std::pair<Curve_const_iterator, Curve_const_iterator>
                                                        Cci_pair;
  typedef typename Traits_2::Construct_curves_2         Construct_curves_2;
  typedef CGAL::Gps_default_dcel<Traits_2>              Dcel;
  // IMPORTATNT! TODO!
  // Currently the topology traits is the bounded planar traits. This
  // should be replaced with a templated topology traits!
  typedef typename Default_planar_topology<Traits_2, Dcel>::Traits
                                                        Topology_traits;
  typedef CGAL::Gps_on_surface_base_2<Traits_2, Topology_traits>
                                                        Polygon_set_2;
  typedef typename Polygon_set_2::Size                  Size;
  typedef  typename Traits_2::Polygon_2                 Polygon_2;
  typedef typename Traits_2::Polygon_with_holes_2       Polygon_with_holes_2;
  typedef typename Polygon_with_holes_2::Hole_const_iterator
                                                        Hole_const_iterator;
  typedef typename Traits_2::X_monotone_curve_2         X_monotone_curve_2;
  typedef std::pair<Curve_const_iterator, Curve_const_iterator>
                                                        Cci_pair;
  typedef typename Traits_2::Construct_curves_2         Construct_curves_2;
  typedef typename Traits_2::Construct_polygon_with_holes_2
    Construct_polygon_with_holes_2;

  typedef Gps_polygon_validation_visitor<Traits_2>      Visitor;
  typedef Ss2::Surface_sweep_2<Visitor>                 Surface_sweep;
  typedef typename Polygon_set_2::Arrangement_on_surface_2
                                                        Arrangement_2;

  /* Should be perfored more efficeintly  than using sweep and than
   * difference().
   *
   * Use sweep to find intersections on the interior of curves (not on vertices)
   * and overlapping edges which are not allowed (note that 0/1 dimension
   * intersections are not detects by do_intersect() which only returns the
   * 2D intersection polygon if exists)
   * Note that using this sweep alone allows for a hole and an edge to share
   * a vertex and intersect (like illegal input pgn_w_overlap_hole.dat in
   * validation_example)
   */
  Hole_const_iterator hoit;
  // Construct a container of all boundary curves.
  Polygon_2 pgn2 = traits.construct_outer_boundary_object()(pwh);
  Construct_curves_2 construct_curves_func;
  Cci_pair itr_pair = construct_curves_func(pgn2);

  std::list<X_monotone_curve_2>  curves;
  std::copy(itr_pair.first, itr_pair.second, std::back_inserter(curves));

  std::pair<Hole_const_iterator, Hole_const_iterator> pair =
    traits.construct_holes_object()(pwh);
  //for (hoit = pgn.holes_begin(); hoit != pgn.holes_end(); ++hoit)
  for (hoit = pair.first; hoit!=pair.second; ++hoit) {
    itr_pair = construct_curves_func (*hoit);
    std::copy (itr_pair.first, itr_pair.second, std::back_inserter(curves));
  }

  // Perform the sweep and check for curve  intersections on the interior.
  // Traits_2     traits; moved to top, needed also for boundary.
  Visitor visitor(false);
  Surface_sweep surface_sweep(&traits, &visitor);
  visitor.sweep(curves.begin(), curves.end());
  if (!visitor.is_valid()) return false;

  Polygon_set_2 gps(traits);
  // check for 2D  intersections of holes (holes must be disjoint except for
  // vertices)
  Size num_of_holes = 0;
  // functors for creating a pwh needed for inserting pgns into the arrangement
  // quickly
  Construct_polygon_with_holes_2 construct_pwh_functor =
    traits.construct_polygon_with_holes_2_object();
  for (hoit = pwh.holes_begin(); hoit != pwh.holes_end(); ++hoit) {
    Polygon_2 hole(*hoit);
    hole.reverse_orientation();
    /* gps.join() and gps.insert()requires that the polyon insrted is valid,
     * and therefore the hole orientation must be reversed
     */
    bool intersect = gps.do_intersect(hole);
    if (intersect) return false;
    else {
      /* to use gps.insert(hole) it is required that the set components and the
       * new holes  do not intersect.
       * because the sweep detects shared edges and the do_intersect query
       * detects 2D intersections we can safely use the insert(pwh) function
       * whose performance is better than the join(pgn)
       */
      Polygon_with_holes_2 empty_pwh = construct_pwh_functor(hole);
      // traits.Construct_polygon_with_holes_2 (hole);
      // Polygon_with_holes_2 empty_pwh(hole);
      gps.insert(empty_pwh);
      num_of_holes++;
    }
  }
  /* not good - doesn't work if intersection at vertices is legal.
   * Size arr_num_of_holes = gps.number_of_polygons_with_holes();
   * if (num_of_holes != arr_num_of_holes)
   *   return false;
   */

  // check for intersection of holes with the outer boundary

  /* outer boundary can be relatively simple. Execution of
   * do_intersect(hole, boundary) or difference(hole,boundary) relies on
   * implementation of General polygon set which has a precondition that
   * requires valid polygon or PWH to be inserted (not just a simple polygon).
   * This helper function is utilized after checking for the PWH closure,
   * relative simplicity and orientation. Therefore it is safe to assume the
   * outer boundary is  valid PWH with no holes. We can't assume it is a valid
   * (simple) polygon.
   */

  // Polygon_with_holes_2 boundary(pwh.outer_boundary(), fit, fit);
  Polygon_with_holes_2 boundary = construct_pwh_functor(pwh.outer_boundary());
  // Unbounded outer boundaries contain all the holes and the holes were checked
  // and are OK.
  if (boundary.is_unbounded()) return true;

  /* do_intersect predicate will not suffice as hole can be completely outside
   * the outer boundary in an (extremely strange) case
   * The gps now contains all the holes. the difference between the boundary
   * and a union of all the holes should be the empty set. For performance
   * reasons, we use a customized overlay traits and perform an arrangement
   * overlay instead of difference
   */
  Validation_overlay_traits<Arrangement_2> valOverlayTraits;
  valOverlayTraits.setHoleOverlap(false);
  Polygon_set_2 gps2(traits);

  Arrangement_2& boundary_arr = gps2.arrangement();
  gps2._insert(boundary,boundary_arr);
  Arrangement_2& holes_arr = gps.arrangement();
  Arrangement_2 output_arr(holes_arr.geometry_traits());
  overlay(holes_arr, boundary_arr, output_arr, valOverlayTraits);
  if (valOverlayTraits.getHoleOverlap()) return false;

  /* old code that works less efficiently than the new overly traits
   * gps.validation_difference(boundary);
   * if gps is not empty at least one hole intersected the boundary
   * if (!gps.is_empty())
   *   return false;
   */
  return true;
}

/* A valid polygon with holes is :
 * 1 - Has empty or closed boundary and all the holes are closed
 * 2 - The PWH is relatively simple polygon (holes are simple...)
 * 3 - Has it's boundary oriented counterclockwise and the holes oriented
 *     clockwise
 * 4 - All the segments (boundary and holes) do not cross or intersect in their
 *     relative interior
 * 5 - The holes are on the interior of the boundary polygon if the boundary
 *     is not empty
 */
template <typename Traits_2>
bool
is_valid_polygon_with_holes(const typename Traits_2::Polygon_with_holes_2& pgn,
                            const Traits_2& traits)
{
  bool closed = is_closed_polygon_with_holes(pgn, traits);
  CGAL_warning_msg(closed,
                   "The polygon's boundary or one of its holes is not closed.");
  if (! closed) return false;

  bool relatively_simple = is_relatively_simple_polygon_with_holes(pgn, traits);
  CGAL_warning_msg (relatively_simple, "The polygon is not relatively simple.");
  if (! relatively_simple) return false;

  bool no_cross = is_crossover_outer_boundary(pgn, traits);
  CGAL_warning_msg (no_cross, "The polygon has a crossover.");
  if (!no_cross) return false;

  bool valid_orientation = has_valid_orientation_polygon_with_holes(pgn, traits);
  CGAL_warning_msg (valid_orientation, "The polygon has a wrong orientation.");
  if (! valid_orientation) return false;

  bool holes_disjoint = are_holes_and_boundary_pairwise_disjoint(pgn, traits);
  CGAL_warning_msg
    (holes_disjoint,
     "Holes of the PWH intersect amongst themselves or with outer boundary");
  if (! holes_disjoint) return false;

  return true;
}

template <typename Traits_2>
bool
is_valid_unknown_polygon(const typename Traits_2::Polygon_with_holes_2& pgn,
                         const Traits_2& traits)
{ return is_valid_polygon_with_holes(pgn, traits); }

template <typename Traits_2>
bool is_valid_unknown_polygon(const typename Traits_2::Polygon_2& pgn,
                              const Traits_2& traits)
{ return is_valid_polygon(pgn, traits); }

} // namespace CGAL

#endif

