// Copyright (c) 2004-2006  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/Mesh_2/include/CGAL/Delaunay_mesher_2.h $
// $Id: include/CGAL/Delaunay_mesher_2.h 08b27d3db14 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Laurent RINEAU

#ifndef CGAL_DELAUNAY_MESHER_2_H
#define CGAL_DELAUNAY_MESHER_2_H

#include <CGAL/license/Mesh_2.h>

#include <CGAL/disable_warnings.h>

#include <CGAL/Mesh_2/Refine_edges_with_clusters.h>
#include <CGAL/Mesh_2/Refine_edges_visitor.h>
#include <CGAL/Mesh_2/Refine_faces.h>
#include <CGAL/Delaunay_mesh_size_criteria_2.h>
#include <CGAL/Named_function_parameters.h>

namespace CGAL {

template <typename Tr, typename Crit>
class Delaunay_mesher_2
{

  /** \name `Tr` types */
  typedef typename Tr::Vertex_handle Vertex_handle;
  typedef typename Tr::Face_handle Face_handle;
  typedef typename Tr::Edge Edge;

  typedef typename Tr::Point Point;

  /** \name Types needed for private member data */
  typedef Mesh_2::Refine_edges_with_clusters<Tr,
    Mesh_2::Is_locally_conforming_Gabriel<Tr> > Edges_level;

  typedef Mesh_2::Refine_faces<Tr, Crit, Edges_level> Faces_level;

public:

  typedef Tr Triangulation;
  typedef Crit Criteria;
  typedef Mesh_2::Clusters<Tr> Clusters;

  /** \name Types needed to mark the domain for Delaunay_mesh_2<Tr> */

  typedef std::list<Point> Seeds;
  typedef typename Seeds::const_iterator Seeds_iterator;
  typedef Seeds_iterator Seeds_const_iterator;

private:
  // --- PRIVATE MEMBER DATA ---
  Tr& tr;
  Criteria criteria;
  Null_mesher_level null_level;
  Null_mesh_visitor null_visitor;
  Clusters clusters_;
  Edges_level edges_level;
  Faces_level faces_level;
  Mesh_2::Refine_edges_visitor_from_faces<Faces_level> visitor;

  Seeds seeds;
  bool seeds_mark;
public:
  /** \name CONSTRUCTORS */
  Delaunay_mesher_2(Tr& tr_, const Criteria& criteria_ = Criteria())
    : tr(tr_),
      criteria(criteria_),
      null_level(),
      null_visitor(),
      clusters_(tr),
      edges_level(tr, clusters_, null_level),
      faces_level(tr, criteria, edges_level),
      visitor(faces_level, edges_level, null_visitor),
      seeds_mark(false),
      initialized(false)
  {
  }

  Delaunay_mesher_2(Tr& tr_, Edges_level& edges_level_,
                    const Criteria& criteria_ = Criteria())
    : tr(tr_),
      criteria(criteria_),
      null_level(),
      null_visitor(),
      clusters_(tr),
      edges_level(edges_level_),
      faces_level(tr, criteria, edges_level),
      visitor(faces_level, null_visitor),
      seeds_mark(false),
      initialized(false)
  {
  }

  /** SEEDS HANDLING FUNCTIONS */

  Seeds_const_iterator seeds_begin() const
  {
    return seeds.begin();
  }

  Seeds_const_iterator seeds_end() const
  {
    return seeds.end();
  }

private:
  /** \name INITIALIZED */

  bool initialized;

public:
  /** \name MARKING FUNCTIONS */

  /** The value type of \a InputIterator should be `Point`, and represents
      seeds. Connected components of seeds are marked with the value of
      \a mark. Other components are marked with `!mark`. The connected
      component of infinite faces is always marked with `false`.
  */
  template <class InputIterator>
  void set_seeds(InputIterator b, InputIterator e,
                 const bool mark = false,
                 const bool do_it_now = false)
  {
    seeds.clear();
    std::copy(b, e, std::back_inserter(seeds));
    seeds_mark=mark;
    if(do_it_now) mark_facets();
  }

  void clear_seeds()
  {
    seeds.clear();
    seeds_mark = false;
  }

  void mark_facets(bool domain_specified = false)
  {
    if(!domain_specified) {
      mark_facets(tr, seeds.begin(), seeds.end(), seeds_mark);
    }
    else {
      propagate_marks(tr.infinite_face(), false);
    }
  }

  /** Procedure that marks facets according to a list of seeds. */
  template <typename Seeds_it>
  static void mark_facets(Tr& tr,
                          Seeds_it begin,
                          Seeds_it end,
                          bool mark = false)
  {
    if (tr.dimension()<2) return;
    if( begin != end )
      {
        for(typename Tr::All_faces_iterator it=tr.all_faces_begin();
            it!=tr.all_faces_end();
            ++it)
          it->set_in_domain(!mark);

        for(Seeds_it sit=begin; sit!=end; ++sit)
          {
            Face_handle fh=tr.locate(*sit);
            if(fh!=nullptr)
              propagate_marks(fh, mark);
          }
        propagate_marks(tr.infinite_face(), false);
      }
    else
      mark_convex_hull(tr);
  }

  /**
   * Marks all faces of the convex hull but those connected to the
   * infinite faces.
   */
  static void mark_convex_hull(Tr& tr)
  {
    for(typename Tr::All_faces_iterator fit=tr.all_faces_begin();
        fit!=tr.all_faces_end();
        ++fit)
      fit->set_in_domain(true);
    propagate_marks(tr.infinite_face(), false);
  }

  /** Propagates the mark `mark` recursively. */
  static void propagate_marks(const Face_handle fh, bool mark)
  {
    // std::queue only works with std::list on VC++6, and not with
    // std::deque, which is the default
    // But it should be fixed by VC++7 know. [Laurent Rineau 2003/03/24]
    std::queue<Face_handle/*, std::list<Face_handle>*/> face_queue;
    fh->set_in_domain(mark);
    face_queue.push(fh);
    while( !face_queue.empty() )
      {
        Face_handle fh = face_queue.front();
        face_queue.pop();
        for(int i=0;i<3;i++)
          {
            const Face_handle& nb = fh->neighbor(i);
            if( !fh->is_constrained(i) && (mark != nb->is_in_domain()) )
              {
                nb->set_in_domain(mark);
                face_queue.push(nb);
              }
          }
      }
  }
  /** \name MESHING FUNCTIONS */

  void refine_mesh()
  {
    if(initialized != true) init();
    faces_level.refine(visitor);
  }

  /** \name REMESHING FUNCTIONS */

  void set_criteria(const Criteria& criteria_,
                    bool recalculate_bad_faces = true)
  {
    criteria = criteria_;
    if (recalculate_bad_faces) faces_level.scan_triangulation();
  }

  const Criteria& get_criteria() const
  {
    return criteria;
  }

  template <class Fh_it>
  void set_bad_faces(Fh_it begin, Fh_it end)
  {
    faces_level.set_bad_faces(begin, end);
  }

  /** \name STEP BY STEP FUNCTIONS */

  /**
     Initialize the data structures
     (The call of this function is REQUIRED before any step by step
     operation).
  */
  void init(bool domain_specified = false)
  {
    mark_facets(domain_specified);
    clusters_.create_clusters();
    edges_level.scan_triangulation();
    faces_level.scan_triangulation();
    initialized = true;
  }

  bool is_refinement_done ()
  {
    return faces_level.is_algorithm_done();
  }

  bool
  step_by_step_refine_mesh()
  {
    return faces_level.try_to_insert_one_point(visitor);
  }

  bool try_one_step_refine_mesh()
  {
    return faces_level.one_step(visitor);
  }

  /** \name ACCESS FUNCTIONS */

  const Mesh_2::Clusters<Tr>& clusters() const
  {
    return clusters_;
  }

  const Triangulation& triangulation() const
  {
    return tr;
  }

  /** \name DEBUGGING FUNCTIONS */

  typedef typename Edges_level::Constrained_edge Constrained_edge;

  bool is_edges_refinement_done()
  {
    return edges_level.is_algorithm_done();
  }

  Edge next_encroached_edge()
  {
    return edges_level.get_next_element();
  }

  const Face_handle next_bad_face()
  {
    return faces_level.get_next_element();
  }

  const Point next_refinement_point()
  {
    if( !edges_level.is_algorithm_done() )
      return edges_level.refinement_point(next_encroached_edge());
    else
      return faces_level.refinement_point(next_bad_face());
  }

  typedef typename Edges_level::Edges_const_iterator
    Encroached_edges_const_iterator;

  typedef typename Faces_level::Bad_faces_const_iterator
    Bad_faces_const_iterator;

  Encroached_edges_const_iterator encroached_edges_begin() const
  {
    return edges_level.begin();
  }

  Encroached_edges_const_iterator encroached_edges_end() const
  {
    return edges_level.end();
  }

  Bad_faces_const_iterator bad_faces_begin() const
  {
    return faces_level.begin();
  }

  Bad_faces_const_iterator bad_faces_end() const
  {
    return faces_level.end();
  }
}; // end class Delaunay_mesher_2

// --- GLOBAL FUNCTIONS ---

#if !defined(CGAL_NO_DEPRECATED_CODE)
template <typename Tr, typename Criteria>
CGAL_DEPRECATED
void
refine_Delaunay_mesh_2(Tr& t,
                       const Criteria& criteria = Criteria(), bool domain_specified=false)
{
  Delaunay_mesher_2<Tr, Criteria> mesher(t, criteria);
  mesher.init(domain_specified);
  mesher.refine_mesh();
}

template <typename Tr, typename Criteria, typename InputIterator>
CGAL_DEPRECATED
void
refine_Delaunay_mesh_2(Tr& t,
                       InputIterator b, InputIterator e,
                       const Criteria& criteria = Criteria(),
                       bool mark = false)
{
  Delaunay_mesher_2<Tr, Criteria> mesher(t, criteria);
  mesher.set_seeds(b, e, mark);
  mesher.refine_mesh();
}
#endif

template<typename Tr, typename CGAL_NP_TEMPLATE_PARAMETERS>
void
refine_Delaunay_mesh_2(Tr& t, const CGAL_NP_CLASS& np)
{
  typedef Delaunay_mesh_size_criteria_2<Tr> Default_criteria;
  typedef typename internal_np::Lookup_named_param_def<internal_np::criteria_t,
                                                       CGAL_NP_CLASS,
                                                       Default_criteria>::type Criteria;

  using parameters::choose_parameter;
  using parameters::get_parameter;
  using parameters::get_parameter_reference;
  using parameters::is_default_parameter;

  Delaunay_mesher_2<Tr, Criteria> mesher(
      t, choose_parameter<Default_criteria>(
             get_parameter_reference(np, internal_np::criteria)));

  if (!choose_parameter(get_parameter(np, internal_np::domain_is_initialized), false))
  {
    if (is_default_parameter<CGAL_NP_CLASS, internal_np::seeds_t>::value) // no seeds provided
    {
      mesher.init(false);
    }
    else
    {
      typedef std::vector<typename Tr::Point_2> Default_seeds;
      typedef typename internal_np::Lookup_named_param_def<internal_np::seeds_t,
                                                           CGAL_NP_CLASS,
                                                           Default_seeds>::reference Seeds;
      Default_seeds ds;
      Seeds seeds = choose_parameter(get_parameter_reference(np, internal_np::seeds), ds);
      mesher.set_seeds(seeds.begin(), seeds.end(),
                       choose_parameter(get_parameter(np, internal_np::seeds_are_in_domain), false));
    }
  }
  else
    mesher.init(true);

  mesher.refine_mesh();
}

} // end namespace CGAL

#include <CGAL/enable_warnings.h>

#endif // CGAL_DELAUNAY_MESHER_2_H
