/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2015-2019 CERN
 * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
 * @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
 *
 * This program is free software; 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 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#ifndef __SHAPE_POLY_SET_H
#define __SHAPE_POLY_SET_H

#include <cstdio>
#include <deque>                        // for deque
#include <iosfwd>                       // for string, stringstream
#include <memory>
#include <set>                          // for set
#include <stdexcept>                    // for out_of_range
#include <stdlib.h>                     // for abs
#include <vector>

#include <clipper.hpp>                  // for ClipType, PolyTree (ptr only)
#include <geometry/seg.h>               // for SEG
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <math/box2.h>                  // for BOX2I
#include <math/vector2d.h>              // for VECTOR2I
#include <md5_hash.h>


/**
 * SHAPE_POLY_SET
 *
 * Represents a set of closed polygons. Polygons may be nonconvex, self-intersecting
 * and have holes. Provides boolean operations (using Clipper library as the backend).
 *
 * Let us define the terms used on this class to clarify methods names and comments:
 *      - Polygon: each polygon in the set.
 *      - Outline: first polyline in each polygon; represents its outer contour.
 *      - Hole: second and following polylines in the polygon.
 *      - Contour: each polyline of each polygon in the set, whether or not it is an
 *      outline or a hole.
 *      - Vertex (or corner): each one of the points that define a contour.
 *
 * TODO: add convex partitioning & spatial index
 */
class SHAPE_POLY_SET : public SHAPE
{
    public:
        ///> represents a single polygon outline with holes. The first entry is the outline,
        ///> the remaining (if any), are the holes
        ///> N.B. SWIG only supports typedef, so avoid c++ 'using' keyword
        typedef std::vector<SHAPE_LINE_CHAIN> POLYGON;

        class TRIANGULATED_POLYGON
        {
        public:
            struct TRI
            {
                TRI( int _a = 0, int _b = 0, int _c = 0 ) : a( _a ), b( _b ), c( _c )
                {
                }

                int a, b, c;
            };

            void Clear()
            {
                m_vertices.clear();
                m_triangles.clear();
            }

            void GetTriangle( int index, VECTOR2I& a, VECTOR2I& b, VECTOR2I& c ) const
            {
                auto tri = m_triangles[ index ];
                a = m_vertices[ tri.a ];
                b = m_vertices[ tri.b ];
                c = m_vertices[ tri.c ];
            }

            void AddTriangle( const TRI& aTri )
            {
                m_triangles.push_back( aTri );
            }

            void AddTriangle( int a, int b, int c )
            {
                m_triangles.emplace_back( a, b, c );
            }

            void AddVertex( const VECTOR2I& aP )
            {
                m_vertices.push_back( aP );
            }

            size_t GetTriangleCount() const
            {
                return m_triangles.size();
            }

            size_t GetVertexCount() const
            {
                return m_vertices.size();
            }

        private:

            std::deque<TRI> m_triangles;
            std::deque<VECTOR2I> m_vertices;
        };

        /**
         * Struct VERTEX_INDEX
         *
         * Structure to hold the necessary information in order to index a vertex on a
         * SHAPE_POLY_SET object: the polygon index, the contour index relative to the polygon and
         * the vertex index relative the contour.
         */
        typedef struct VERTEX_INDEX
        {
            int m_polygon;   /*!< m_polygon is the index of the polygon. */
            int m_contour;   /*!< m_contour is the index of the contour relative to the polygon. */
            int m_vertex;    /*!< m_vertex is the index of the vertex relative to the contour. */

            VERTEX_INDEX() : m_polygon(-1), m_contour(-1), m_vertex(-1)
            {
            }
        } VERTEX_INDEX;

        /**
         * ITERATOR_TEMPLATE
         *
         * Base class for iterating over all vertices in a given SHAPE_POLY_SET.
         */
        template <class T>
        class ITERATOR_TEMPLATE
        {
        public:

            /**
             * Function IsEndContour.
             * @return bool - true if the current vertex is the last one of the current contour
             *              (outline or hole); false otherwise.
             */
            bool IsEndContour() const
            {
                return m_currentVertex + 1 == m_poly->CPolygon( m_currentPolygon )[m_currentContour].PointCount();
            }

            /**
             * Function IsLastOutline.
             * @return bool - true if the current outline is the last one; false otherwise.
             */
            bool IsLastPolygon() const
            {
                return m_currentPolygon == m_lastPolygon;
            }

            operator bool() const
            {
                if( m_currentPolygon < m_lastPolygon )
                    return true;

                if( m_currentPolygon != m_poly->OutlineCount() - 1 )
                    return false;

                const auto& currentPolygon = m_poly->CPolygon( m_currentPolygon );

                return m_currentContour < (int) currentPolygon.size() - 1
                           || m_currentVertex < currentPolygon[m_currentContour].PointCount();
            }

            /**
             * Function Advance
             * advances the indices of the current vertex/outline/contour, checking whether the
             * vertices in the holes have to be iterated through
             */
            void Advance()
            {
                // Advance vertex index
                m_currentVertex ++;

                // Check whether the user wants to iterate through the vertices of the holes
                // and behave accordingly
                if( m_iterateHoles )
                {
                    // If the last vertex of the contour was reached, advance the contour index
                    if( m_currentVertex >= m_poly->CPolygon( m_currentPolygon )[m_currentContour].PointCount() )
                    {
                        m_currentVertex = 0;
                        m_currentContour++;

                        // If the last contour of the current polygon was reached, advance the
                        // outline index
                        int totalContours = m_poly->CPolygon( m_currentPolygon ).size();

                        if( m_currentContour >= totalContours )
                        {
                            m_currentContour = 0;
                            m_currentPolygon++;
                        }
                    }
                }
                else
                {
                    // If the last vertex of the outline was reached, advance to the following polygon
                    if( m_currentVertex >= m_poly->CPolygon( m_currentPolygon )[0].PointCount() )
                    {
                        m_currentVertex = 0;
                        m_currentPolygon++;
                    }
                }
            }

            void operator++( int dummy )
            {
                Advance();
            }

            void operator++()
            {
                Advance();
            }

            const T& Get()
            {
                return m_poly->Polygon( m_currentPolygon )[m_currentContour].CPoint(
                        m_currentVertex );
            }

            const T& operator*()
            {
                return Get();
            }

            const T* operator->()
            {
                return &Get();
            }

            /**
             * Function GetIndex
             * @return VERTEX_INDEX - indices of the current polygon, contour and vertex.
             */
            VERTEX_INDEX GetIndex()
            {
                VERTEX_INDEX index;

                index.m_polygon = m_currentPolygon;
                index.m_contour = m_currentContour;
                index.m_vertex = m_currentVertex;

                return index;
            }


        private:
            friend class SHAPE_POLY_SET;

            SHAPE_POLY_SET* m_poly;
            int m_currentPolygon;
            int m_currentContour;
            int m_currentVertex;
            int m_lastPolygon;
            bool m_iterateHoles;
        };

        /**
         * SEGMENT_ITERATOR_TEMPLATE
         *
         * Base class for iterating over all segments in a given SHAPE_POLY_SET.
         */
        template <class T>
        class SEGMENT_ITERATOR_TEMPLATE
        {
        public:
            /**
             * Function IsLastOutline.
             * @return bool - true if the current outline is the last one.
             */
            bool IsLastPolygon() const
            {
                return m_currentPolygon == m_lastPolygon;
            }

            operator bool() const
            {
                return m_currentPolygon <= m_lastPolygon;
            }

            /**
             * Function Advance
             * advances the indices of the current vertex/outline/contour, checking whether the
             * vertices in the holes have to be iterated through
             */
            void Advance()
            {
                // Advance vertex index
                m_currentSegment++;
                int last;

                // Check whether the user wants to iterate through the vertices of the holes
                // and behave accordingly
                if( m_iterateHoles )
                {
                    last = m_poly->CPolygon( m_currentPolygon )[m_currentContour].SegmentCount();

                    // If the last vertex of the contour was reached, advance the contour index
                    if( m_currentSegment >= last )
                    {
                        m_currentSegment = 0;
                        m_currentContour++;

                        // If the last contour of the current polygon was reached, advance the
                        // outline index
                        int totalContours = m_poly->CPolygon( m_currentPolygon ).size();

                        if( m_currentContour >= totalContours )
                        {
                            m_currentContour = 0;
                            m_currentPolygon++;
                        }
                    }
                }
                else
                {
                    last = m_poly->CPolygon( m_currentPolygon )[0].SegmentCount();
                    // If the last vertex of the outline was reached, advance to the following
                    // polygon
                    if( m_currentSegment >= last )
                    {
                        m_currentSegment = 0;
                        m_currentPolygon++;
                    }
                }
            }

            void operator++( int dummy )
            {
                Advance();
            }

            void operator++()
            {
                Advance();
            }

            T Get()
            {
                return m_poly->Polygon( m_currentPolygon )[m_currentContour].Segment( m_currentSegment );
            }

            T operator*()
            {
                return Get();
            }

            /**
             * Function GetIndex
             * @return VERTEX_INDEX - indices of the current polygon, contour and vertex.
             */
            VERTEX_INDEX GetIndex()
            {
                VERTEX_INDEX index;

                index.m_polygon = m_currentPolygon;
                index.m_contour = m_currentContour;
                index.m_vertex = m_currentSegment;

                return index;
            }

            /**
             * Function IsAdjacent
             * @param  aOther is an iterator pointing to another segment.
             * @return        bool - true if both iterators point to the same segment of the same
             *                     contour of the same polygon of the same polygon set; false
             *                     otherwise.
             */
            bool IsAdjacent( SEGMENT_ITERATOR_TEMPLATE<T> aOther )
            {
                // Check that both iterators point to the same contour of the same polygon of the
                // same polygon set
                if( m_poly == aOther.m_poly && m_currentPolygon == aOther.m_currentPolygon &&
                    m_currentContour == aOther.m_currentContour )
                {
                    // Compute the total number of segments
                    int numSeg;
                    numSeg = m_poly->CPolygon( m_currentPolygon )[m_currentContour].SegmentCount();

                    // Compute the difference of the segment indices. If it is exactly one, they
                    // are adjacent. The only missing case where they also are adjacent is when
                    // the segments are the first and last one, in which case the difference
                    // always equals the total number of segments minus one.
                    int indexDiff = abs( m_currentSegment - aOther.m_currentSegment );

                    return ( indexDiff == 1 ) || ( indexDiff == (numSeg - 1) );
                }

                return false;
            }

        private:
            friend class SHAPE_POLY_SET;

            SHAPE_POLY_SET* m_poly;
            int m_currentPolygon;
            int m_currentContour;
            int m_currentSegment;
            int m_lastPolygon;
            bool m_iterateHoles;
        };

        // Iterator and const iterator types to visit polygon's points.
        typedef ITERATOR_TEMPLATE<VECTOR2I> ITERATOR;
        typedef ITERATOR_TEMPLATE<const VECTOR2I> CONST_ITERATOR;

        // Iterator and const iterator types to visit polygon's edges.
        typedef SEGMENT_ITERATOR_TEMPLATE<SEG> SEGMENT_ITERATOR;
        typedef SEGMENT_ITERATOR_TEMPLATE<const SEG> CONST_SEGMENT_ITERATOR;

        SHAPE_POLY_SET();

        /**
         * Copy constructor SHAPE_POLY_SET
         * Performs a deep copy of \p aOther into \p this.
         * @param aOther is the SHAPE_POLY_SET object that will be copied.
         * @param aDeepCopy if true, make new copies of the triangulated unique_ptr vector
         */
        SHAPE_POLY_SET( const SHAPE_POLY_SET& aOther, bool aDeepCopy = false );

        ~SHAPE_POLY_SET();

        /**
         * Function GetRelativeIndices
         *
         * Converts a global vertex index ---i.e., a number that globally identifies a vertex in a
         * concatenated list of all vertices in all contours--- and get the index of the vertex
         * relative to the contour relative to the polygon in which it is.
         * @param  aGlobalIdx  is the global index of the corner whose structured index wants to
         *                     be found
         * @param  aRelativeIndices is a pointer to the set of relative indices to store.
         * @return bool - true if the global index is correct and the information in
         *              aRelativeIndices is valid; false otherwise.
         */
        bool GetRelativeIndices( int aGlobalIdx, VERTEX_INDEX* aRelativeIndices) const;

        /**
         * Function GetGlobalIndex
         * computes the global index of a vertex from the relative indices of polygon, contour and
         * vertex.
         * @param  aRelativeIndices is the set of relative indices.
         * @param  aGlobalIdx       [out] is the computed global index.
         * @return bool - true if the relative indices are correct; false otherwise. The computed
         *              global index is returned in the \p aGlobalIdx reference.
         */
        bool GetGlobalIndex( VERTEX_INDEX aRelativeIndices, int& aGlobalIdx );

        /// @copydoc SHAPE::Clone()
        SHAPE* Clone() const override;

        ///> Creates a new empty polygon in the set and returns its index
        int NewOutline();

        ///> Creates a new hole in a given outline
        int NewHole( int aOutline = -1 );

        ///> Adds a new outline to the set and returns its index
        int AddOutline( const SHAPE_LINE_CHAIN& aOutline );

        ///> Adds a new hole to the given outline (default: last) and returns its index
        int AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline = -1 );

        ///> Appends a vertex at the end of the given outline/hole (default: the last outline)
        /**
         * Function Append
         * adds a new vertex to the contour indexed by \p aOutline and \p aHole (defaults to the
         * outline of the last polygon).
         * @param  x                 is the x coordinate of the new vertex.
         * @param  y                 is the y coordinate of the new vertex.
         * @param  aOutline          is the index of the polygon.
         * @param  aHole             is the index of the hole (-1 for the main outline),
         * @param  aAllowDuplication is a flag to indicate whether it is allowed to add this
         *                           corner even if it is duplicated.
         * @return int - the number of corners of the selected contour after the addition.
         */
        int Append( int x, int y, int aOutline = -1, int aHole = -1,
                    bool aAllowDuplication = false );

        ///> Merges polygons from two sets.
        void Append( const SHAPE_POLY_SET& aSet );

        ///> Appends a vertex at the end of the given outline/hole (default: the last outline)
        void Append( const VECTOR2I& aP, int aOutline = -1, int aHole = -1 );

        /**
         * Function InsertVertex
         * Adds a vertex in the globally indexed position aGlobalIndex.
         * @param aGlobalIndex is the global index of the position in which teh new vertex will be
         *                     inserted.
         * @param aNewVertex   is the new inserted vertex.
         */
        void InsertVertex( int aGlobalIndex, VECTOR2I aNewVertex );

        ///> Returns the index-th vertex in a given hole outline within a given outline
        const VECTOR2I& CVertex( int aIndex, int aOutline, int aHole ) const;

        ///> Returns the aGlobalIndex-th vertex in the poly set
        const VECTOR2I& CVertex( int aGlobalIndex ) const;

        ///> Returns the index-th vertex in a given hole outline within a given outline
        const VECTOR2I& CVertex( VERTEX_INDEX aIndex ) const;

        /**
         * Returns the global indexes of the previous and the next corner
         * of the aGlobalIndex-th corner of a contour in the polygon set.
         * they are often aGlobalIndex-1 and aGlobalIndex+1, but not for the first and last
         * corner of the contour.
         * @param  aGlobalIndex is index of the corner, globally indexed between all edges in all
         *                      contours
         * @param aPrevious - the globalIndex of the previous corner of the same contour.
         * @param aNext - the globalIndex of the next corner of the same contour.
         * @return true if OK, false if aGlobalIndex is out of range
         */
        bool GetNeighbourIndexes( int aGlobalIndex, int* aPrevious, int* aNext );


        /**
         * Function IsPolygonSelfIntersecting.
         * Checks whether the aPolygonIndex-th polygon in the set is self intersecting.
         * @param  aPolygonIndex index of the polygon that wants to be checked.
         * @return bool - true if the aPolygonIndex-th polygon is self intersecting, false
         *              otherwise.
         */
        bool IsPolygonSelfIntersecting( int aPolygonIndex ) const;

        /**
         * Function IsSelfIntersecting
         * Checks whether any of the polygons in the set is self intersecting.
         * @return bool - true if any of the polygons is self intersecting, false otherwise.
         */
        bool IsSelfIntersecting() const;

        ///> Returns the number of triangulated polygons
        unsigned int TriangulatedPolyCount() const { return m_triangulatedPolys.size(); }

        ///> Returns the number of outlines in the set
        int OutlineCount() const { return m_polys.size(); }

        ///> Returns the number of vertices in a given outline/hole
        int VertexCount( int aOutline = -1, int aHole = -1 ) const;

        ///> Returns the number of holes in a given outline
        int HoleCount( int aOutline ) const
        {
            if( ( aOutline < 0 ) || (aOutline >= (int)m_polys.size()) || (m_polys[aOutline].size() < 2) )
                return 0;

            // the first polygon in m_polys[aOutline] is the main contour,
            // only others are holes:
            return m_polys[aOutline].size() - 1;
        }

        ///> Returns the reference to aIndex-th outline in the set
        SHAPE_LINE_CHAIN& Outline( int aIndex )
        {
            return m_polys[aIndex][0];
        }

        /**
         * Function Subset
         * returns a subset of the polygons in this set, the ones between aFirstPolygon and
         * aLastPolygon.
         * @param  aFirstPolygon is the first polygon to be included in the returned set.
         * @param  aLastPolygon  is the first polygon to be excluded of the returned set.
         * @return SHAPE_POLY_SET - a set containing the polygons between aFirstPolygon (included)
         *                        and aLastPolygon (excluded).
         */
        SHAPE_POLY_SET Subset( int aFirstPolygon, int aLastPolygon );

        SHAPE_POLY_SET UnitSet( int aPolygonIndex )
        {
            return Subset( aPolygonIndex, aPolygonIndex + 1 );
        }

        ///> Returns the reference to aHole-th hole in the aIndex-th outline
        SHAPE_LINE_CHAIN& Hole( int aOutline, int aHole )
        {
            return m_polys[aOutline][aHole + 1];
        }

        ///> Returns the aIndex-th subpolygon in the set
        POLYGON& Polygon( int aIndex )
        {
            return m_polys[aIndex];
        }

        const POLYGON& Polygon( int aIndex ) const
        {
            return m_polys[aIndex];
        }

        const TRIANGULATED_POLYGON* TriangulatedPolygon( int aIndex ) const
        {
            return m_triangulatedPolys[aIndex].get();
        }

        const SHAPE_LINE_CHAIN& COutline( int aIndex ) const
        {
            return m_polys[aIndex][0];
        }

        const SHAPE_LINE_CHAIN& CHole( int aOutline, int aHole ) const
        {
            return m_polys[aOutline][aHole + 1];
        }

        const POLYGON& CPolygon( int aIndex ) const
        {
            return m_polys[aIndex];
        }

        /**
         * Function Iterate
         * returns an object to iterate through the points of the polygons between \p aFirst and
         * \p aLast.
         * @param  aFirst        is the first polygon whose points will be iterated.
         * @param  aLast         is the last polygon whose points will be iterated.
         * @param  aIterateHoles is a flag to indicate whether the points of the holes should be
         *                       iterated.
         * @return ITERATOR - the iterator object.
         */
        ITERATOR Iterate( int aFirst, int aLast, bool aIterateHoles = false )
        {
            ITERATOR iter;

            iter.m_poly = this;
            iter.m_currentPolygon = aFirst;
            iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast;
            iter.m_currentContour = 0;
            iter.m_currentVertex = 0;
            iter.m_iterateHoles = aIterateHoles;

            return iter;
        }

        /**
         * Function Iterate
         * @param  aOutline the index of the polygon to be iterated.
         * @return ITERATOR - an iterator object to visit all points in the main outline of the
         *                  aOutline-th polygon, without visiting the points in the holes.
         */
        ITERATOR Iterate( int aOutline )
        {
            return Iterate( aOutline, aOutline );
        }

        /**
         * Function IterateWithHoles
         * @param  aOutline the index of the polygon to be iterated.
         * @return ITERATOR - an iterator object to visit all points in the main outline of the
         *                  aOutline-th polygon, visiting also the points in the holes.
         */
        ITERATOR IterateWithHoles( int aOutline )
        {
            return Iterate( aOutline, aOutline, true );
        }

        /**
         * Function Iterate
         * @return ITERATOR - an iterator object to visit all points in all outlines of the set,
         *                  without visiting the points in the holes.
         */
        ITERATOR Iterate()
        {
            return Iterate( 0, OutlineCount() - 1 );
        }

        /**
         * Function IterateWithHoles
         * @return ITERATOR - an iterator object to visit all points in all outlines of the set,
         *                  visiting also the points in the holes.
         */
        ITERATOR IterateWithHoles()
        {
            return Iterate( 0, OutlineCount() - 1, true );
        }


        CONST_ITERATOR CIterate( int aFirst, int aLast, bool aIterateHoles = false ) const
        {
            CONST_ITERATOR iter;

            iter.m_poly = const_cast<SHAPE_POLY_SET*>( this );
            iter.m_currentPolygon = aFirst;
            iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast;
            iter.m_currentContour = 0;
            iter.m_currentVertex = 0;
            iter.m_iterateHoles = aIterateHoles;

            return iter;
        }

        CONST_ITERATOR CIterate( int aOutline ) const
        {
            return CIterate( aOutline, aOutline );
        }

        CONST_ITERATOR CIterateWithHoles( int aOutline ) const
        {
            return CIterate( aOutline, aOutline, true );
        }

        CONST_ITERATOR CIterate() const
        {
            return CIterate( 0, OutlineCount() - 1 );
        }

        CONST_ITERATOR CIterateWithHoles() const
        {
            return CIterate( 0, OutlineCount() - 1, true );
        }

        ITERATOR IterateFromVertexWithHoles( int aGlobalIdx )
        {
            // Build iterator
            ITERATOR iter = IterateWithHoles();

            // Get the relative indices of the globally indexed vertex
            VERTEX_INDEX indices;

            if( !GetRelativeIndices( aGlobalIdx, &indices ) )
                throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) );

            // Adjust where the iterator is pointing
            iter.m_currentPolygon = indices.m_polygon;
            iter.m_currentContour = indices.m_contour;
            iter.m_currentVertex = indices.m_vertex;

            return iter;
        }

        ///> Returns an iterator object, for iterating between aFirst and aLast outline, with or
        /// without holes (default: without)
        SEGMENT_ITERATOR IterateSegments( int aFirst, int aLast, bool aIterateHoles = false )
        {
            SEGMENT_ITERATOR iter;

            iter.m_poly = this;
            iter.m_currentPolygon = aFirst;
            iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast;
            iter.m_currentContour = 0;
            iter.m_currentSegment = 0;
            iter.m_iterateHoles = aIterateHoles;

            return iter;
        }

        ///> Returns an iterator object, for iterating between aFirst and aLast outline, with or
        /// without holes (default: without)
        CONST_SEGMENT_ITERATOR CIterateSegments( int aFirst, int aLast,
                                                 bool aIterateHoles = false ) const
        {
            CONST_SEGMENT_ITERATOR iter;

            iter.m_poly = const_cast<SHAPE_POLY_SET*>( this );
            iter.m_currentPolygon = aFirst;
            iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast;
            iter.m_currentContour = 0;
            iter.m_currentSegment = 0;
            iter.m_iterateHoles = aIterateHoles;

            return iter;
        }

        ///> Returns an iterator object, for iterating aPolygonIdx-th polygon edges
        SEGMENT_ITERATOR IterateSegments( int aPolygonIdx )
        {
            return IterateSegments( aPolygonIdx, aPolygonIdx );
        }

        ///> Returns an iterator object, for iterating aPolygonIdx-th polygon edges
        CONST_SEGMENT_ITERATOR CIterateSegments( int aPolygonIdx ) const
        {
            return CIterateSegments( aPolygonIdx, aPolygonIdx );
        }

        ///> Returns an iterator object, for all outlines in the set (no holes)
        SEGMENT_ITERATOR IterateSegments()
        {
            return IterateSegments( 0, OutlineCount() - 1 );
        }

        ///> Returns an iterator object, for all outlines in the set (with holes)
        SEGMENT_ITERATOR IterateSegmentsWithHoles()
        {
            return IterateSegments( 0, OutlineCount() - 1, true );
        }

        ///> Returns an iterator object, for the aOutline-th outline in the set (with holes)
        SEGMENT_ITERATOR IterateSegmentsWithHoles( int aOutline )
        {
            return IterateSegments( aOutline, aOutline, true );
        }

        ///> Returns an iterator object, for the aOutline-th outline in the set (with holes)
        CONST_SEGMENT_ITERATOR CIterateSegmentsWithHoles( int aOutline ) const
        {
            return CIterateSegments( aOutline, aOutline, true );
        }

        /** operations on polygons use a aFastMode param
         * if aFastMode is PM_FAST (true) the result can be a weak polygon
         * if aFastMode is PM_STRICTLY_SIMPLE (false) (default) the result is (theorically) a strictly
         * simple polygon, but calculations can be really significantly time consuming
         * Most of time PM_FAST is preferable.
         * PM_STRICTLY_SIMPLE can be used in critical cases (Gerber output for instance)
         */
        enum POLYGON_MODE
        {
            PM_FAST = true,
            PM_STRICTLY_SIMPLE = false
        };

        ///> Performs boolean polyset union
        ///> For aFastMode meaning, see function booleanOp
        void BooleanAdd( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode );

        ///> Performs boolean polyset difference
        ///> For aFastMode meaning, see function booleanOp
        void BooleanSubtract( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode );

        ///> Performs boolean polyset intersection
        ///> For aFastMode meaning, see function booleanOp
        void BooleanIntersection( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode );

        ///> Performs boolean polyset union between a and b, store the result in it self
        ///> For aFastMode meaning, see function booleanOp
        void BooleanAdd( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b,
                         POLYGON_MODE aFastMode );

        ///> Performs boolean polyset difference between a and b, store the result in it self
        ///> For aFastMode meaning, see function booleanOp
        void BooleanSubtract( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b,
                              POLYGON_MODE aFastMode );

        ///> Performs boolean polyset intersection between a and b, store the result in it self
        ///> For aFastMode meaning, see function booleanOp
        void BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b,
                                  POLYGON_MODE aFastMode );

        enum CORNER_STRATEGY    ///< define how inflate transform build inflated polygon
        {
            ALLOW_ACUTE_CORNERS,    ///< just inflate the polygon. Acute angles create spikes
            CHAMFER_ACUTE_CORNERS,  ///< Acute angles are chamfered
            ROUND_ACUTE_CORNERS,    ///< Acute angles are rounded
            CHAMFER_ALL_CORNERS,    ///< All angles are chamfered.
                                    ///< The distance between new and old polygon edges is not
                                    ///< constant, but do not change a lot
            ROUND_ALL_CORNERS       ///< All angles are rounded.
                                    ///< The distance between new and old polygon edges is constant
        };

        /**
         * Performs outline inflation/deflation.  Polygons can have holes, but not linked holes
         * with main outlines, if aFactor < 0.  For those use InflateWithLinkedHoles() to avoid
         * odd corners where the link segments meet the outline.
         *
         * @param aAmount - number of units to offset edges
         * @param aCircleSegmentsCount - number of segments per 360° to use in curve approx
         * @param aCornerStrategy - ALLOW_ACUTE_CORNERS to preserve all angles,
         *                          CHOP_ACUTE_CORNERS to chop angles less than 90°,
         *                          ROUND_ACUTE_CORNERS to round off angles less than 90°,
         *                          ROUND_ALL_CORNERS to round regardless of angles
         */
        void Inflate( int aAmount, int aCircleSegmentsCount,
                      CORNER_STRATEGY aCornerStrategy = ROUND_ALL_CORNERS );

        void Deflate( int aAmount, int aCircleSegmentsCount,
                      CORNER_STRATEGY aCornerStrategy = ROUND_ALL_CORNERS )
        {
            Inflate( -aAmount, aCircleSegmentsCount, aCornerStrategy );
        }

        /**
         * Performs outline inflation/deflation, using round corners.  Polygons can have holes,
         * and/or linked holes with main outlines.  The resulting polygons are laso polygons with
         * linked holes to main outlines.  For aFastMode meaning, see function booleanOp  .
         */
        void InflateWithLinkedHoles( int aFactor, int aCircleSegmentsCount, POLYGON_MODE aFastMode );

        ///> Converts a set of polygons with holes to a singe outline with "slits"/"fractures"
        ///> connecting the outer ring to the inner holes
        ///> For aFastMode meaning, see function booleanOp
        void Fracture( POLYGON_MODE aFastMode );

        ///> Converts a single outline slitted ("fractured") polygon into a set ouf outlines
        ///> with holes.
        void Unfracture( POLYGON_MODE aFastMode );

        ///> Returns true if the polygon set has any holes.
        bool HasHoles() const;

        ///> Returns true if the polygon set has any holes tha share a vertex.
        bool HasTouchingHoles() const;


        ///> Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
        ///> For aFastMode meaning, see function booleanOp
        void Simplify( POLYGON_MODE aFastMode );

        /**
         * Function NormalizeAreaOutlines
         * Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s)
         * Removes null segments.
         * @return int - the polygon count (always >= 1, because there is at least one polygon)
         *             There are new polygons only if the polygon count is > 1.
         */
        int NormalizeAreaOutlines();

        /// @copydoc SHAPE::Format()
        const std::string Format() const override;

        /// @copydoc SHAPE::Parse()
        bool Parse( std::stringstream& aStream ) override;

        /// @copydoc SHAPE::Move()
        void Move( const VECTOR2I& aVector ) override;

        /**
         * Mirrors the line points about y or x (or both)
         * @param aX If true, mirror about the y axis (flip x coordinate)
         * @param aY If true, mirror about the x axis
         * @param aRef sets the reference point about which to mirror
         */
        void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aRef = { 0, 0 } );

        /**
         * Function Rotate
         * rotates all vertices by a given angle
         * @param aCenter is the rotation center
         * @param aAngle rotation angle in radians
         */
        void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } );

        /// @copydoc SHAPE::IsSolid()
        bool IsSolid() const override
        {
            return true;
        }

        const BOX2I BBox( int aClearance = 0 ) const override;

        /**
         * Function PointOnEdge()
         *
         * Checks if point aP lies on an edge or vertex of some of the outlines or holes.
         * @param aP is the point to check.
         * @return bool - true if the point lies on the edge of any polygon.
         */
        bool PointOnEdge( const VECTOR2I& aP ) const;

        /**
         * Function Collide
         * Checks whether the point aP collides with the inside of the polygon set; if the point
         * lies on an edge or on a corner of any of the polygons, there is no collision: the edges
         * does not belong to the polygon itself.
         * @param  aP         is the VECTOR2I point whose collision with respect to the poly set
         *                    will be tested.
         * @param  aClearance is the security distance; if the point lies closer to the polygon
         *                    than aClearance distance, then there is a collision.
         * @return bool - true if the point aP collides with the polygon; false in any other case.
         */
        bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const override;

        /**
         * Function Collide
         * Checks whether the segment aSeg collides with the inside of the polygon set;  if the
         * segment touches an edge or a corner of any of the polygons, there is no collision:
         * the edges do not belong to the polygon itself.
         * @param  aSeg       is the SEG segment whose collision with respect to the poly set
         *                    will be tested.
         * @param  aClearance is the security distance; if the segment passes closer to the polygon
         *                    than aClearance distance, then there is a collision.
         * @return bool - true if the segment aSeg collides with the polygon;
         *                    false in any other case.
         */
        bool Collide( const SEG& aSeg, int aClearance = 0 ) const override;

        /**
         * Function CollideVertex
         * Checks whether aPoint collides with any vertex of any of the contours of the polygon.
         * @param  aPoint     is the VECTOR2I point whose collision with respect to the polygon
         *                    will be tested.
         * @param  aClearance is the security distance; if \p aPoint lies closer to a vertex than
         *                    aClearance distance, then there is a collision.
         * @param aClosestVertex is the index of the closes vertex to \p aPoint.
         * @return bool - true if there is a collision, false in any other case.
         */
        bool CollideVertex( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex,
                            int aClearance = 0 );

        /**
         * Function CollideEdge
         * Checks whether aPoint collides with any edge of any of the contours of the polygon.
         * @param  aPoint     is the VECTOR2I point whose collision with respect to the polygon
         *                    will be tested.
         * @param  aClearance is the security distance; if \p aPoint lies closer to a vertex than
         *                    aClearance distance, then there is a collision.
         * @param aClosestVertex is the index of the closes vertex to \p aPoint.
         * @return bool - true if there is a collision, false in any other case.
         */
        bool CollideEdge( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex,
                          int aClearance = 0 );

        /**
         * Constructs BBoxCaches for Contains(), below.  These caches MUST be built before a
         * group of calls to Contains().  They are NOT kept up-to-date by editing actions.
         */
        void BuildBBoxCaches();

        /**
         * Returns true if a given subpolygon contains the point aP
         *
         * @param aP is the point to check
         * @param aSubpolyIndex is the subpolygon to check, or -1 to check all
         * @param aUseBBoxCaches gives faster performance when multiple calls are made with no
         *                       editing in between, but the caller MUST cache the bbox caches
         *                       before calling (via BuildBBoxCaches(), above)
         * @return true if the polygon contains the point
         */
        bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1, int aAccuracy = 0,
                       bool aUseBBoxCaches = false ) const;

        ///> Returns true if the set is empty (no polygons at all)
        bool IsEmpty() const
        {
            return m_polys.size() == 0;
        }

        /**
         * Function RemoveVertex
         * deletes the aGlobalIndex-th vertex.
         * @param aGlobalIndex is the global index of the to-be-removed vertex.
         */
        void RemoveVertex( int aGlobalIndex );

        /**
         * Function RemoveVertex
         * deletes the vertex indexed by aIndex (index of polygon, contour and vertex).
         * @param aRelativeIndices is the set of relative indices of the to-be-removed vertex.
         */
        void RemoveVertex( VERTEX_INDEX aRelativeIndices );

        ///> Removes all outlines & holes (clears) the polygon set.
        void RemoveAllContours();

        /**
         * Function RemoveContour
         * deletes the aContourIdx-th contour of the aPolygonIdx-th polygon in the set.
         * @param aContourIdx is the index of the contour in the aPolygonIdx-th polygon to be
         *                    removed.
         * @param aPolygonIdx is the index of the polygon in which the to-be-removed contour is.
         *                    Defaults to the last polygon in the set.
         */
        void RemoveContour( int aContourIdx, int aPolygonIdx = -1 );

        /**
         * Function RemoveNullSegments
         * looks for null segments; ie, segments whose ends are exactly the same and deletes them.
         * @return int - the number of deleted segments.
         */
        int RemoveNullSegments();

        /**
         * Function SetVertex
         * Accessor function to set the position of a specific point
         * @param aIndex VERTEX_INDEX of the point to move
         * @param aPos destination position of the specified point
         */
        void SetVertex( const VERTEX_INDEX& aIndex, const VECTOR2I& aPos );

        /**
         * Sets the vertex based on the global index.  Throws if the index
         * doesn't exist
         * @param aGlobalIndex global index of the to-be-moved vertex
         * @param aPos New position on the vertex
         */
        void SetVertex( int aGlobalIndex, const VECTOR2I& aPos );

        ///> Returns total number of vertices stored in the set.
        int TotalVertices() const;

        ///> Deletes aIdx-th polygon from the set
        void DeletePolygon( int aIdx );

        /**
         * Function Chamfer
         * returns a chamfered version of the aIndex-th polygon.
         * @param aDistance is the chamfering distance.
         * @param aIndex is the index of the polygon to be chamfered.
         * @param aPreserveCorners an optional set of corners which should not be chamfered.
         * @return POLYGON - A polygon containing the chamfered version of the aIndex-th polygon.
         */
        POLYGON ChamferPolygon( unsigned int aDistance, int aIndex,
                                std::set<VECTOR2I>* aPreserveCorners );

        /**
         * Function Fillet
         * returns a filleted version of the aIndex-th polygon.
         * @param aRadius is the fillet radius.
         * @param aErrorMax is the maximum allowable deviation of the polygon from the circle
         * @param aIndex is the index of the polygon to be filleted
         * @param aPreserveCorners an optional set of corners which should not be filleted.
         * @return POLYGON - A polygon containing the filleted version of the aIndex-th polygon.
         */
        POLYGON FilletPolygon( unsigned int aRadius, int aErrorMax, int aIndex,
                               std::set<VECTOR2I>* aPreserveCorners = nullptr );

        /**
         * Function Chamfer
         * returns a chamfered version of the polygon set.
         * @param aDistance is the chamfering distance.
         * @param aPreserveCorners an optional set of corners which should not be chamfered.
         * @return SHAPE_POLY_SET - A set containing the chamfered version of this set.
         */
        SHAPE_POLY_SET Chamfer( int aDistance,
                                std::set<VECTOR2I>* aPreserveCorners = nullptr );

        /**
         * Function Fillet
         * returns a filleted version of the polygon set.
         * @param aRadius is the fillet radius.
         * @param aErrorMax is the maximum allowable deviation of the polygon from the circle
         * @param aPreserveCorners an optional set of corners which should not be filleted.
         * @return SHAPE_POLY_SET - A set containing the filleted version of this set.
         */
        SHAPE_POLY_SET Fillet( int aRadius, int aErrorMax,
                               std::set<VECTOR2I>* aPreserveCorners = nullptr );

        /**
         * Function DistanceToPolygon
         * computes the minimum distance between the aIndex-th polygon and aPoint.
         * @param  aPoint is the point whose distance to the aIndex-th polygon has to be measured.
         * @param  aIndex is the index of the polygon whose distace to aPoint has to be measured.
         * @return int -  The minimum distance between aPoint and all the segments of the aIndex-th
         *                polygon. If the point is contained in the polygon, the distance is zero.
         */
        int DistanceToPolygon( VECTOR2I aPoint, int aIndex );

        /**
         * Function DistanceToPolygon
         * computes the minimum distance between the aIndex-th polygon and aSegment with a
         * possible width.
         * @param  aSegment is the segment whose distance to the aIndex-th polygon has to be
         *                  measured.
         * @param  aIndex   is the index of the polygon whose distace to aPoint has to be measured.
         * @param  aSegmentWidth is the width of the segment; defaults to zero.
         * @return int -    The minimum distance between aSegment and all the segments of the
         *                  aIndex-th polygon. If the point is contained in the polygon, the
         *                  distance is zero.
         */
        int DistanceToPolygon( const SEG& aSegment, int aIndex, int aSegmentWidth = 0 );

        /**
         * Function DistanceToPolygon
         * computes the minimum distance between aPoint and all the polygons in the set
         * @param  aPoint is the point whose distance to the set has to be measured.
         * @return int -  The minimum distance between aPoint and all the polygons in the set. If
         *                the point is contained in any of the polygons, the distance is zero.
         */
        int Distance( VECTOR2I aPoint );

        /**
         * Function DistanceToPolygon
         * computes the minimum distance between aSegment and all the polygons in the set.
         * @param  aSegment is the segment whose distance to the polygon set has to be measured.
         * @param  aSegmentWidth is the width of the segment; defaults to zero.
         * @return int -    The minimum distance between aSegment and all the polygons in the set.
         *                  If the point is contained in the polygon, the distance is zero.
         */
        int Distance( const SEG& aSegment, int aSegmentWidth = 0 );

        /**
         * Function IsVertexInHole.
         * checks whether the aGlobalIndex-th vertex belongs to a hole.
         * @param  aGlobalIdx is the index of the vertex.
         * @return bool - true if the globally indexed aGlobalIdx-th vertex belongs to a hole.
         */
        bool IsVertexInHole( int aGlobalIdx );

    private:
        void fractureSingle( POLYGON& paths );
        void unfractureSingle ( POLYGON& path );
        void importTree( ClipperLib::PolyTree* tree );

        /** Function booleanOp
         * this is the engine to execute all polygon boolean transforms
         * (AND, OR, ... and polygon simplification (merging overlaping  polygons)
         * @param aType is the transform type ( see ClipperLib::ClipType )
         * @param aOtherShape is the SHAPE_LINE_CHAIN to combine with me.
         * @param aFastMode is an option to choose if the result can be a weak polygon
         * or a stricty simple polygon.
         * if aFastMode is PM_FAST the result can be a weak polygon
         * if aFastMode is PM_STRICTLY_SIMPLE (default) the result is (theorically) a strictly
         * simple polygon, but calculations can be really significantly time consuming
         */
        void booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape,
                        POLYGON_MODE aFastMode );

        void booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aShape,
                        const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode );

        bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath,
                             bool aIgnoreEdges, bool aUseBBoxCaches = false ) const;

        /**
         * containsSingle function
         * Checks whether the point aP is inside the aSubpolyIndex-th polygon of the polyset. If
         * the points lies on an edge, the polygon is considered to contain it.
         * @param  aP            is the VECTOR2I point whose position with respect to the inside of
         *                       the aSubpolyIndex-th polygon will be tested.
         * @param  aSubpolyIndex is an integer specifying which polygon in the set has to be
         *                       checked.
         * @param  aAccuracy     accuracy in internal units
         * @param aUseBBoxCaches gives faster performance when multiple calls are made with no
         *                       editing in between, but the caller MUST cache the bbox caches
         *                       before calling (via BuildBBoxCaches(), above)
         * @return bool - true if aP is inside aSubpolyIndex-th polygon; false in any other
         *         case.
         */
        bool containsSingle( const VECTOR2I& aP, int aSubpolyIndex, int aAccuracy,
                             bool aUseBBoxCaches = false ) const;

        /**
         * Operations ChamferPolygon and FilletPolygon are computed under the private chamferFillet
         * method; this enum is defined to make the necessary distinction when calling this method
         * from the public ChamferPolygon and FilletPolygon methods.
         */
        enum CORNER_MODE
        {
            CHAMFERED,
            FILLETED
        };

        /**
         * Function chamferFilletPolygon
         * Returns the camfered or filleted version of the aIndex-th polygon in the set, depending
         * on the aMode selected
         * @param  aMode     represent which action will be taken: CORNER_MODE::CHAMFERED will
         *                   return a chamfered version of the polygon, CORNER_MODE::FILLETED will
         *                   return a filleted version of the polygon.
         * @param  aDistance is the chamfering distance if aMode = CHAMFERED; if aMode = FILLETED,
         *                   is the filleting radius.
         * @param  aIndex    is the index of the polygon that will be chamfered/filleted.
         * @param  aErrorMax is the maximum allowable deviation of the polygon from the circle
         *                   if aMode = FILLETED. If aMode = CHAMFERED, it is unused.
         * @param aPreserveCorners an optional set of corners which should be skipped.
         * @return POLYGON - the chamfered/filleted version of the polygon.
         */
        POLYGON chamferFilletPolygon( CORNER_MODE aMode, unsigned int aDistance,
                                      int aIndex, int aErrorMax,
                                      std::set<VECTOR2I>* aPreserveCorners );

        ///> Returns true if the polygon set has any holes that touch share a vertex.
        bool hasTouchingHoles( const POLYGON& aPoly ) const;

        typedef std::vector<POLYGON> POLYSET;

        POLYSET m_polys;

    public:

        SHAPE_POLY_SET& operator=( const SHAPE_POLY_SET& );

        void CacheTriangulation();
        bool IsTriangulationUpToDate() const;

        MD5_HASH GetHash() const;

    private:

        MD5_HASH checksum() const;

        std::vector<std::unique_ptr<TRIANGULATED_POLYGON>> m_triangulatedPolys;
        bool m_triangulationValid = false;
        MD5_HASH m_hash;

};

#endif
