/*
 * KiRouter - a push-and-(sometimes-)shove PCB router
 *
 * Copyright (C) 2013-2015 CERN
 * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#ifndef __PNS_MEANDER_H
#define __PNS_MEANDER_H

#include <math/vector2d.h>

#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>

class PNS_MEANDER_PLACER_BASE;

///< Shapes of available meanders
enum PNS_MEANDER_TYPE {
        MT_SINGLE,          // _|^|_, single-sided
        MT_START,           // _|^|
        MT_FINISH,          // |^|_
        MT_TURN,            // |^| or |_|
        MT_CHECK_START,     // try fitting a start type, but don't produce a line
        MT_CHECK_FINISH,    // try fitting a finish type, but don't produce a line
        MT_CORNER,          // line corner
        MT_EMPTY            // no meander (straight line)
};

/**
 * Class PNS_MEANDER_SETTINGS
 *
 * Holds dimensions for the meandering algorithm.
 */
class PNS_MEANDER_SETTINGS
{
public:

    ///> meander corner shape
    enum CornerType {
        ROUND = 1,          // rounded (90 degree arc)
        CHAMFER             // chamfered (45 degree segment)
    };

    PNS_MEANDER_SETTINGS()
    {
        m_minAmplitude = 100000;
        m_maxAmplitude = 1000000;
        m_step = 50000;
        m_spacing = 600000;
        m_targetLength = 100000000;
        m_targetSkew = 0;
        m_cornerType = ROUND;
        m_cornerRadiusPercentage = 100;
        m_lengthTolerance = 100000;
        m_cornerArcSegments = 8;
    }

    ///> minimum meandering amplitude
    int m_minAmplitude;
    ///> maximum meandering amplitude
    int m_maxAmplitude;
    ///> meandering period/spacing (see dialog picture for explanation)
    int m_spacing;
    ///> amplitude/spacing adjustment step
    int m_step;
    ///> desired length of the tuned line/diff pair
    int m_targetLength;
    ///> type of corners for the meandered line
    CornerType m_cornerType;
    ///> rounding percentage (0 - 100)
    int m_cornerRadiusPercentage;
    ///> allowable tuning error
    int m_lengthTolerance;
    ///> number of line segments for arc approximation
    int m_cornerArcSegments;
    ///> target skew value for diff pair de-skewing
    int m_targetSkew;
};

class PNS_MEANDERED_LINE;

/**
 * Class PNS_MEANDER_SETTINGS
 *
 * Holds the geometry of a single meander.
 */
class PNS_MEANDER_SHAPE
{
public:
    /**
     * Constructor
     *
     * @param aPlacer the meander placer instance
     * @param aWidth width of the meandered line
     * @param aIsDual when true, the shape contains two meandered
     *                lines at a given offset (diff pairs)
     */
    PNS_MEANDER_SHAPE( PNS_MEANDER_PLACER_BASE *aPlacer, int aWidth, bool aIsDual = false ) :
        m_placer( aPlacer ),
        m_dual( aIsDual ),
        m_width( aWidth ),
        m_baselineOffset( 0 )
     {
     }

    /**
     * Function SetType()
     *
     * Sets the type of the meander.
     */
    void SetType( PNS_MEANDER_TYPE aType )
    {
        m_type = aType;
    }

    /**
     * Function Type()
     *
     * @return the type of the meander.
     */
    PNS_MEANDER_TYPE Type() const
    {
        return m_type;
    }

    /**
     * Function SetBaseIndex()
     *
     * Sets an auxillary index of the segment being meandered in its original PNS_LINE.
     */
    void SetBaseIndex( int aIndex )
    {
        m_baseIndex = aIndex;
    }

    /**
     * Function BaseIndex()
     *
     * @return auxillary index of the segment being meandered in its original PNS_LINE.
     */
    int BaseIndex() const
    {
        return m_baseIndex;
    }

    /**
     * Function Amplitude()
     *
     * @return the amplitude of the meander shape.
     */
    int Amplitude() const
    {
        return m_amplitude;
    }

    /**
     * Function MakeCorner()
     *
     * Creates a dummy meander shape representing a line corner. Used to define
     * the starts/ends of meandered segments.
     * @param aP1 corner point of the 1st line
     * @param aP2 corner point of the 2nd line (if m_dual == true)
     */
    void MakeCorner( VECTOR2I aP1, VECTOR2I aP2 = VECTOR2I( 0, 0 ) );

    /**
     * Function Resize()
     *
     * Changes the amplitude of the meander shape to aAmpl and recalculates
     * the resulting line chain.
     * @param aAmpl new amplitude.
     */
    void Resize( int aAmpl );

    /**
     * Function Recalculate()
     *
     * Recalculates the line chain representing the meanders's shape.
     */
    void Recalculate();

    /**
     * Function IsDual()
     *
     * @return true if the shape represents 2 parallel lines (diff pair).
     */
    bool IsDual() const
    {
        return m_dual;
    }

    /**
     * Function Side()
     *
     * @return true if the meander is to the right of its base segment.
     */
    bool Side() const
    {
        return m_side;
    }

    /**
     * Function End()
     *
     * @return end vertex of the base segment of the meander shape.
     */
    VECTOR2I End() const
    {
        return m_clippedBaseSeg.B;
    }

    /**
     * Function CLine()
     *
     * @return the line chain representing the shape of the meander.
     */
    const SHAPE_LINE_CHAIN& CLine( int aShape ) const
    {
        return m_shapes[aShape];
    }

    /**
     * Function MakeEmpty()
     *
     * Replaces the meander with straight bypass line(s), effectively
     * clearing it.
     */
    void MakeEmpty();

    /**
     * Function Fit()
     *
     * Attempts to fit a meander of a given type onto a segment, avoiding
     * collisions with other board features.
     * @param aType type of meander shape
     * @param aSeg base segment for meandering
     * @param aP start point of the meander
     * @param aSide side of aSeg to put the meander on (true = right)
     * @return true on success.
     */
    bool Fit( PNS_MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP, bool aSide );

    /**
     * Function BaseSegment()
     *
     * Returns the base segment the meadner was fitted to.
     * @return the base segment.
     */
    const SEG& BaseSegment() const
    {
        return m_clippedBaseSeg;
    }

    /**
     * Function BaselineLength()
     *
     * @return length of the base segment for the meander (i.e.
     *         the minimum tuned length.
     */
    int BaselineLength() const;

    /**
     * Function MaxTunableLength()
     *
     * @return the length of the fitted line chain.
     */
    int MaxTunableLength() const;

    /**
     * Function Settings()
     *
     * @return the current meandering settings.
     */
    const PNS_MEANDER_SETTINGS& Settings() const;

    /**
     * Function Width()
     *
     * @return width of the meandered line.
     */
    int Width() const
    {
        return m_width;
    }

    /**
     * Function SetBaselineOffset()
     *
     * Sets the parallel offset between the base segment and the meandered
     * line. Used for dual menaders (diff pair) only.
     * @param aOffset the offset
     */
    void SetBaselineOffset( int aOffset )
    {
        m_baselineOffset = aOffset;
    }

private:
    friend class PNS_MEANDERED_LINE;

    ///> starts turtle drawing
    void start( SHAPE_LINE_CHAIN* aTarget, const VECTOR2D& aWhere, const VECTOR2D& aDir );
    ///> moves turtle forward by aLength
    void forward( int aLength );
    ///> turns the turtle by aAngle
    void turn( int aAngle );
    ///> tells the turtle to draw an arc of given radius and turn direction
    void arc( int aRadius, bool aSide );
    ///> tells the turtle to draw an U-like shape
    void uShape( int aSides, int aCorner, int aTop );

    ///> generates a 90-degree circular arc
    SHAPE_LINE_CHAIN circleQuad( VECTOR2D aP, VECTOR2D aDir, bool aSide );

    ///> reflects a point onto other side of a given segment
    VECTOR2I reflect( VECTOR2I aP, const SEG& aLine );

    ///> produces a meander shape of given type
    SHAPE_LINE_CHAIN genMeanderShape( VECTOR2D aP, VECTOR2D aDir, bool aSide, PNS_MEANDER_TYPE aType, int aAmpl, int aBaselineOffset = 0 );

    ///> recalculates the clipped baseline after the parameters of
    ///> the meander have been changed.
    void updateBaseSegment();

    ///> returns sanitized corner radius value
    int cornerRadius() const;

    ///> returns sanitized spacing value
    int spacing() const;

    ///> the type
    PNS_MEANDER_TYPE m_type;
    ///> the placer that placed this meander
    PNS_MEANDER_PLACER_BASE* m_placer;
    ///> dual or single line
    bool m_dual;
    ///> width of the line
    int m_width;
    ///> amplitude of the meander
    int m_amplitude;
    ///> offset wrs the base segment (dual only)
    int m_baselineOffset;
    ///> first point of the meandered line
    VECTOR2I m_p0;
    ///> base segment (unclipped)
    SEG m_baseSeg;
    ///> base segment (clipped)
    SEG m_clippedBaseSeg;
    ///> side (true = right)
    bool m_side;
    ///> the actual shapes (0 used for single, both for dual)
    SHAPE_LINE_CHAIN m_shapes[2];
    ///> index of the meandered segment in the base line
    int m_baseIndex;
    ///> current turtle direction
    VECTOR2D m_currentDir;
    ///> current turtle position
    VECTOR2D m_currentPos;
    ///> the line the turtle is drawing on
    SHAPE_LINE_CHAIN* m_currentTarget;
};


/**
 * Class PNS_MEANDERED_LINE
 *
 * Represents a set of meanders fitted over a single or two lines.
 */
class PNS_MEANDERED_LINE
{
public:
    PNS_MEANDERED_LINE()
    {
    }

    /**
     * Constructor
     *
     * @param aPlacer the meander placer instance
     * @param aIsDual when true, the meanders are generated for two coupled lines
     */
    PNS_MEANDERED_LINE( PNS_MEANDER_PLACER_BASE* aPlacer, bool aIsDual = false ) :
        m_placer( aPlacer ),
        m_dual( aIsDual )
    {
    }

    /**
     * Function AddCorner()
     *
     * Creates a dummy meander shape representing a line corner. Used to define
     * the starts/ends of meandered segments.
     * @param aA corner point of the 1st line
     * @param aB corner point of the 2nd line (if m_dual == true)
     */
    void AddCorner( const VECTOR2I& aA, const VECTOR2I& aB = VECTOR2I( 0, 0 ) );

    /**
     * Function AddMeander()
     *
     * Adds a new meander shape the the meandered line.
     * @param aShape the meander shape to add
     */
    void AddMeander( PNS_MEANDER_SHAPE* aShape );

    /**
     * Function Clear()
     *
     * Clears the line geometry, removing all corners and meanders.
     */
    void Clear();

    /**
     * Function SetWidth()
     *
     * Sets the line width.
     */
    void SetWidth( int aWidth )
    {
        m_width = aWidth;
    }

    /**
     * Function MeanderSegment()
     *
     * Fits maximum amplitude meanders on a given segment and adds to the
     * current line.
     * @param aSeg the base segment to meander
     * @param aBaseIndex index of the base segment in the original line
     */
    void MeanderSegment( const SEG& aSeg, int aBaseIndex = 0 );

    /// @copydoc PNS_MEANDER_SHAPE::SetBaselineOffset()
    void SetBaselineOffset( int aOffset )
    {
        m_baselineOffset = aOffset;
    }

    /**
     * Function Meanders()
     *
     * @return set of meander shapes for this line
     */
    std::vector<PNS_MEANDER_SHAPE*>& Meanders()
    {
        return m_meanders;
    }

    /**
     * Function CheckSelfIntersections()
     *
     * Checks if the given shape is intersecting with any other meander in
     * the current line.
     * @param aShape the shape to check
     * @param aClearance clearance value
     * @return true, if the meander shape is not colliding
     */
    bool CheckSelfIntersections ( PNS_MEANDER_SHAPE* aShape, int aClearance );

    /**
     * Function Settings()
     *
     * @return the current meandering settings.
     */
    const PNS_MEANDER_SETTINGS& Settings() const;

private:
    VECTOR2I m_last;

    PNS_MEANDER_PLACER_BASE* m_placer;
    std::vector<PNS_MEANDER_SHAPE*> m_meanders;

    bool m_dual;
    int m_width;
    int m_baselineOffset;
};

#endif    // __PNS_MEANDER_H
