// Copyright (c) 2001  Tel-Aviv University (Israel).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0+
//
//
// author(s)     : Eli Packer <elip@post.tau.ac.il>

#ifndef CGAL_SNAP_ROUNDING_2_H
#define CGAL_SNAP_ROUNDING_2_H

#include <CGAL/license/Snap_rounding_2.h>


#include <iostream>
#include <CGAL/basic.h>
#include <CGAL/enum.h>
#include <CGAL/predicates_on_points_2.h>
#include <CGAL/intersection_2.h>
#include <CGAL/Surface_sweep_2_algorithms.h>
#include <list>
#include <set>
#include <CGAL/Snap_rounding_kd_2.h>
#include <CGAL/utility.h>
#include <CGAL/Iterator_project.h>
#include <CGAL/function_objects.h>
#include <CGAL/tss.h>

namespace CGAL {

enum SEG_Direction {SEG_UP_RIGHT,SEG_UP_LEFT,SEG_DOWN_RIGHT,SEG_DOWN_LEFT,
                SEG_UP,SEG_DOWN,SEG_LEFT,SEG_RIGHT,SEG_POINT_SEG};

template<class Traits_>
class Segment_data {
private:
  typedef Traits_                               Traits;
  typedef typename Traits::FT                   NT;
  typedef typename Traits::Segment_2            Segment_2;
  typedef typename Traits::Point_2              Point_2;

private:
  Point_2 p;
  Point_2 q;

  Traits m_gt;

public:
  Segment_data();
  Segment_data(const Point_2 & p_inp,const Point_2 & q_inp);
  Segment_2 segment() const { return(Segment_2(p, q)); }
  const Point_2& source() const { return(p); }
  const Point_2& target() const { return(q); }
  inline void set_data(const Point_2 & inp_p,const Point_2 & inp_q);
  void determine_direction(SEG_Direction & seg_dir);
  bool equal(const Segment_2 & s);
  Segment_data(const Segment_data & other);
};

/*! */
template<class Traits_>
class Hot_pixel {
private:
  typedef Traits_                       Traits;
  typedef typename Traits::FT           NT;
  typedef typename Traits::Segment_2    Segment_2;
  typedef typename Traits::Point_2      Point_2;
  typedef CGAL::Segment_data<Traits>          Segment_data;

private:
  // p is the center of the hot pixel
  Point_2 p;
  Point_2 p_left;
  Point_2 p_right;
  Point_2 p_down;
  Point_2 p_up;
  Traits_ m_gt;
  NT pixel_size;
  Segment_2 *right_seg;
  Segment_2 *left_seg;
  Segment_2 *top_seg;
  Segment_2 *bot_seg;

  static SEG_Direction& direction()
  {
    CGAL_STATIC_THREAD_LOCAL_VARIABLE(SEG_Direction, seg_dir,SEG_UP);
    return seg_dir;
  }



public:
  Hot_pixel(const Point_2 & inp_point, NT inp_pixel_size);
  ~Hot_pixel();
  inline Point_2 get_center() const;
  inline Point_2 get_center(bool int_output) const;
  bool intersect_left(const Segment_2 & seg, SEG_Direction seg_dir) const;
  bool intersect_right(const Segment_2 & seg, SEG_Direction seg_dir) const;
  bool intersect_bot(const Segment_2 & seg, SEG_Direction seg_dir) const;
  bool intersect_top(const Segment_2 & seg, SEG_Direction seg_dir) const;
  bool intersect(Segment_data & seg, SEG_Direction seg_dir) const;
  void set_direction(SEG_Direction inp_seg_dir) { direction() = inp_seg_dir; }
  SEG_Direction get_direction() const { return direction(); }
};

/*! */


// a function for compare two hot pixels for the set of hot pixels
template<class Traits_>
struct Hot_pixel_auclidian_cmp {
  typedef CGAL::Hot_pixel<Traits_>    Hot_pixel;

  Traits_ m_gt;

  bool operator()(const Hot_pixel * h1, const Hot_pixel * h2) const;
};

// a function for compare two hot pixels for the set of hot pixels a
// certain segment intersect
template<class Traits_>
struct Hot_pixel_dir_cmp {
  typedef CGAL::Hot_pixel<Traits_>    Hot_pixel;

  Traits_ m_gt;

  bool operator()(const Hot_pixel * h1, const Hot_pixel * h2) const;
};

#ifdef CGAL_SR_DEBUG
int number_of_false_hp;
#endif

template<class Traits_,class OutputContainer>
class Snap_rounding_2 {
private:
  typedef Traits_                                       Traits;
  typedef typename Traits::FT                           NT;
  typedef typename Traits::X_monotone_curve_2           X_monotone_curve_2;
  typedef typename OutputContainer::value_type          Polyline_type;
  typedef CGAL::Hot_pixel<Traits_>                      Hot_pixel;
  typedef CGAL::Segment_data<Traits>                    Segment_data;
  typedef CGAL::Multiple_kd_tree<Traits,Hot_pixel *>    Multiple_kd_tree;
  typedef std::list<Segment_data>                       Segment_data_list;
  typedef CGAL::Hot_pixel_dir_cmp<Traits>               Hot_pixel_dir_cmp;
  typedef std::set<Hot_pixel *, Hot_pixel_dir_cmp>      Hot_pixel_set;

public:
  // friend class Segment_data<Traits>;
  // friend class Hot_pixel<Traits>;
  // friend struct Hot_pixel_dir_cmp<Traits>;

  typedef typename Traits::Segment_2                    Segment_2;
  typedef typename Traits::Point_2                      Point_2;
  typedef std::list<Point_2>                            Point_list;

  void find_hot_pixels_and_create_kd_trees(NT pixel_size,
                                           unsigned int number_of_kd_trees,
                                           Segment_data_list & seg_list,
                                           Multiple_kd_tree ** mul_kd_tree);

  void iterate(OutputContainer & output_container,
               NT pixel_size, bool int_output, bool do_isr,
               Segment_data_list & seg_list,
               Multiple_kd_tree * mul_kd_tree);

private:
  Traits m_gt;

  static const int default_number_of_kd_trees = 1;

  void find_intersected_hot_pixels(Segment_data & seg,
                                   Hot_pixel_set &hot_pixels_intersected_set,
                                   int &number_of_intersections,
                                   NT pixel_size,
                                   Multiple_kd_tree * mul_kd_tree);

  void reroute_sr(Hot_pixel_set & inp_hot_pixels_intersected_set,
                  Polyline_type & seg_output,
                  bool int_output);

  void reroute_isr(Hot_pixel_set & inp_hot_pixels_intersected_set,
                   Polyline_type & seg_output,
                   int number_of_intersections,
                   bool first_time,
                   NT pixel_size,
                   bool int_output,
                   Multiple_kd_tree * mul_kd_tree);
};

/*! Constructor */
template<class Traits_>
Segment_data<Traits_>::Segment_data() {}

/*! Constructor */
template<class Traits_>
Segment_data<Traits_>::Segment_data(const Point_2 & p_inp,
                                    const Point_2 & q_inp) :
  p(p_inp), q(q_inp) {}

/*! Constructor */
template<class Traits_>
Segment_data<Traits_>::Segment_data(const Segment_data & other)
{
  p = other.p;
  q = other.q;
}

/*! */
template<class Traits_>
inline void Segment_data<Traits_>::set_data(const Point_2 & inp_p,
                                            const Point_2 & inp_q)
{
  p = inp_p;
  q = inp_q;
}

/*! */
template<class Traits_>
bool Segment_data<Traits_>::equal(const Segment_2 & s)
{
  typedef typename Traits_::Construct_vertex_2  Construct_vertex_2;
  Construct_vertex_2 construct_vertex = m_gt.construct_vertex_2_object();
  return(construct_vertex(s, 0) == p && construct_vertex(s, 1) == q);
}

/*! */
template<class Traits_>
void Segment_data<Traits_>::determine_direction(SEG_Direction & seg_dir)
{
  typedef typename Traits_::Compare_y_2         Compare_y_2;
  typedef typename Traits_::Compare_x_2         Compare_x_2;

  Compare_x_2 compare_x = m_gt.compare_x_2_object();
  Compare_y_2 compare_y = m_gt.compare_y_2_object();

  Comparison_result cx = compare_x(p, q);
  Comparison_result cy = compare_y(p, q);

  if (cx == SMALLER) {
    if (cy == SMALLER) seg_dir = SEG_UP_RIGHT;
    else if (cy == EQUAL) seg_dir = SEG_RIGHT;
    else seg_dir = SEG_DOWN_RIGHT;
  } else if (cx == EQUAL) {
    if (cy == SMALLER) seg_dir = SEG_UP;
    else if (cy == EQUAL) seg_dir = SEG_POINT_SEG;
    else seg_dir = SEG_DOWN;
  } else {
    if (cy == SMALLER) seg_dir = SEG_UP_LEFT;
    else if (cy == EQUAL) seg_dir = SEG_LEFT;
    else seg_dir = SEG_DOWN_LEFT;
  }
}

// intersection pixel
template<class Traits_>
Hot_pixel<Traits_>::Hot_pixel(const Point_2 & inp_point, NT inp_pixel_size) :
  pixel_size(inp_pixel_size)
{
  NT x,y;
  m_gt.snap_2_object()(inp_point, pixel_size, x, y);

  NT left_coord = x - pixel_size / NT(2.0);
  NT right_coord = x + pixel_size / NT(2.0);
  NT bottom_coord = y - pixel_size / NT(2.0);
  NT top_coord = y + pixel_size / NT(2.0);
  p = Point_2(x, y);
  p_left = Point_2(left_coord, y);
  p_right = Point_2(right_coord, y);
  p_down = Point_2(x, bottom_coord);
  p_up = Point_2(x, top_coord);

  typedef typename Traits::Construct_segment_2  Construct_segment_2;
  Construct_segment_2 construct_seg = m_gt.construct_segment_2_object();

  Point_2 lb(left_coord, bottom_coord);
  Point_2 rb(right_coord, bottom_coord);
  Point_2 lt(left_coord, top_coord);
  Point_2 rt(right_coord, top_coord);
  right_seg = new Segment_2(construct_seg(rb, rt));
  left_seg = new Segment_2(construct_seg(lb, lt));
  top_seg = new Segment_2(construct_seg(lt, rt));
  bot_seg = new Segment_2(construct_seg(lb, rb));
}

/*! */
template<class Traits_>
Hot_pixel<Traits_>::~Hot_pixel()
{
  delete right_seg;
  delete left_seg;
  delete top_seg;
  delete bot_seg;
}

/*! */
template<class Traits_>
inline typename Hot_pixel<Traits_>::Point_2
Hot_pixel<Traits_>::get_center() const
{
  return(p);
}

/*! */
template<class Traits_>
inline typename Hot_pixel<Traits_>::Point_2
Hot_pixel<Traits_>::get_center(bool int_output) const
{
  if (int_output) {
    Point_2 out_p = m_gt.integer_grid_point_2_object()(p,pixel_size);
    return(out_p);
  } else
    return(p);
}

/*! */
template<class Traits_>
bool Hot_pixel<Traits_>::intersect_left(const Segment_2 & seg,
                                        SEG_Direction seg_dir) const
{
  typedef typename Traits_::Compare_y_2         Compare_y_2;
  typedef typename Traits_::Construct_vertex_2  Construct_vertex_2;

  Object result;
  Point_2 p;
  Segment_2 s;

  result = intersection(seg, *left_seg);

  if (assign(p, result)) {
    Compare_y_2 compare_y = m_gt.compare_y_2_object();
    Construct_vertex_2 construct_vertex = m_gt.construct_vertex_2_object();

    Comparison_result c_p = compare_y(p, p_up);
    Comparison_result c_target = compare_y(construct_vertex(seg, 1), p_up);
    Comparison_result c_source = compare_y(construct_vertex(seg, 0), p_up);

    return(c_p != EQUAL || (seg_dir == SEG_UP_LEFT && c_source != EQUAL) ||
           (seg_dir == SEG_DOWN_RIGHT && c_target != EQUAL));
  } else if (assign(s,result))
    return(true);
  else
    return(false);
}

/*! */
template<class Traits_>
bool Hot_pixel<Traits_>::intersect_right(const Segment_2 & seg,
                                         SEG_Direction seg_dir) const
{
  typedef typename Traits_::Compare_y_2         Compare_y_2;
  typedef typename Traits_::Compare_x_2         Compare_x_2;
  typedef typename Traits_::Construct_vertex_2  Construct_vertex_2;

  Object result;
  Point_2 p;
  Segment_2 s;

  result = intersection(seg, *right_seg);

  if (assign(p,result)) {
    // bottom right point was checked in intersect_bot
    Compare_y_2 compare_y = m_gt.compare_y_2_object();
    Construct_vertex_2 construct_vertex = m_gt.construct_vertex_2_object();

    Comparison_result c1 = compare_y(p, p_up);
    Comparison_result c2 = compare_y(p, p_down);

    Point_2 src = construct_vertex(seg, 0);
    Point_2 trg = construct_vertex(seg, 1);
    Comparison_result c3 = compare_y(src, p_up);
    Comparison_result c4 = compare_y(trg, p_up);

    if (c1 == EQUAL)
      return ((seg_dir == SEG_UP_RIGHT && c3 != EQUAL) ||
              (seg_dir == SEG_DOWN_LEFT && c4 != EQUAL));
    else if (c2 == EQUAL)
      return false;// was checked

    Compare_x_2 compare_x = m_gt.compare_x_2_object();
    Comparison_result c_target = compare_x(p_right, trg);
    Comparison_result c_source = compare_x(p_right, src);

    return (((seg_dir == SEG_LEFT || seg_dir == SEG_DOWN_LEFT ||
	      seg_dir == SEG_UP_LEFT) && c_target != EQUAL) ||
            ((seg_dir == SEG_RIGHT || seg_dir == SEG_DOWN_RIGHT ||
	      seg_dir == SEG_UP_RIGHT) && c_source != EQUAL));
  }
  return false;
}

/*! */
template<class Traits_>
bool Hot_pixel<Traits_>::intersect_bot(const Segment_2 & seg,
                                       SEG_Direction seg_dir) const
{
  typedef typename Traits_::Compare_x_2         Compare_x_2;
  typedef typename Traits_::Construct_vertex_2  Construct_vertex_2;

  Object result;
  Point_2 p;
  Segment_2 s;

  result = intersection(seg,*bot_seg);

  if (assign(p,result)) {
    Compare_x_2 compare_x = m_gt.compare_x_2_object();
    Construct_vertex_2 construct_vertex = m_gt.construct_vertex_2_object();

    Comparison_result c_p = compare_x(p, p_right);
    Comparison_result c_target = compare_x(construct_vertex(seg, 1), p_right);
    Comparison_result c_source = compare_x(construct_vertex(seg, 0), p_right);

    return(c_p != EQUAL || (seg_dir == SEG_UP_LEFT && c_target != EQUAL) ||
	   (seg_dir == SEG_DOWN_RIGHT && c_source != EQUAL));
  } else if (assign(s,result))
    return(true);
  else
    return(false);
}

/*! */
template<class Traits_>
bool Hot_pixel<Traits_>::intersect_top(const Segment_2 & seg,
                                       SEG_Direction seg_dir) const
{
  typedef typename Traits_::Compare_x_2         Compare_x_2;
  typedef typename Traits_::Compare_y_2         Compare_y_2;
  typedef typename Traits_::Construct_vertex_2  Construct_vertex_2;

  Object result;
  Point_2 p;
  Segment_2 s;

  result = intersection(seg, *top_seg);

  if (assign(p,result))
  {
    Compare_x_2 compare_x = m_gt.compare_x_2_object();
    Compare_y_2 compare_y = m_gt.compare_y_2_object();
    Construct_vertex_2 construct_vertex = m_gt.construct_vertex_2_object();

    // corner points was checked in intersect_bot
    Comparison_result c1 = compare_x(p, p_left);
    Comparison_result c2 = compare_x(p, p_right);
    Comparison_result c3 = compare_y(construct_vertex(seg, 1), p_up);
    Comparison_result c4 = compare_y(construct_vertex(seg, 0), p_up);

    if (c1 == EQUAL || c2 == EQUAL)
      return(false);// were checked
    else
      return(((seg_dir == SEG_DOWN || seg_dir == SEG_DOWN_LEFT ||
	       seg_dir == SEG_DOWN_RIGHT) && c3 != EQUAL) ||
             ((seg_dir == SEG_UP || seg_dir == SEG_UP_LEFT ||
	       seg_dir == SEG_UP_RIGHT) && c4 != EQUAL));
  }
  return(false);
}

/*! */
template<class Traits_>
bool
Hot_pixel<Traits_>::intersect(Segment_data & seg, SEG_Direction my_seg_dir) const
{
  Segment_2 s = seg.segment();

  return(intersect_bot(s,my_seg_dir) || intersect_left(s, my_seg_dir) ||
         intersect_right(s, my_seg_dir) || intersect_top(s, my_seg_dir));
}

// a function for compare two hot pixels for the set of hot pixels
template<class Traits_>
bool Hot_pixel_auclidian_cmp<Traits_>::
operator()(const Hot_pixel * h1, const Hot_pixel * h2) const
{
  typedef typename Traits_::Compare_x_2         Compare_x_2;
  typedef typename Traits_::Compare_y_2         Compare_y_2;

  Compare_x_2 compare_x = m_gt.compare_x_2_object();
  Compare_y_2 compare_y = m_gt.compare_y_2_object();

  Comparison_result cx = compare_x(h1->get_center(), h2->get_center());
  Comparison_result cy = compare_y(h1->get_center(), h2->get_center());

  return(cx == SMALLER || ( cx == EQUAL && cy == SMALLER));
}

// a function for compare two hot pixels for the set of hot pixels a certain
// segment intersect
template<class Traits_>
bool Hot_pixel_dir_cmp<Traits_>::operator ()(const Hot_pixel * h1,
                                             const Hot_pixel * h2) const
{
  typedef typename Traits_::Compare_x_2         Compare_x_2;
  typedef typename Traits_::Compare_y_2         Compare_y_2;

  Compare_x_2 compare_x = m_gt.compare_x_2_object();
  Compare_y_2 compare_y = m_gt.compare_y_2_object();

  Comparison_result cx = compare_x(h1->get_center(), h2->get_center());
  Comparison_result cy = compare_y(h1->get_center(), h2->get_center());
  SEG_Direction seg_dir = h1->get_direction();

  // Point segment intersects only one pixel, thus ignored
  return((seg_dir == SEG_UP_RIGHT &&  (cx == SMALLER || (cx == EQUAL &&
							 cy == SMALLER))) ||
	 (seg_dir == SEG_UP_LEFT && (cx == LARGER || (cx == EQUAL &&
						      cy == SMALLER))) ||
	 (seg_dir == SEG_DOWN_RIGHT && (cx == SMALLER || (cx == EQUAL &&
							  cy == LARGER))) ||
         (seg_dir == SEG_DOWN_LEFT && (cx == LARGER || (cx == EQUAL &&
							cy == LARGER))) ||
	 (seg_dir == SEG_UP && cy == SMALLER) ||
         (seg_dir == SEG_DOWN && cy == LARGER) ||
	 (seg_dir == SEG_LEFT && cx == LARGER) ||
	 (seg_dir == SEG_RIGHT && cx == SMALLER));
}

/*! */
template<class Traits,class OutputContainer>
void Snap_rounding_2<Traits,OutputContainer>::
find_hot_pixels_and_create_kd_trees(NT pixel_size,
                                    unsigned int number_of_kd_trees,
                                    std::list<Segment_data> & seg_list,
                                    Multiple_kd_tree ** mul_kd_tree)
{
  typedef std::pair<Point_2, Hot_pixel *>               Point_hot_pixel_pair;
  typedef typename std::list<Segment_data>::iterator    Segment_data_iter;
  typedef std::list<Segment_2>                          Segment_list;
  typedef typename std::list<Point_2>::const_iterator   Point_const_iter;

  typedef typename Traits::Construct_segment_2  Construct_segment_2;
  Construct_segment_2 construct_seg = m_gt.construct_segment_2_object();

  Hot_pixel * hp;
  Segment_data_iter iter1;
  Object result;
  Point_2 p;
  std::list<Point_hot_pixel_pair> hot_pixels_list;
  Segment_list segments;

  if (seg_list.empty()) return;

  for (iter1 = seg_list.begin(); iter1 != seg_list.end(); ++iter1) {
    segments.push_back(construct_seg(iter1->source(), iter1->target()));
  }

  // get intersection points (with endpoints)
  Point_list mypointlist;

  CGAL::compute_intersection_points(segments.begin(), segments.end(),
                             std::back_inserter(mypointlist), true, m_gt);

  for (Point_const_iter v_iter = mypointlist.begin();
       v_iter != mypointlist.end(); ++v_iter)
  {
    hp = new Hot_pixel(*v_iter, pixel_size);
    hot_pixels_list.push_back(Point_hot_pixel_pair(hp->get_center(), hp));
  }

  // create kd multiple tree
  // create simple_list from seg_list
  Segment_list simple_seg_list;
  for (Segment_data_iter iter = seg_list.begin(); iter != seg_list.end();
       ++iter)
  {
    simple_seg_list.push_back(construct_seg(iter->source(), iter->target()));
  }
  *mul_kd_tree = new Multiple_kd_tree(hot_pixels_list, number_of_kd_trees,
                                      simple_seg_list);
}

/*! */
template<class Traits_,class OutputContainer>
void Snap_rounding_2<Traits_,OutputContainer>::
find_intersected_hot_pixels(Segment_data & seg,
                            std::set<Hot_pixel *,
                            Hot_pixel_dir_cmp> & hot_pixels_intersected_set,
                            int &number_of_intersections,
                            NT pixel_size,
                            Multiple_kd_tree * mul_kd_tree)
{
  typedef typename std::list<Hot_pixel *>::iterator     Hot_pixel_iter;

  Hot_pixel_iter iter;
  SEG_Direction seg_dir;

  hot_pixels_intersected_set.clear();
  seg.determine_direction(seg_dir);
  number_of_intersections = 0;
  std::list<Hot_pixel *> hot_pixels_list;

  mul_kd_tree->get_intersecting_points(hot_pixels_list,
                                       Segment_2(seg.segment()), pixel_size);

  for (iter = hot_pixels_list.begin();iter != hot_pixels_list.end();++iter) {
    if ((*iter)->intersect(seg,seg_dir)) {
      (*iter)->set_direction(seg_dir);
      hot_pixels_intersected_set.insert(*iter);
    }

#ifdef CGAL_SR_DEBUG
    else
      ++number_of_false_hp;
#endif

  }

  number_of_intersections = static_cast<int>(hot_pixels_intersected_set.size());
}

/*! */
template<class Traits_,class OutputContainer>
void Snap_rounding_2<Traits_,OutputContainer>::
reroute_sr(std::set<Hot_pixel *, Hot_pixel_dir_cmp>
           & inp_hot_pixels_intersected_set,
           Polyline_type & seg_output,
           bool int_output)
{
  typedef typename std::set<Hot_pixel *, Hot_pixel_dir_cmp>::iterator
    Hot_pixel_iter;

  Hot_pixel_iter hot_pixel_iter = inp_hot_pixels_intersected_set.begin();
  ++hot_pixel_iter;

  while (hot_pixel_iter != inp_hot_pixels_intersected_set.end()) {
    seg_output.push_back((*hot_pixel_iter)->get_center(int_output));
    ++hot_pixel_iter;
  }
}

/*! */
template<class Traits_,class OutputContainer>
void Snap_rounding_2<Traits_,OutputContainer>::
reroute_isr(std::set<Hot_pixel *, Hot_pixel_dir_cmp>
            & inp_hot_pixels_intersected_set,
            Polyline_type & seg_output,
            int number_of_intersections,
            bool first_time,
            NT pixel_size,
            bool int_output,
            Multiple_kd_tree * mul_kd_tree)
{
  typedef std::set<Hot_pixel *, Hot_pixel_dir_cmp>      Hot_pixel_set;
  typedef typename std::set<Hot_pixel *, Hot_pixel_dir_cmp>::iterator
    Hot_pixel_iter;

  Hot_pixel_iter hot_pixel_iter, next_hot_pixel_iter,
    before_last_hot_pixel_iter;
  Segment_data seg;
  Hot_pixel_set hot_pixels_intersected_set;
  SEG_Direction seg_dir;

  if (number_of_intersections > 2 || first_time) {
    before_last_hot_pixel_iter = inp_hot_pixels_intersected_set.end();
    --before_last_hot_pixel_iter;

    for (hot_pixel_iter = inp_hot_pixels_intersected_set.begin();
         hot_pixel_iter != before_last_hot_pixel_iter; ++hot_pixel_iter)
    {
      next_hot_pixel_iter = hot_pixel_iter;
      ++next_hot_pixel_iter;
      seg.set_data((*hot_pixel_iter)->get_center(),
                   (*next_hot_pixel_iter)->get_center());
      seg.determine_direction(seg_dir);
      find_intersected_hot_pixels(seg, hot_pixels_intersected_set,
                                  number_of_intersections, pixel_size,
                                  mul_kd_tree);
      reroute_isr(hot_pixels_intersected_set, seg_output,
                  number_of_intersections,false, pixel_size,
                  int_output, mul_kd_tree);
    }
  } else {
    // insert second hot pixel
    hot_pixel_iter = inp_hot_pixels_intersected_set.begin();
    ++hot_pixel_iter;
    seg_output.push_back((*hot_pixel_iter)->get_center(int_output));
  }
}

/*! */
template<class Traits_, class OutputContainer>
void Snap_rounding_2<Traits_,OutputContainer>::
iterate(OutputContainer & output_container,
        NT pixel_size,
        bool int_output, bool do_isr,
        std::list<Segment_data> & seg_list,
        Multiple_kd_tree * mul_kd_tree)
{
  typedef std::set<Hot_pixel *, Hot_pixel_dir_cmp>      Hot_pixel_set;
  typedef typename std::set<Hot_pixel *, Hot_pixel_dir_cmp>::iterator
    Hot_pixel_iter;
  typedef typename std::list<Segment_data>::iterator    Segment_data_iter;

  Polyline_type seg_output;
  Hot_pixel_set hot_pixels_intersected_set;
  Hot_pixel_iter hot_pixel_iter;
  int number_of_intersections;
  Hot_pixel * hp;
  SEG_Direction seg_dir;

  for (Segment_data_iter iter = seg_list.begin(); iter != seg_list.end();
       ++iter)
  {
    seg_output.clear();
    iter->determine_direction(seg_dir);
    find_intersected_hot_pixels(*iter, hot_pixels_intersected_set,
                                number_of_intersections, pixel_size,
                                mul_kd_tree);
    // hot_pixels_intersected_set must have at least two hot pixels when the
    // segment is not in entirely inside a hot pixel enter first hot pixel
    hot_pixel_iter = hot_pixels_intersected_set.begin();
    if (hot_pixel_iter == hot_pixels_intersected_set.end()) {
      // segment entirely inside a pixel
      hp = new Hot_pixel(iter->source(), pixel_size);
      seg_output.push_back(hp->get_center(int_output));
      delete hp;
    } else {
      seg_output.push_back((*hot_pixel_iter)->get_center(int_output));
      if (number_of_intersections > 1) {
        // segments that have at most one intersecting hot pixel are
        // done(it was inserted)
        if (do_isr)
          reroute_isr(hot_pixels_intersected_set, seg_output,
                      number_of_intersections, true,pixel_size,
                      int_output, mul_kd_tree);
        else
          reroute_sr(hot_pixels_intersected_set, seg_output, int_output);
      }
    }

    output_container.push_back(seg_output);
  }
}

/*! */
template<class Traits, class InputIterator, class OutputContainer>
void snap_rounding_2(InputIterator begin,
                     InputIterator end,
                     OutputContainer & output_container,
                     typename Traits::NT pixel_size,
                     bool do_isr = true,
                     bool int_output = true,
                     unsigned int number_of_kd_trees = 1)
{
#ifdef CGAL_SR_DEBUG
  number_of_false_hp = 0;
#endif

  typedef CGAL::Hot_pixel<Traits>                     Hot_pixel;
  typedef CGAL::Segment_data<Traits>                  Segment_data;
  typedef CGAL::Multiple_kd_tree<Traits,Hot_pixel *>  Multiple_kd_tree;
  typedef std::list<Segment_data>                     Segment_data_list;

  Segment_data_list seg_list;
  Multiple_kd_tree * mul_kd_tree = NULL;

  output_container.clear();
  // copy segments list
  while (begin != end) {
    seg_list.push_back(Segment_data(begin->source(), begin->target()));
    ++begin;
  }

  Snap_rounding_2<Traits,OutputContainer> s;

  s.find_hot_pixels_and_create_kd_trees(pixel_size, number_of_kd_trees,
                                        seg_list, &mul_kd_tree);
  s.iterate(output_container, pixel_size, int_output, do_isr,seg_list,
            mul_kd_tree);

  // hope that find_hot_pixels_and_create_kd_trees does not suddenly
  // new up an array of multiple kd_tree
  delete mul_kd_tree;

#ifdef CGAL_SR_DEBUG
  std::cout << "Overall number of false hot pixels in all the queries : "
            << number_of_false_hp << std::endl;
#endif

}

} //namespace CGAL

#endif
