// Copyright (c) 2006 Fernando Luis Cacciola Carballal. All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//

// $URL: https://github.com/CGAL/cgal/blob/v6.1.2/Straight_skeleton_2/include/CGAL/create_straight_skeleton_2.h $
// $Id: include/CGAL/create_straight_skeleton_2.h 37e2b278910 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s)     : Fernando Cacciola <fernando_cacciola@ciudad.com.ar>
//
#ifndef CGAL_CREATE_STRAIGHT_SKELETON_2_H
#define CGAL_CREATE_STRAIGHT_SKELETON_2_H

#include <CGAL/license/Straight_skeleton_2.h>

#include <CGAL/compute_outer_frame_margin.h>
#include <CGAL/Straight_skeleton_2.h>
#include <CGAL/Straight_skeleton_builder_2.h>
#include <CGAL/Straight_skeleton_2/Polygon_iterators.h>

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/Polygon_2.h>

#include <optional>
#include <memory>

#include <algorithm>
#include <iterator>
#include <vector>

namespace CGAL {

template<class PointIterator, class HoleIterator,
         class K = Exact_predicates_inexact_constructions_kernel>
std::shared_ptr< Straight_skeleton_2<K> >
create_interior_straight_skeleton_2 ( PointIterator aOuterContour_VerticesBegin
                                    , PointIterator aOuterContour_VerticesEnd
                                    , HoleIterator  aHolesBegin
                                    , HoleIterator  aHolesEnd
                                    , const K& = K())
{
  typedef Straight_skeleton_2<K> Ss ;

  typedef Straight_skeleton_builder_traits_2<K> SsBuilderTraits;

  typedef Straight_skeleton_builder_2<SsBuilderTraits,Ss> SsBuilder;

  typedef typename std::iterator_traits<PointIterator>::value_type InputPoint ;
  typedef typename Kernel_traits<InputPoint>::Kernel InputKernel ;

  Cartesian_converter<InputKernel, K> Point_converter ;

  SsBuilder ssb ;

  ssb.enter_contour( aOuterContour_VerticesBegin, aOuterContour_VerticesEnd, Point_converter ) ;

  for ( HoleIterator hi = aHolesBegin ; hi != aHolesEnd ; ++ hi )
    ssb.enter_contour( CGAL_SS_i::vertices_begin(*hi), CGAL_SS_i::vertices_end(*hi), Point_converter ) ;

  return ssb.construct_skeleton();
}

template<class PointIterator,
         class K = Exact_predicates_inexact_constructions_kernel>
std::shared_ptr< Straight_skeleton_2<K> >
inline
create_interior_straight_skeleton_2 ( PointIterator aOuterContour_VerticesBegin
                                    , PointIterator aOuterContour_VerticesEnd
                                    , const K& k = K())
{
  typedef typename std::iterator_traits<PointIterator>::value_type InputPoint ;
  typedef typename Kernel_traits<InputPoint>::Kernel InputKernel ;

  std::vector< Polygon_2<InputKernel> > no_holes ;
  return create_interior_straight_skeleton_2(aOuterContour_VerticesBegin
                                            ,aOuterContour_VerticesEnd
                                            ,no_holes.begin()
                                            ,no_holes.end()
                                            ,k
                                            );
}

template<class Polygon,
         class K = Exact_predicates_inexact_constructions_kernel>
std::shared_ptr< Straight_skeleton_2<K> >
inline
create_interior_straight_skeleton_2 ( const Polygon& aOutContour,
                                      const K& k = K(),
                                      std::enable_if_t<
                                        ! CGAL_SS_i::has_Hole_const_iterator<Polygon>::value>* = nullptr)
{
  return create_interior_straight_skeleton_2(CGAL_SS_i::vertices_begin(aOutContour)
                                            ,CGAL_SS_i::vertices_end(aOutContour)
                                            ,k
                                            );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
/// EXTERIOR

template<class FT, class PointIterator,
         class K = Exact_predicates_inexact_constructions_kernel>
std::shared_ptr< Straight_skeleton_2<K> >
create_exterior_straight_skeleton_2 ( FT const&      aMaxOffset
                                    , PointIterator  aVerticesBegin
                                    , PointIterator  aVerticesEnd
                                    , const K& k = K())
{
  CGAL_precondition( aMaxOffset > 0 ) ;

  typedef typename std::iterator_traits<PointIterator>::value_type   Point_2 ;
  typedef typename Kernel_traits<Point_2>::Kernel                    IK;
  typedef typename IK::FT                                            IFT;

  std::shared_ptr<Straight_skeleton_2<K> > rSkeleton;

  // That's because we might not have FT == IK::FT (e.g. `double` and `Core`)
  // Note that we can also have IK != K (e.g. `Simple_cartesian<Core>` and `EPICK`)
  IFT lOffset = aMaxOffset;

  // @todo This likely should be done in the kernel K rather than the input kernel (i.e. the same
  // converter stuff that is done in `create_partial_exterior_straight_skeleton_2`?).
  std::optional<IFT> margin = compute_outer_frame_margin( aVerticesBegin,
                                                            aVerticesEnd,
                                                            lOffset );

  if ( margin )
  {
    const IFT lm = *margin;
    const Bbox_2 bbox = bbox_2(aVerticesBegin, aVerticesEnd);

    const IFT fxmin = IFT(bbox.xmin()) - lm ;
    const IFT fxmax = IFT(bbox.xmax()) + lm ;
    const IFT fymin = IFT(bbox.ymin()) - lm ;
    const IFT fymax = IFT(bbox.ymax()) + lm ;

    Point_2 frame[4] ;
    frame[0] = Point_2(fxmin,fymin) ;
    frame[1] = Point_2(fxmax,fymin) ;
    frame[2] = Point_2(fxmax,fymax) ;
    frame[3] = Point_2(fxmin,fymax) ;

    typedef std::vector<Point_2> Hole ;

    Hole lPoly(aVerticesBegin, aVerticesEnd);
    std::reverse(lPoly.begin(), lPoly.end());

    std::vector<Hole> holes ;
    holes.push_back(lPoly) ;

    rSkeleton = create_interior_straight_skeleton_2(frame, frame+4, holes.begin(), holes.end(), k ) ;
  }

  return rSkeleton ;
}

template<class FT, class Polygon,
         class K = Exact_predicates_inexact_constructions_kernel>
std::shared_ptr< Straight_skeleton_2<K> >
inline
create_exterior_straight_skeleton_2(const FT& aMaxOffset,
                                    const Polygon& aPoly,
                                    const K& k = K())
{
  return create_exterior_straight_skeleton_2(aMaxOffset
                                            ,CGAL_SS_i::vertices_begin(aPoly)
                                            ,CGAL_SS_i::vertices_end  (aPoly)
                                            ,k
                                            );
}

} // namespace CGAL

#endif // CGAL_CREATE_STRAIGHT_SKELETON_2_H
