/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Few parts of this code come from  FreePCB, released under the GNU General Public License V2.
 * (see http://www.freepcb.com/ )
 *
 * Copyright (C) 2012-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
 * Copyright (C) 2008-2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
 * Copyright (C) 2008-2013 Wayne Stambaugh <stambaughw@verizon.net>
 * Copyright (C) 2012-2015 KiCad Developers, see CHANGELOG.TXT for contributors.
 *
 * 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
 */


/**
 * @file PolyLine.h
 * @note definition of CPolyLine class
 */

//
// A polyline contains one or more contours, where each contour
// is defined by a list of corners and side-styles
// There may be multiple contours in a polyline.
// The last contour may be open or closed, any others must be closed.
// All of the corners and side-styles are concatenated into 2 arrays,
// separated by setting the end_contour flag of the last corner of
// each contour.
//
// When used for copper (or technical layers) areas, the first contour is the outer edge
// of the area, subsequent ones are "holes" in the copper.

#ifndef POLYLINE_H
#define POLYLINE_H

#include <vector>

#include <wx/gdicmn.h>                          // for wxPoint definition
#include <layers_id_colors_and_visibility.h>    // for LAYER_NUM definition
#include <class_eda_rect.h>                     // for EDA_RECT definition

#include <geometry/shape_poly_set.h> // fixme

class CSegment
{
public:
    wxPoint m_Start;
    wxPoint m_End;

    CSegment() { };
    CSegment( const wxPoint& aStart, const wxPoint& aEnd )
    {
        m_Start = aStart;
        m_End   = aEnd;
    }

    CSegment( int x0, int y0, int x1, int y1 )
    {
        m_Start.x   = x0; m_Start.y = y0;
        m_End.x     = x1; m_End.y = y1;
    }
};

class CPolyPt : public wxPoint
{
public:
    CPolyPt( int aX = 0, int aY = 0, bool aEnd = false, int aUtility = 0 ) :
        wxPoint( aX, aY ), end_contour( aEnd ), m_flags( aUtility )
    {}

    // / Pure copy constructor is here to dis-ambiguate from the
    // / specialized CPolyPt( const wxPoint& ) constructor version below.
    CPolyPt( const CPolyPt& aPt ) :
        wxPoint( aPt.x, aPt.y ), end_contour( aPt.end_contour ), m_flags( aPt.m_flags )
    {}

    CPolyPt( const wxPoint& aPoint ) :
        wxPoint( aPoint ), end_contour( false ), m_flags( 0 )
    {}


    bool    end_contour;
    int     m_flags;

    bool operator ==( const CPolyPt& cpt2 ) const
    { return (x == cpt2.x) && (y == cpt2.y) && (end_contour == cpt2.end_contour); }

    bool operator !=( CPolyPt& cpt2 ) const
    { return (x != cpt2.x) || (y != cpt2.y) || (end_contour != cpt2.end_contour); }
};

/**
 * CPOLYGONS_LIST handle a list of contours (polygons corners).
 * Each corner is a CPolyPt item.
 * The last cornet of each contour has its end_contour member = true
 */
class CPOLYGONS_LIST
{
private:
    std::vector <CPolyPt> m_cornersList;    // array of points for corners
public:
    CPOLYGONS_LIST() {};

    CPolyPt& operator [](int aIdx) { return m_cornersList[aIdx]; }

    // Accessor:
    const std::vector <CPolyPt>& GetList() const {return m_cornersList;}

    int        GetX( int ic ) const { return m_cornersList[ic].x; }
    void       SetX( int ic, int aValue ) { m_cornersList[ic].x = aValue; }
    int        GetY( int ic ) const { return m_cornersList[ic].y; }
    void       SetY( int ic, int aValue ) { m_cornersList[ic].y = aValue; }

    bool       IsEndContour( int ic ) const
    {
        return m_cornersList[ic].end_contour;
    }

    const wxPoint&  GetPos( int ic ) const { return m_cornersList[ic]; }
    const CPolyPt&  GetCorner( int ic ) const { return m_cornersList[ic]; }

    // vector <> methods
    void reserve( int aSize ) { m_cornersList.reserve( aSize ); }


    void RemoveAllContours( void ) { m_cornersList.clear(); }
    CPolyPt& GetLastCorner() { return m_cornersList.back(); }

    unsigned GetCornersCount() const { return m_cornersList.size(); }

    void DeleteCorner( int aIdx )
    {
        m_cornersList.erase( m_cornersList.begin() + aIdx );
    }

    // used only to erase an entire polygon
    void DeleteCorners( int aIdFirstCorner, int aIdLastCorner )
    {
        m_cornersList.erase( m_cornersList.begin() + aIdFirstCorner,
                             m_cornersList.begin() + aIdLastCorner + 1 );
    }

    void Append( const CPOLYGONS_LIST& aList )
    {
        m_cornersList.insert( m_cornersList.end(),
                              aList.m_cornersList.begin(),
                              aList.m_cornersList.end() );
    }

    void Append( const CPolyPt& aItem )
    {
        m_cornersList.push_back( aItem );
    }

    void Append( const wxPoint& aItem )
    {
        CPolyPt item( aItem );

        m_cornersList.push_back( aItem );
    }

    void InsertCorner( int aPosition, const CPolyPt& aItem )
    {
        m_cornersList.insert( m_cornersList.begin() + aPosition + 1, aItem );
    }

    /**
     * function AddCorner
     * add a corner to the list
     */
    void    AddCorner( const CPolyPt& aCorner )
    {
        m_cornersList.push_back( aCorner );
    }

    /**
     * function CloseLastContour
     * Set the .end_contour member of the last corner in list to true
     */
    void    CloseLastContour()
    {
        if( m_cornersList.size() > 0 )
            m_cornersList.back().end_contour = true;
    }

    /**
     * Function GetContoursCount.
     * @return the number of polygons stored in list
     * (number of corners flagged "end_contour"
     */
    int GetContoursCount() const;
};

class CPolyLine
{
public:
    enum HATCH_STYLE { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE };    // hatch styles

    // constructors/destructor
    CPolyLine();
    CPolyLine( const CPolyLine& aCPolyLine);
    ~CPolyLine();

    /**
     * Function ImportSettings
     * Copy settings (layer, hatch styles) from aPoly
     * @param aPoly is the CPolyLine to import settings
     */
    void        ImportSettings( const CPolyLine* aPoly );

    // functions for modifying the CPolyLine contours

    /* initialize a contour
     * set layer, hatch style, and starting point
     */
    void        Start( LAYER_NUM layer, int x, int y, int hatch );

    void        AppendCorner( int x, int y );
    void        InsertCorner( int ic, int x, int y );

    /**
     * Function DeleteCorner
     * remove the given corner. if it is the last point of a contour
     * keep the controur closed by modifying the previous corner
     * @param ic = the index of the corner to delete
     */
    void        DeleteCorner( int ic );
    void        MoveCorner( int ic, int x, int y );

    /**
     * function CloseLastContour
     * Set the .end_contour member of the last corner
     *  of the last contour to true
     */
    void        CloseLastContour()
    {
        m_CornersList.CloseLastContour();
    }

    void        RemoveContour( int icont );

    /**
     * Function IsPolygonSelfIntersecting
     * Test a CPolyLine for self-intersection of vertex (all contours).
     *
     * @return :
     *  false if no intersecting sides
     *  true if intersecting sides
     * When a CPolyLine is self intersectic, it need to be normalized.
     * (converted to non intersecting polygons)
     */
    bool        IsPolygonSelfIntersecting();

    /**
     * Function Chamfer
     * returns a chamfered version of a polygon.
     * @param aDistance is the chamfering distance.
     * @return CPolyLine* - Pointer to new polygon.
     */
    CPolyLine*  Chamfer( unsigned int aDistance );

    /**
     * Function Fillet
     * returns a filleted version of a polygon.
     * @param aRadius is the fillet radius.
     * @param aSegments is the number of segments / fillet.
     * @return CPolyLine* - Pointer to new polygon.
     */
    CPolyLine*  Fillet( unsigned int aRadius, unsigned int aSegments );

    /**
     * Function RemoveNullSegments
     * Removes corners which create a null segment edge
     * (i.e. when 2 successive corners are at the same location)
     * @return the count of removed corners.
     */
    int         RemoveNullSegments();

    void        RemoveAllContours( void );

    // Remove or create hatch
    void        UnHatch();
    void        Hatch();

    // Transform functions
    void        MoveOrigin( int x_off, int y_off );

    // misc. functions
    /**
     * @return the full bounding box of polygons
     */
    const EDA_RECT GetBoundingBox();

    /**
     * @return the bounding box of a given polygon
     * @param icont = the index of the polygon contour
     * (0 = main contour, 1 ... n = other contours, usually holes)
     */
    const EDA_RECT GetBoundingBox( int icont );

    void        Copy( const CPolyLine* src );
    bool        TestPointInside( int x, int y );

    /**
     * @return true if the corner aCornerIdx is on a hole inside the main outline
     * and false if it is on the main outline
     */
    bool        IsCutoutContour( int aCornerIdx );

    /**
     * Function AppendArc.
     * Adds segments to current contour to approximate the given arc
     */
    void        AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num );

    // access functions
    void       SetLayer( LAYER_NUM aLayer ) { m_layer = aLayer; }
    LAYER_NUM  GetLayer() const { return m_layer; }

    int GetCornersCount() const
    {
        return m_CornersList.GetCornersCount();
    }

    /**
     * @return true if the last corner in corners list is flagged end_contour
     */
    bool        GetClosed();

    /**
     * Function GetContoursCount.
     * @return the number of polygons stored in list
     * (number of corners flagged "end_contour"
     */
    int         GetContoursCount() const;

    /**
     * Function GetContour.
     * @return the contour number containing the corner ic
     * @param ic = the index of the corner in the corner list
     */
    int         GetContour( int ic );

    /**
     * Function GetContourStart.
     * @return the index of the first corner (in corners list) of a contour
     * @param icont = the index of the contour
     */
    int         GetContourStart( int icont );

    /**
     * Function GetContourEnd.
     * @return the index of the last corner (in corners list) of a contour
     * @param icont = the index of the contour
     */
    int         GetContourEnd( int icont );

    /**
     * Function GetContourSize.
     * @return the corners count of a contour
     * @param icont = the index of the contour
     */
    int         GetContourSize( int icont );

    int        GetX( int ic ) const { return m_CornersList.GetX( ic ); }
    int        GetY( int ic ) const { return m_CornersList.GetY( ic ); }

    /**
     * Function IsEndContour.
     * @return true if a corner is flagged end_contour
     * @param ic= the index (in corners list) of the corner
     */
    bool       IsEndContour( int ic ) const { return m_CornersList.IsEndContour( ic ); }

    const wxPoint& GetPos( int ic ) const { return m_CornersList.GetPos( ic ); }

    int        GetHatchPitch() const { return m_hatchPitch; }
    static int GetDefaultHatchPitchMils() { return 20; }    // default hatch pitch value in mils

    enum HATCH_STYLE GetHatchStyle() const { return m_hatchStyle; }
    void       SetHatch( int aHatchStyle, int aHatchPitch, bool aRebuildHatch )
    {
        SetHatchPitch( aHatchPitch );
        m_hatchStyle = (enum HATCH_STYLE) aHatchStyle;

        if( aRebuildHatch )
            Hatch();
    }

    void    SetX( int ic, int x )
    {
        m_CornersList.SetX( ic, x );
    }

    void    SetY( int ic, int y )
    {
        m_CornersList.SetY( ic, y );
    }

    void       SetHatchStyle( enum HATCH_STYLE style )
    {
        m_hatchStyle = style;
    }

    void       SetHatchPitch( int pitch ) { m_hatchPitch = pitch; }

    /**
     * Function NormalizeAreaOutlines
     * Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s)
     * @param aNewPolygonList = a std::vector<CPolyLine*> reference where to store new CPolyLine
     * needed by the normalization
     * @return 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( std::vector<CPolyLine*>* aNewPolygonList );

    // Bezier Support
    void    AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3 );
    void    AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );

    /**
     * Function Distance
     * Calculates the distance between a point and the zone:
     * @param aPoint the coordinate of the point.
     * @return int = distance between the point and outline.
     *               0 if the point is inside
     */
    int     Distance( const wxPoint& aPoint );

    /**
     * Function Distance
     * Calculates the distance between a segment and the zone:
     * @param aStart the starting point of the segment.
     * @param aEnd  the ending point of the segment.
     * @param aWidth  the width of the segment.
     * @return int = distance between the segment and outline.
     *               0 if segment intersects or is inside
     */
    int     Distance( wxPoint aStart, wxPoint aEnd, int aWidth );

    /**
     * Function HitTestForEdge
     * test is the point aPos is near (< aDistMax ) a vertex
     * @param aPos = the reference point
     * @param aDistMax = the max distance between a vertex and the reference point
     * @return int = the index of the first corner of the vertex, or -1 if not found.
     */
    int HitTestForEdge( const wxPoint& aPos, int aDistMax ) const;

    /**
     * Function HitTestForCorner
     * test is the point aPos is near (< aDistMax ) a corner
     * @param aPos = the reference point
     * @param aDistMax = the max distance between a vertex and the corner
     * @return int = the index of corner of the, or -1 if not found.
     */
    int HitTestForCorner( const wxPoint& aPos, int aDistMax ) const;

private:
    LAYER_NUM           m_layer;            // layer to draw on
    enum HATCH_STYLE    m_hatchStyle;       // hatch style, see enum above
    int                 m_hatchPitch;       // for DIAGONAL_EDGE hatched outlines, basic distance between 2 hatch lines
                                            // and the len of eacvh segment
                                            // for DIAGONAL_FULL, the pitch is twice this value
    int                 m_flags;            // a flag used in some calculations
public:
    CPOLYGONS_LIST          m_CornersList;  // array of points for corners
    std::vector <CSegment>  m_HatchLines;   // hatch lines showing the polygon area
};

const SHAPE_POLY_SET ConvertPolyListToPolySet( const CPOLYGONS_LIST& aList );
const CPOLYGONS_LIST ConvertPolySetToPolyList( const SHAPE_POLY_SET& aPolyset );

#endif    // #ifndef POLYLINE_H