// Copyright (c) 1997  INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.1.1/Triangulation_2/include/CGAL/Constrained_Delaunay_triangulation_2.h $
// $Id: include/CGAL/Constrained_Delaunay_triangulation_2.h 08b27d3db14 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Mariette Yvinec, Jean Daniel Boissonnat

#ifndef CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_2_H
#define CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_2_H

#include <CGAL/license/Triangulation_2.h>


#include <CGAL/assertions.h>
#include <CGAL/Constrained_triangulation_2.h>
#include <CGAL/Triangulation_2/insert_constraints.h>

#ifndef CGAL_TRIANGULATION_2_DONT_INSERT_RANGE_OF_POINTS_WITH_INFO
#include <CGAL/Spatial_sort_traits_adapter_2.h>
#include <CGAL/STL_Extension/internal/info_check.h>
#include <CGAL/type_traits/is_iterator.h>

#include <boost/container/flat_set.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/mpl/and.hpp>

namespace CGAL {

namespace internal{

template <class T,bool has_info=is_iterator<T>::value>
struct Get_iterator_value_type{
 struct type{};
};

template <class T>
struct Get_iterator_value_type<T,true>{
  using type = typename std::iterator_traits<T>::value_type;
};

} } //namespace CGAL::internal
#endif //CGAL_TRIANGULATION_2_DONT_INSERT_RANGE_OF_POINTS_WITH_INFO

namespace CGAL {
namespace internal {

// Compare geometry to ensure deterministic flips
template <class Tr>
class Cdt_2_less_edge
  : public CGAL::cpp98::binary_function<typename Tr::Edge, typename Tr::Edge, bool>
{
  const Tr* tr_ptr;

  using Point = typename Tr::Point;
  using Edge = typename Tr::Edge;

public:
  Cdt_2_less_edge(const Tr* tr_ptr) : tr_ptr(tr_ptr) { }

  // just a manual lexicographical_compare
  bool operator() (const Edge& e1, const Edge& e2) const
  {
    const Point& e1p1 = tr_ptr->point(e1.first, Tr::ccw(e1.second));
    const Point& e2p1 = tr_ptr->point(e2.first, Tr::ccw(e2.second));

    CGAL::Comparison_result res1 = tr_ptr->compare_xy(e1p1, e2p1);
    if(res1 == CGAL::SMALLER)
      return true;
    if(res1 == CGAL::LARGER)
      return false;

    const Point& e1p2 = tr_ptr->point(e1.first, Tr::cw(e1.second));
    const Point& e2p2 = tr_ptr->point(e2.first, Tr::cw(e2.second));

    CGAL::Comparison_result res2 = tr_ptr->compare_xy(e1p2, e2p2);
    if(res2 == CGAL::SMALLER)
      return true;

    return false;
  }
};

} // namespace internal

template <class Gt,
          class Tds_ = Default ,
          class Itag_ = Default>
class Constrained_Delaunay_triangulation_2
  : public  Constrained_triangulation_2<Gt, Tds_, Itag_>
{
public:
  using Base = Constrained_triangulation_2<Gt,Tds_,Itag_>;
  using Ctr = Base;

  // using-declarations, to import types from the base class
  //   (do not mix-up with type aliases)
  // see:  https://en.cppreference.com/w/cpp/language/type_alias
  //       https://en.cppreference.com/w/cpp/language/using_declaration
  using typename Ctr::Tds;
  using typename Ctr::Itag;

  using typename Ctr::Geom_traits;
  using typename Ctr::Intersection_tag;

  using typename Ctr::Constraint;
#if defined(BOOST_MSVC) && (BOOST_MSVC < 1920)
  using Vertex_handle = typename Ctr::Vertex_handle; // workaround for VC++ 19.16 (from MSVC 2017)
#else
  using typename Ctr::Vertex_handle;
#endif
  using typename Ctr::Face_handle;
  using typename Ctr::Edge;
  using typename Ctr::Finite_faces_iterator;
  using typename Ctr::Constrained_edges_iterator;
  using typename Ctr::Face_circulator;
  using typename Ctr::size_type;
  using typename Ctr::Locate_type;

  using typename Ctr::List_edges;
  using typename Ctr::List_faces;
  using typename Ctr::List_vertices;
  using typename Ctr::List_constraints;

  // type aliases (aka type defs)
  using CDt = Constrained_Delaunay_triangulation_2<Gt,Tds_,Itag_>;
  using Point = typename Geom_traits::Point_2;

  //Tag to distinguish Delaunay from regular triangulations
  using Weighted_tag = Tag_false;

  // Tag to distinguish periodic triangulations from others
  using Periodic_tag = Tag_false;

  // using-declarations to import member functions from the base class
  using Ctr::geom_traits;
  using Ctr::number_of_vertices;
  using Ctr::finite_faces_begin;
  using Ctr::finite_faces_end;
  using Ctr::dimension;
  using Ctr::cw;
  using Ctr::ccw;
  using Ctr::infinite_vertex;
  using Ctr::side_of_oriented_circle;
  using Ctr::is_infinite;
  using Ctr::collinear_between;
  using Ctr::are_there_incident_constraints;
  using Ctr::make_hole;
  // The next using statement makes trouble for VC++ with the version
  // of the method that is templated with PointIterator
  // VC++ cannot disambiguate between this method and the method of the base class
  // using Ctr::insert_constraint;
  using Ctr::locate;
  using Ctr::test_dim_down;
  using Ctr::fill_hole_delaunay;
  using Ctr::update_constraints;
  using Ctr::delete_vertex;
  using Ctr::push_back;
  using Ctr::mirror_index;

  Constrained_Delaunay_triangulation_2(const Geom_traits& gt=Geom_traits())
    : Ctr(gt) { }

  Constrained_Delaunay_triangulation_2(const List_constraints& lc,
                                       const Geom_traits& gt=Geom_traits())
    : Ctr(gt)
    {
      insert_constraints(lc.begin(), lc.end());
      CGAL_postcondition( is_valid() );
    }

  template<class InputIterator>
  Constrained_Delaunay_triangulation_2(InputIterator it,
                                       InputIterator last,
                                       const Geom_traits& gt=Geom_traits() )
    : Ctr(gt)
    {
      insert_constraints(it, last);
      CGAL_postcondition( is_valid() );
    }

  ~Constrained_Delaunay_triangulation_2() override {}


  // Ensure rule-of-five: define the copy- and move- constructors
  // as well as the copy- and move- assignment operators.
  Constrained_Delaunay_triangulation_2(
      const Constrained_Delaunay_triangulation_2 &) = default;
  Constrained_Delaunay_triangulation_2(
      Constrained_Delaunay_triangulation_2 &&) = default;

  Constrained_Delaunay_triangulation_2 &
  operator=(const Constrained_Delaunay_triangulation_2 &) = default;

  Constrained_Delaunay_triangulation_2 &
  operator=(Constrained_Delaunay_triangulation_2 &&) = default;

  // FLIPS
  bool is_flipable(Face_handle f, int i, bool perturb = true) const;
  void flip(Face_handle& f, int i);
  void flip_around(Vertex_handle va);
  void flip_around(List_vertices & new_vertices);
#ifndef CGAL_CDT2_USE_RECURSIVE_PROPAGATING_FLIP
  void non_recursive_propagating_flip(Face_handle f,int i);
  void propagating_flip(Face_handle f,int i, int depth=0);
#else
  void propagating_flip(Face_handle f,int i);
#endif
  void propagating_flip(List_edges & edges);

  // CONFLICTS
  bool test_conflict(Face_handle fh, const Point& p) const; //deprecated
  bool test_conflict(const Point& p, Face_handle fh) const;
  void find_conflicts(const Point& p, std::list<Edge>& le,  //deprecated
                      Face_handle hint= Face_handle()) const;
  //  //template member functions, declared and defined at the end
  // template <class OutputItFaces, class OutputItBoundaryEdges>
  // std::pair<OutputItFaces,OutputItBoundaryEdges>
  // get_conflicts_and_boundary(const Point  &p,
  //                                 OutputItFaces fit,
  //                                 OutputItBoundaryEdges eit,
  //                                 Face_handle start) const;
  // template <class OutputItFaces>
  // OutputItFaces
  // get_conflicts (const Point  &p,
  //                OutputItFaces fit,
  //                     Face_handle start ) const;
  // template <class OutputItBoundaryEdges>
  // OutputItBoundaryEdges
  // get_boundary_of_conflicts(const Point  &p,
  //                                OutputItBoundaryEdges eit,
  //                                Face_handle start ) const;

  // INSERTION-REMOVAL
  Vertex_handle insert(const Point & a, Face_handle start = Face_handle());
  Vertex_handle insert(const Point& p,
                       Locate_type lt,
                       Face_handle loc, int li );
  Vertex_handle push_back(const Point& a);
//   template < class InputIterator >
//   std::ptrdiff_t insert(InputIterator first, InputIterator last);

  void remove(Vertex_handle v);
  void remove_incident_constraints(Vertex_handle v);
  void remove_constrained_edge(Face_handle f, int i);
//  template <class OutputItFaces>
//  OutputItFaces
//  remove_constrained_edge(Face_handle f, int i, OutputItFaces out)

  //for backward compatibility
  void insert(Point a, Point b) { insert_constraint(a, b);}

  void insert(Vertex_handle va, Vertex_handle  vb) {insert_constraint(va,vb);}

  void insert_constraint(Vertex_handle va, Vertex_handle  vb)
  {
    ((Ctr*)this)->insert_constraint(va,vb);
  }

  void
  insert_constraint(const Point& a, const Point& b)
  {
    ((Ctr*)this)->insert_constraint(a,b);
  }

  template <class PointIterator>
  void insert_constraint(PointIterator first, PointIterator last, bool close=false)
  {
    if(first == last){
      return;
    }
    const Point& p0 = *first;
    Point p = p0;
    Vertex_handle v0 = insert(p0), v(v0), w(v0);
    Face_handle hint = v0->face();
    ++first;
    for(; first!=last; ++first){
      const Point& q = *first;
      if(p != q){
        w = insert(q,hint);
        insert_constraint(v,w);
        hint = w->face();
        v = w;
        p = q;
      }
    }
    if(close && (p != p0)){
      insert(w,v0);
    }
  }


  void remove_constraint(Face_handle f, int i){remove_constrained_edge(f,i);}

  // CHECK
  bool is_valid(bool verbose = false, int level = 0) const;

protected:
  Vertex_handle virtual_insert(const Point & a,
                               Face_handle start = Face_handle()) override;
  Vertex_handle virtual_insert(const Point& a,
                               Locate_type lt,
                               Face_handle loc,
                               int li ) override;
//Vertex_handle special_insert_in_edge(const Point & a, Face_handle f, int i);
  void remove_2D(Vertex_handle v );
  void triangulate_hole(List_faces& intersected_faces,
                        List_edges& conflict_boundary_ab,
                        List_edges& conflict_boundary_ba) override;

public:
  // MESHING
  // suppressed meshing functions from here

  //template member functions
public:
  template < class InputIterator >
#ifndef CGAL_TRIANGULATION_2_DONT_INSERT_RANGE_OF_POINTS_WITH_INFO
  std::ptrdiff_t
  insert( InputIterator first, InputIterator last,
          std::enable_if_t<
            std::is_convertible<
                typename internal::Get_iterator_value_type< InputIterator >::type,
                Point
            >::value
          >* = nullptr
  )
#else
#if defined(_MSC_VER)
  std::ptrdiff_t insert(InputIterator first, InputIterator last, int i = 0)
#else
    std::ptrdiff_t insert(InputIterator first, InputIterator last)
#endif
#endif
    {
      size_type n = number_of_vertices();

      std::vector<Point> points (first, last);
      spatial_sort (points.begin(), points.end(), geom_traits());
      Face_handle f;
      for (typename std::vector<Point>::const_iterator p = points.begin(), end = points.end();
              p != end; ++p)
          f = insert (*p, f)->face();

      return number_of_vertices() - n;
    }

  // function usable only if no constraint has been inserted
  void restore_Delaunay(Vertex_handle v)
  {
    if(this->dimension() <= 1) return;

    Face_handle f=v->face();
    Face_handle next;
    int i;
    Face_handle start(f);
    do {
      i = f->index(v);
      next = f->neighbor(ccw(i));  // turn ccw around v
      propagating_flip(f,i);
      f = next;
    } while(next != start);
    return;
  }


#ifndef CGAL_TRIANGULATION_2_DONT_INSERT_RANGE_OF_POINTS_WITH_INFO
private:
  //top stands for tuple-or-pair
  template <class Info>
  const Point& top_get_first(const std::pair<Point,Info>& pair) const { return pair.first; }
  template <class Info>
  const Info& top_get_second(const std::pair<Point,Info>& pair) const { return pair.second; }
  template <class Info>
  const Point& top_get_first(const boost::tuple<Point,Info>& tuple) const { return boost::get<0>(tuple); }
  template <class Info>
  const Info& top_get_second(const boost::tuple<Point,Info>& tuple) const { return boost::get<1>(tuple); }

  template <class Tuple_or_pair,class InputIterator>
  std::ptrdiff_t insert_with_info(InputIterator first,InputIterator last)
  {
    size_type n = number_of_vertices();
    std::vector<std::size_t> indices;
    std::vector<Point> points;
    std::vector<typename Tds::Vertex::Info> infos;
    std::size_t index=0;
    for (InputIterator it=first;it!=last;++it){
      Tuple_or_pair value=*it;
      points.push_back( top_get_first(value)  );
      infos.push_back ( top_get_second(value) );
      indices.push_back(index++);
    }

    using Pmap = typename Pointer_property_map<Point>::type;
    using Search_traits = Spatial_sort_traits_adapter_2<Geom_traits,Pmap>;

    spatial_sort(indices.begin(),
                 indices.end(),
                 Search_traits(make_property_map(points),geom_traits()));

    Vertex_handle v_hint;
    Face_handle hint;
    for (typename std::vector<std::size_t>::const_iterator
      it = indices.begin(), end = indices.end();
      it != end; ++it){
      v_hint = insert(points[*it], hint);
      if (v_hint!=Vertex_handle()){
        v_hint->info()=infos[*it];
        hint=v_hint->face();
      }
    }

    return number_of_vertices() - n;
  }

public:

  template < class InputIterator >
  std::ptrdiff_t
  insert( InputIterator first,
          InputIterator last,
          std::enable_if_t<
            std::is_convertible<
              typename internal::Get_iterator_value_type< InputIterator >::type,
              std::pair<Point,typename internal::Info_check<typename Tds::Vertex>::type>
            >::value
          >* =nullptr
  )
  {
    return insert_with_info< std::pair<Point,typename internal::Info_check<typename Tds::Vertex>::type> >(first,last);
  }

  template <class  InputIterator_1,class InputIterator_2>
  std::ptrdiff_t
  insert( boost::zip_iterator< boost::tuple<InputIterator_1,InputIterator_2> > first,
          boost::zip_iterator< boost::tuple<InputIterator_1,InputIterator_2> > last,
          std::enable_if_t<
              std::is_convertible_v< typename std::iterator_traits<InputIterator_1>::value_type, Point > &&
              std::is_convertible_v< typename std::iterator_traits<InputIterator_2>::value_type, typename internal::Info_check<typename Tds::Vertex>::type >
          >* =nullptr
  )
  {
    return insert_with_info< boost::tuple<Point,typename internal::Info_check<typename Tds::Vertex>::type> >(first,last);
  }
#endif //CGAL_TRIANGULATION_2_DONT_INSERT_RANGE_OF_POINTS_WITH_INFO


  template <class PointIterator, class IndicesIterator>
  std::size_t insert_constraints(PointIterator points_first,
                                 PointIterator points_beyond,
                                 IndicesIterator indices_first,
                                 IndicesIterator indices_beyond)
  {
    if(indices_first == indices_beyond){
      return insert(points_first, points_beyond);
    }
    std::vector<Point> points(points_first, points_beyond);
    return internal::insert_constraints(*this,points, indices_first, indices_beyond);
  }


 template <class ConstraintIterator>
  std::size_t insert_constraints(ConstraintIterator first,
                                 ConstraintIterator beyond)
  {
    return internal::insert_constraints(*this,first,beyond);
  }


  template <class OutputItFaces, class OutputItBoundaryEdges>
  std::pair<OutputItFaces,OutputItBoundaryEdges>
  get_conflicts_and_boundary(const Point  &p,
                             OutputItFaces fit,
                             OutputItBoundaryEdges eit,
                             Face_handle start = Face_handle()) const {
    CGAL_precondition( dimension() == 2);
    int li;
    Locate_type lt;
    Face_handle fh = locate(p,lt,li, start);
    switch(lt) {
    case Ctr::OUTSIDE_AFFINE_HULL:
    case Ctr::VERTEX:
      return std::make_pair(fit,eit);
    case Ctr::FACE:
    case Ctr::EDGE:
    case Ctr::OUTSIDE_CONVEX_HULL:
      *fit++ = fh; //put fh in OutputItFaces
      std::pair<OutputItFaces,OutputItBoundaryEdges>
        pit = std::make_pair(fit,eit);
      pit = propagate_conflicts(p,fh,0,pit);
      pit = propagate_conflicts(p,fh,1,pit);
      pit = propagate_conflicts(p,fh,2,pit);
      return pit;
    }
    CGAL_assertion(false);
    return std::make_pair(fit,eit);
  }

  template <class OutputItFaces>
  OutputItFaces
  get_conflicts (const Point  &p,
                 OutputItFaces fit,
                 Face_handle start= Face_handle()) const {
    std::pair<OutputItFaces,Emptyset_iterator> pp =
      get_conflicts_and_boundary(p,fit,Emptyset_iterator(),start);
    return pp.first;
  }

  template <class OutputItBoundaryEdges>
  OutputItBoundaryEdges
  get_boundary_of_conflicts(const Point  &p,
                            OutputItBoundaryEdges eit,
                            Face_handle start= Face_handle()) const {
    std::pair<Emptyset_iterator, OutputItBoundaryEdges> pp =
      get_conflicts_and_boundary(p,Emptyset_iterator(),eit,start);
    return pp.second;
  }


public:
// made  public for the need of Mesh_2
// but not documented
#ifdef CGAL_CDT2_USE_RECURSIVE_PROPAGATE_CONFLICTS
  template <class OutputItFaces, class OutputItBoundaryEdges>
  std::pair<OutputItFaces,OutputItBoundaryEdges>
  propagate_conflicts (const Point  &p,
                      Face_handle fh,
                      int i,
                      std::pair<OutputItFaces,OutputItBoundaryEdges>  pit)  const
  {
     Face_handle fn = fh->neighbor(i);

     if ( fh->is_constrained(i) || ! test_conflict(p,fn)) {
       *(pit.second)++ = Edge(fn, fn->index(fh));
     } else {
       *(pit.first)++ = fn;
       int j = fn->index(fh);
       pit = propagate_conflicts(p,fn,ccw(j),pit);
       pit = propagate_conflicts(p,fn,cw(j), pit);
     }
     return pit;
  }
#else // NO CGAL_CDT2_USE_RECURSIVE_PROPAGATE_CONFLICTS
  template <class OutputItFaces, class OutputItBoundaryEdges>
  std::pair<OutputItFaces,OutputItBoundaryEdges>
  non_recursive_propagate_conflicts (const Point  &p,
                                     Face_handle fh,
                                     int i,
                                     std::pair<OutputItFaces,OutputItBoundaryEdges>  pit)  const
  {
    std::stack<std::pair<Face_handle, int> > stack;
    stack.push(std::make_pair(fh, i));
    while(!stack.empty()) {
      const Face_handle fh = stack.top().first;
      const int i = stack.top().second;
      stack.pop();
      const Face_handle fn = fh->neighbor(i);
      if ( fh->is_constrained(i) || ! test_conflict(p,fn)) {
        *(pit.second)++ = Edge(fn, fn->index(fh));
      } else {
        *(pit.first)++ = fn;
        int j = fn->index(fh);
        stack.push(std::make_pair(fn, cw(j)));
        stack.push(std::make_pair(fn,ccw(j)));
      }
    }
    return pit;
  }

  template <class OutputItFaces, class OutputItBoundaryEdges>
  std::pair<OutputItFaces,OutputItBoundaryEdges>
  propagate_conflicts (const Point  &p,
                      Face_handle fh,
                      int i,
                      std::pair<OutputItFaces,OutputItBoundaryEdges>  pit,
                      int depth=0)  const
  {
    if ( depth==100) return non_recursive_propagate_conflicts(p,fh,i,pit);
    Face_handle fn = fh->neighbor(i);

    if ( fh->is_constrained(i) || ! test_conflict(p,fn)) {
      *(pit.second)++ = Edge(fn, fn->index(fh));
    } else {
      *(pit.first)++ = fn;
      int j = fn->index(fh);
      pit = propagate_conflicts(p,fn,ccw(j),pit,depth+1);
      pit = propagate_conflicts(p,fn,cw(j), pit,depth+1);
    }
     return pit;
  }
#endif // NO CGAL_CDT2_USE_RECURSIVE_PROPAGATE_CONFLICTS




public:
 template <class OutputItFaces>
 OutputItFaces
 propagating_flip(List_edges & edges,
                  OutputItFaces out = Emptyset_iterator()) {
  // makes the triangulation Delaunay by flipping
  // List edges contains an initial list of edges to be flipped
  // Precondition : the output triangulation is Delaunay if the list
  // edges contains all edges of the input triangulation that need to be
  // flipped (plus possibly others)
  int i, ii, indf, indn;
  Face_handle ni, f,ff;
  Edge ei,eni;

  using Less_edge = internal::Cdt_2_less_edge<CDt>;
  using Edge_set = boost::container::flat_set<Edge, Less_edge>;

  Less_edge less_edge(this);
  Edge_set edge_set(less_edge);

  Edge e[4];
  typename List_edges::iterator itedge=edges.begin();

  // initialization of the set of edges to be flip
  while (itedge != edges.end()) {
    f=(*itedge).first;
    i=(*itedge).second;
    if (is_flipable(f,i)) {
      eni=Edge(f->neighbor(i),mirror_index(f,i));
      if (less_edge(*itedge,eni)) edge_set.insert(*itedge);
      else edge_set.insert(eni);
    }
    ++itedge;
  }

  // flip edges and updates the set of edges to be flipped
  while (!(edge_set.empty())) {
    f=(*(edge_set.begin())).first;
    indf=(*(edge_set.begin())).second;

    // erase from edge_set the 4 edges of the wing of the edge to be
    // flipped (edge_set.begin) , i.e. the edges of the faces f and
    // f->neighbor(indf) that are distinct from the edge to be flipped

    ni = f->neighbor(indf);
    indn=mirror_index(f,indf);
    ei= Edge(f,indf);
    edge_set.erase(ei);
    e[0]= Edge(f,cw(indf));
    e[1]= Edge(f,ccw(indf));
    e[2]= Edge(ni,cw(indn));
    e[3]= Edge(ni,ccw(indn));

    for(i=0;i<4;i++) {
      ff=e[i].first;
      ii=e[i].second;
      eni=Edge(ff->neighbor(ii),mirror_index(ff,ii));
      if (less_edge(e[i],eni)) {edge_set.erase(e[i]);}
      else { edge_set.erase(eni);}
    }

    // here is the flip
    *out++ = f;
    *out++ = f->neighbor(indf);
    flip(f, indf);


    //insert in edge_set the 4 edges of the wing of the edge that
    //have been flipped
    e[0]= Edge(f,indf);
    e[1]= Edge(f,cw(indf));
    e[2]= Edge(ni,indn);
    e[3]= Edge(ni,cw(indn));

    for(i=0;i<4;i++) {
      ff=e[i].first;
      ii=e[i].second;
      if (is_flipable(ff,ii)) {
        eni=Edge(ff->neighbor(ii),mirror_index(ff,ii));
        if (less_edge(e[i],eni)) {
          edge_set.insert(e[i]);}
        else {
          edge_set.insert(eni);}
      }
    }
  }
  return out;
 }

 template <class OutputItFaces>
 OutputItFaces
 remove_constrained_edge(Face_handle f, int i, OutputItFaces out) {
  Ctr::remove_constrained_edge(f,i);
  if(dimension() == 2) {
    List_edges le;
    le.push_back(Edge(f,i));
    propagating_flip(le,out);
  }
  return out;
 }

};


template < class Gt, class Tds, class Itag >
bool
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
is_flipable(Face_handle f, int i, bool perturb /* = true */) const
  // determines if edge (f,i) can be flipped
{
  Face_handle ni = f->neighbor(i);
  if (is_infinite(f) || is_infinite(ni)) return false;
  if (f->is_constrained(i)) return false;
  return (side_of_oriented_circle(ni, f->vertex(i)->point(), perturb)
                                        == ON_POSITIVE_SIDE);
}

template < class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
flip (Face_handle& f, int i)
{
  Face_handle g = f->neighbor(i);
  int j = mirror_index(f,i);

  // save wings neighbors to be able to restore constraint status
  Face_handle f1 = f->neighbor(cw(i));
  int i1 = mirror_index(f,cw(i));
  Face_handle f2 = f->neighbor(ccw(i));
  int i2 = mirror_index(f,ccw(i));
  Face_handle f3 = g->neighbor(cw(j));
  int i3 = mirror_index(g,cw(j));
  Face_handle f4 = g->neighbor(ccw(j));
  int i4 = mirror_index(g,ccw(j));

  // The following precondition prevents the test suit
  // of triangulation to work on constrained Delaunay triangulation
  //CGAL_precondition(is_flipable(f,i));
  this->_tds.flip(f, i);

  // restore constraint status
  f->set_constraint(f->index(g), false);
  g->set_constraint(g->index(f), false);
  f1->neighbor(i1)->set_constraint(mirror_index(f1,i1),
                                   f1->is_constrained(i1));
  f2->neighbor(i2)->set_constraint(mirror_index(f2,i2),
                                   f2->is_constrained(i2));
  f3->neighbor(i3)->set_constraint(mirror_index(f3,i3),
                                   f3->is_constrained(i3));
  f4->neighbor(i4)->set_constraint(mirror_index(f4,i4),
                                   f4->is_constrained(i4));
  return;
}

template < class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
flip_around(Vertex_handle va)
  // makes the triangles incident to vertex va Delaunay using flips
{
  if (dimension() <= 1) return;
  Face_handle f=va->face();
  Face_handle next;
  Face_handle start(f);
  int i;
  do {
    i = f->index(va); // FRAGILE : DIM 1
    next = f->neighbor(ccw(i));  // turns ccw around a
    propagating_flip(f,i);
    f=next;
  } while(next != start);
}

template < class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
flip_around(List_vertices& new_vertices)
{
  typename List_vertices::iterator itv=new_vertices.begin();
  for( ; itv != new_vertices.end(); itv++) {
    flip_around(*itv);
  }
  return;
}


#ifndef CGAL_CDT2_USE_RECURSIVE_PROPAGATING_FLIP
template <class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
non_recursive_propagating_flip(Face_handle f , int i)
{
  std::stack<Edge> edges;
  const Vertex_handle& vp = f->vertex(i);
  edges.push(Edge(f,i));

  while(! edges.empty()){
    const Edge& e = edges.top();
    f = e.first;
    i = e.second;

    Face_handle ni = f->neighbor(i);
    flip(f,i);
    if ( !is_flipable(f,i) ) edges.pop();

    i = ni->index(vp);
    if ( is_flipable(ni,i) ) edges.push( Edge(ni,i) );
  }
}

template <class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
propagating_flip(Face_handle f,int i, int depth)
{
  if (!is_flipable(f,i)) return;
#ifdef CGAL_CDT2_IMMEDIATELY_NON_RECURSIVE_PROPAGATING_FLIP
  non_recursive_propagating_flip(f,i);
#else
  int max_depth = 100;
  if(depth==max_depth){
    non_recursive_propagating_flip(f,i);
    return;
  }

  Face_handle ni = f->neighbor(i);
  flip(f, i); // flip for constrained triangulations
  propagating_flip(f,i, depth+1);
  i = ni->index(f->vertex(i));
  propagating_flip(ni,i, depth+1);
#endif
}
#else
template < class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
propagating_flip(Face_handle f,int i)
// similar to the corresponding function in Delaunay_triangulation_2.h
{
  if (!is_flipable(f,i)) return;
  Face_handle ni = f->neighbor(i);
  flip(f, i); // flip for constrained triangulations
  propagating_flip(f,i);
  i = ni->index(f->vertex(i));
  propagating_flip(ni,i);
}
#endif

 template < class Gt, class Tds, class Itag >
 void
 Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
 propagating_flip(List_edges & edges) {
    propagating_flip(edges,Emptyset_iterator());
 }


template < class Gt, class Tds, class Itag >
inline bool
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
test_conflict(const Point& p, Face_handle fh) const
  // true if point P lies inside the circle circumscribing face fh
{
  // return true  if P is inside the circumcircle of fh
  // if fh is infinite, return true when p is in the positive
  // halfspace or on the boundary and in the  finite edge of fh
  Oriented_side os = side_of_oriented_circle(fh,p,true);
  if (os == ON_POSITIVE_SIDE) return true;

  if (os == ON_ORIENTED_BOUNDARY && is_infinite(fh)) {
    int i = fh->index(infinite_vertex());
    return collinear_between(fh->vertex(cw(i))->point(), p,
                             fh->vertex(ccw(i))->point() );
  }

  return false;
}

template < class Gt, class Tds, class Itag >
inline bool
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
test_conflict(Face_handle fh, const Point& p) const
  // true if point P lies inside the circle circumscribing face fh
{
  return test_conflict(p,fh);
}

template < class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
find_conflicts(const Point& p, std::list<Edge>& le, Face_handle hint) const
{
  // sets in le the counterclocwise list of the edges of the boundary of the
  // union of the faces in conflict with p
  // an edge is represented by the incident face that is not in conflict with p
  get_boundary_of_conflicts(p, std::back_inserter(le), hint);
}

template < class Gt, class Tds, class Itag >
inline
typename Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
virtual_insert(const Point & a, Face_handle start)
  // virtual version of the insertion
{
  return insert(a,start);
}

template < class Gt, class Tds, class Itag >
inline
typename Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
virtual_insert(const Point& a,
               Locate_type lt,
               Face_handle loc,
               int li )
// virtual version of insert
{
  return insert(a,lt,loc,li);
}

template < class Gt, class Tds, class Itag >
inline
typename Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
insert(const Point & a, Face_handle start)
 // inserts a in the triangulation
// constrained edges are updated
// Delaunay property is restored
{
  Vertex_handle va= Ctr::insert(a, start);
  flip_around(va);
  return va;
}

template < class Gt, class Tds, class Itag >
typename Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
insert(const Point& a, Locate_type lt, Face_handle loc, int li)
// insert a point p, whose localization is known (lt, f, i)
// constrained edges are updated
// Delaunay property is restored
{
  Vertex_handle va= Ctr::insert(a,lt,loc,li);
  flip_around(va);
  return va;
}

template < class Gt, class Tds, class Itag >
inline
typename Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
push_back(const Point &p)
{
  return insert(p);
}

template < class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
triangulate_hole(List_faces& intersected_faces,
                 List_edges& conflict_boundary_ab,
                 List_edges& conflict_boundary_ba)
{
  List_edges new_edges;
  Ctr::triangulate_hole(intersected_faces,
                       conflict_boundary_ab,
                       conflict_boundary_ba,
                       new_edges);
  propagating_flip(new_edges);
  return;
}


template < class Gt, class Tds, class Itag >
inline void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
remove(Vertex_handle v)
  // remove a vertex and updates the constrained edges of the new faces
  // precondition : there is no incident constraints
{
  CGAL_precondition( v != Vertex_handle() );
  CGAL_precondition( ! is_infinite(v));
  CGAL_precondition( ! are_there_incident_constraints(v));
  if  (dimension() <= 1)    Ctr::remove(v);
  else  remove_2D(v);
  return;
}

// template < class Gt, class Tds, class Itag >
// typename
// Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::Vertex_handle
// Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
// special_insert_in_edge(const Point & a, Face_handle f, int i)
//   // insert  point p in edge(f,i)
//   // bypass the precondition for point a to be in edge(f,i)
//   // update constrained status
//   // this member function is not robust with exact predicates
//   // and approximate construction. Should be removed
// {
//   Vertex_handle vh=Ctr::special_insert_in_edge(a,f,i);
//   flip_around(vh);
//   return vh;
// }

template < class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
remove_2D(Vertex_handle v)
{
 if (test_dim_down(v)) {  this->_tds.remove_dim_down(v);  }
  else {
     std::list<Edge> hole;
    make_hole(v, hole);
    std::list<Edge> shell=hole; //because hole will be emptied by fill_hole
    fill_hole_delaunay(hole);
    update_constraints(shell);
    delete_vertex(v);
  }
  return;
}

template < class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
remove_incident_constraints(Vertex_handle v)
{
   List_edges iconstraints;
   if (are_there_incident_constraints(v,
                                      std::back_inserter(iconstraints))) {
     Ctr::remove_incident_constraints(v);
     if (dimension()==2) propagating_flip(iconstraints);
   }
   return;
}

template < class Gt, class Tds, class Itag >
void
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
remove_constrained_edge(Face_handle f, int i)
{
  remove_constrained_edge(f,i,Emptyset_iterator());
  return;
}


template < class Gt, class Tds, class Itag >
bool
Constrained_Delaunay_triangulation_2<Gt,Tds,Itag>::
is_valid(bool verbose, int level) const
{
  bool result = Ctr::is_valid(verbose, level);
  CGAL_assertion( result );

    Finite_faces_iterator fit= finite_faces_begin();
    for (; fit != finite_faces_end(); fit++) {
      for(int i=0;i<3;i++) {
        result = result && !is_flipable(fit,i, false);
        CGAL_assertion( result );
      }
    }
    return result;
}



} //namespace CGAL
#endif // CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_2_H
