// Copyright (c) 2000  Max-Planck-Institute Saarbruecken (Germany).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.0.3/Partition_2/include/CGAL/partition_is_valid_2.h $
// $Id: include/CGAL/partition_is_valid_2.h cefe3007d59 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Susan Hert <hert@mpi-sb.mpg.de>

#ifndef CGAL_PARTITION_IS_VALID_2_H
#define CGAL_PARTITION_IS_VALID_2_H

#include <CGAL/license/Partition_2.h>


#include <list>
#include <utility>
#include <iterator>
#include <CGAL/assertions.h>
#include <CGAL/Partition_2/Partitioned_polygon_2.h>
#include <CGAL/Partition_2/Partition_vertex_map.h>
#include <CGAL/ch_selected_extreme_points_2.h>
#include <CGAL/Partition_traits_2.h>
#include <CGAL/Partition_is_valid_traits_2.h>
#include <CGAL/Polygon_2.h>

// NOTE:  this could possibly be checked using a planar map overlay, but
//        then the traits class would have to require lots of other things
//        and you have to do many overlaps, not just one so it is
//        less efficient.

namespace CGAL {

template <class Circulator1, class Circulator2, class Traits>
bool
polygons_w_steiner_are_equal(Circulator1 orig_first, Circulator2 new_first,
                             const Traits& traits)
{
   typedef typename Circulator1::value_type                Point_2;

   Circulator1 orig_circ;
   Circulator2 new_circ;

   // find the first (original) vertex in the list of vertices
   for (new_circ = new_first;
        *new_circ != *orig_first && ++new_circ != new_first;)
   {}

   if (new_circ == new_first)
   {
#ifdef CGAL_PARTITION_CHECK_DEBUG
      std::cout << "first vertex " << *orig_first << " not found "
                << std::endl;
#endif // CGAL_PARTITION_CHECK_DEBUG
      return false;
   }

   // first becomes the first one you found; now look for the others
   new_first = new_circ;
   orig_circ = orig_first;
   Point_2 prev_pt = *new_first;

   // keep going until you find all the original vertices, or come back
   // to the first new vertex
   do
   {
      if (*new_circ == *orig_circ)  // points correspond, so move both
      {
         prev_pt = *new_circ;
         new_circ++;
         orig_circ++;
      }
      else // points don't correspond
      {
         if (traits.orientation_2_object()(prev_pt, *new_circ, *orig_circ) !=
             COLLINEAR)
         {
#ifdef CGAL_PARTITION_CHECK_DEBUG
           std::cout << *new_circ << " is not collinear with " << prev_pt
                     << " and " << *orig_circ << std::endl;
#endif
            return false;
         }
         prev_pt = *new_circ;
         new_circ++;
      }
   }
   while (orig_circ != orig_first && new_circ != new_first);

   // if they didn't both come back to the beginning, then something is wrong
   return (orig_circ == orig_first && new_circ == new_first);
}

template <class Circulator1, class Circulator2>
bool
polygons_are_equal(Circulator1 orig_first, Circulator2 new_first)
{
   Circulator1 orig_circ = orig_first;
   Circulator2 new_circ;

   // find the first (original) vertex in the list of vertices
   for (new_circ = new_first;
        *new_circ != *orig_first && ++new_circ != new_first;)
   {}

   new_first = new_circ;
   // now look through both lists until you find a vertex that is not
   // the same or you reach the end of the vertices
   do
   {
#ifdef CGAL_PARTITION_CHECK_DEBUG
      std::cout << *new_first << " is in the right place " << std::endl;
#endif
      orig_circ++; new_circ++;
   }
   while (*orig_circ == *new_circ &&
          orig_circ != orig_first && new_circ != new_first);

   return (orig_circ == orig_first && new_circ == new_first);
}


namespace internal {

template<class InputIterator, class ForwardIterator, class Traits>
bool
partition_is_valid_2 (InputIterator point_first, InputIterator point_last,
                      ForwardIterator poly_first, ForwardIterator poly_last,
                      const Traits& traits)
{
   if (poly_first == poly_last)  return (point_first == point_last);

   typedef typename Traits::Polygon_2::Vertex_const_iterator
                                                       Poly_vtx_iterator;
   typedef typename Traits::Point_2                    Point_2;
   typedef Partition_vertex_map<Traits>                P_Vertex_map;
   typedef typename Traits::Is_valid                   Is_valid;

   Poly_vtx_iterator vtx_begin, vtx_end;

   Is_valid is_valid = traits.is_valid_object(traits);

   std::list<Point_2>    orig_poly;
   for (;point_first != point_last; point_first++)
      orig_poly.push_back(*point_first);

   CGAL_precondition(orientation_2(orig_poly.begin(),orig_poly.end(),
                                   traits) == COUNTERCLOCKWISE);

   P_Vertex_map  output_vertex_set(poly_first, poly_last, traits);

   if (output_vertex_set.polygons_overlap()) return false;

   int poly_num = 0;
   for (; poly_first != poly_last; poly_first++, poly_num++)
   {
      vtx_begin = (*poly_first).vertices_begin();
      vtx_end = (*poly_first).vertices_end();
#ifdef CGAL_PARTITION_CHECK_DEBUG
         std::cout << "Polygon " << poly_num << " is " << std::endl;
         std::cout << *poly_first << std::endl;
#endif
      CGAL_assertion (
           orientation_2(vtx_begin, vtx_end, traits) == COUNTERCLOCKWISE);
      if (!is_valid(vtx_begin, vtx_end))
      {
#ifdef CGAL_PARTITION_CHECK_DEBUG
         std::cout << "It does NOT have the tested property." << std::endl;
#endif
         return false;
      }
   }

   std::list<Point_2>  union_polygon;
   output_vertex_set.union_vertices(std::back_inserter(union_polygon));

#ifdef CGAL_PARTITION_CHECK_DEBUG
   typename std::list<Point_2>::iterator  poly_iterator;
   std::cout << "union polygon is " << std::endl;
   for (poly_iterator = union_polygon.begin();
        poly_iterator != union_polygon.end(); poly_iterator++)
   {
      std::cout << *poly_iterator << " ";
   }
   std::cout << std::endl;
#endif // CGAL_PARTITION_CHECK_DEBUG

   typedef typename std::list<Point_2>::iterator     I;
   typedef Circulator_from_iterator<I>      Circulator;

   Circulator   orig_poly_circ(orig_poly.begin(), orig_poly.end());
   Circulator   union_poly_circ(union_polygon.begin(), union_polygon.end());
   if (orig_poly.size() == union_polygon.size())
     return polygons_are_equal(orig_poly_circ, union_poly_circ);
   else
     return polygons_w_steiner_are_equal(orig_poly_circ, union_poly_circ,
                                         traits);
}
} // namespace internal

template<class InputIterator, class ForwardIterator>
bool
partition_is_valid_2 (InputIterator point_first, InputIterator point_last,
                      ForwardIterator poly_first, ForwardIterator poly_last)
{
   typedef typename std::iterator_traits<InputIterator>::value_type   Point_2;
   typedef typename Kernel_traits<Point_2>::Kernel     K;
   typedef Partition_traits_2<K>                       Traits;
   typedef Is_vacuously_valid<Traits>                  Is_valid;

   Partition_is_valid_traits_2<Traits, Is_valid>   validity_traits;

   return internal::partition_is_valid_2(point_first, point_last,
                                         poly_first, poly_last, validity_traits);
}


template<class InputIterator, class ForwardIterator, class Traits>
bool
partition_is_valid_2 (InputIterator point_first, InputIterator point_last,
                      ForwardIterator poly_first, ForwardIterator poly_last,
                      const Traits& traits)
{
  //typedef typename std::iterator_traits<InputIterator>::value_type   Point_2;
   //typedef typename Kernel_traits<Point_2>::Kernel     K;
   //typedef Partition_traits_2<K>                       Traits;
   typedef Is_vacuously_valid<Traits>                  Is_valid;

   Partition_is_valid_traits_2<Traits, Is_valid>   validity_traits(traits);

   return internal::partition_is_valid_2(point_first, point_last,
                               poly_first, poly_last, validity_traits);
}



template<class InputIterator, class ForwardIterator, class Traits>
bool
convex_partition_is_valid_2(InputIterator point_first,
                            InputIterator point_last,
                            ForwardIterator poly_first,
                            ForwardIterator poly_last,
                            const Traits& traits)
{
   typedef typename Traits::Is_convex_2                 Is_convex_2;
   Partition_is_valid_traits_2<Traits, Is_convex_2>     validity_traits(traits);

   return internal::partition_is_valid_2(point_first, point_last, poly_first, poly_last,
                                         validity_traits);
}

template<class InputIterator, class ForwardIterator>
bool
convex_partition_is_valid_2(InputIterator point_first,
                            InputIterator point_last,
                            ForwardIterator poly_first,
                            ForwardIterator poly_last)
{
   typedef typename std::iterator_traits<InputIterator>::value_type   Point_2;
   typedef typename Kernel_traits<Point_2>::Kernel     K;
   return convex_partition_is_valid_2(point_first, point_last,
                                      poly_first, poly_last,
                                      Partition_traits_2<K>());
}


template<class InputIterator, class ForwardIterator, class Traits>
bool
y_monotone_partition_is_valid_2(InputIterator point_first,
                                InputIterator point_last,
                                ForwardIterator poly_first,
                                ForwardIterator poly_last,
                                const Traits& traits)
{
   typedef typename Traits::Is_y_monotone_2                Is_y_monotone_2;

   Partition_is_valid_traits_2<Traits, Is_y_monotone_2>    validity_traits(traits);

   return internal::partition_is_valid_2(point_first, point_last, poly_first, poly_last,
                                         validity_traits);
}

template<class InputIterator, class ForwardIterator>
bool
y_monotone_partition_is_valid_2(InputIterator point_first,
                                InputIterator point_last,
                                ForwardIterator poly_first,
                                ForwardIterator poly_last)
{
   typedef typename std::iterator_traits<InputIterator>::value_type   Point_2;
   typedef typename Kernel_traits<Point_2>::Kernel   K;
   return y_monotone_partition_is_valid_2(point_first, point_last,
                                          poly_first, poly_last,
                                          Partition_traits_2<K>());
}

}

#endif // CGAL_PARTITION_IS_VALID_2_H
