/*
 * This program source code file is part of KICAD, a free EDA CAD application.
 *
 * Copyright (C) 2010 Virtenio GmbH, Torsten Hueter, torsten.hueter <at> virtenio.de
 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
 * Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
 * Copyright (C) 2013 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 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 VECTOR2D_H_
#define VECTOR2D_H_

#include <limits>
#include <iostream>
#include <sstream>
#include <type_traits>

#include <math/util.h>

#ifdef WX_COMPATIBILITY
#include <wx/gdicmn.h>
#endif

/**
 * Traits class for VECTOR2.
 */
template <class T>
struct VECTOR2_TRAITS
{
    ///< extended range/precision types used by operations involving multiple
    ///< multiplications to prevent overflow.
    typedef T extended_type;
};

template <>
struct VECTOR2_TRAITS<int>
{
    typedef int64_t extended_type;
};

// Forward declarations for template friends
template <class T>
class VECTOR2;
template <class T>
std::ostream& operator<<( std::ostream& aStream, const VECTOR2<T>& aVector );

/**
 * Define a general 2D-vector/point.
 *
 * This class uses templates to be universal. Several operators are provided to help
 * easy implementing of linear algebra equations.
 *
 */
template <class T = int>
class VECTOR2
{
public:
    typedef typename VECTOR2_TRAITS<T>::extended_type extended_type;
    typedef T coord_type;

    static constexpr extended_type ECOORD_MAX = std::numeric_limits<extended_type>::max();
    static constexpr extended_type ECOORD_MIN = std::numeric_limits<extended_type>::min();

    T x, y;

    /// Construct a 2D-vector with x, y = 0
    VECTOR2();

#ifdef WX_COMPATIBILITY
    /// Constructor with a wxPoint as argument
    VECTOR2( const wxPoint& aPoint );

    /// Constructor with a wxSize as argument
    VECTOR2( const wxSize& aSize );
#endif

    /// Construct a vector with given components x, y
    VECTOR2( T x, T y );

    /// Initializes a vector from another specialization. Beware of rounding issues.
    template <typename CastingType>
    VECTOR2( const VECTOR2<CastingType>& aVec )
    {
        if( std::is_same<T, int>::value )
        {
            CastingType minI = static_cast<CastingType>( std::numeric_limits<int>::min() );
            CastingType maxI = static_cast<CastingType>( std::numeric_limits<int>::max() );

            x = static_cast<int>( Clamp( minI, aVec.x, maxI ) );
            y = static_cast<int>( Clamp( minI, aVec.y, maxI ) );
        }
        else
        {
            x = static_cast<T>( aVec.x );
            y = static_cast<T>( aVec.y );
        }
    }

    /// Copy a vector
    VECTOR2( const VECTOR2<T>& aVec )
    {
        x = aVec.x;
        y = aVec.y;
    }

    /// Cast a vector to another specialized subclass. Beware of rounding issues.
    template <typename CastedType>
    VECTOR2<CastedType> operator()() const
    {
        if( std::is_same<CastedType, int>::value )
        {
            T minI = static_cast<T>( std::numeric_limits<int>::min() );
            T maxI = static_cast<T>( std::numeric_limits<int>::max() );

            return VECTOR2<int>( static_cast<int>( Clamp( minI, x, maxI ) ),
                                 static_cast<int>( Clamp( minI, y, maxI ) ) );
        }
        else
        {
            return VECTOR2<CastedType>( static_cast<CastedType>( x ), static_cast<CastedType>( y ) );
        }
    }

    /**
     * Implement the cast to wxPoint.
     *
     * @return the vector cast to wxPoint.
     */
    explicit operator wxPoint() const
    {
        return wxPoint( x, y );
    }

    /**
     * Implement the cast to wxPoint.
     *
     * @return the vector cast to wxPoint.
     */
    explicit operator wxSize() const
    {
        return wxSize( x, y );
    }

    // virtual ~VECTOR2();

    /**
     * Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
     *
     * It is used to calculate the length of the vector.
     *
     * @return Scalar, the euclidean norm
     */
    T EuclideanNorm() const;

    /**
     * Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
     *
     * It is used to calculate the length of the vector.
     *
     * @return Scalar, the euclidean norm
     */
    extended_type SquaredEuclideanNorm() const;


    /**
     * Compute the perpendicular vector.
     *
     * @return Perpendicular vector
     */
    VECTOR2<T> Perpendicular() const;

    /**
     * Return a vector of the same direction, but length specified in \a aNewLength.
     *
     * @param aNewLength is the length of the rescaled vector.
     * @return the rescaled vector.
     */
    VECTOR2<T> Resize( T aNewLength ) const;

    /**
     * Return the vector formatted as a string.
     *
     * @return the formatted string
     */
    const std::string Format() const;

    /**
     * Compute cross product of self with \a aVector.
     */
    extended_type Cross( const VECTOR2<T>& aVector ) const;

    /**
     * Compute dot product of self with \a aVector.
     */
    extended_type Dot( const VECTOR2<T>& aVector ) const;


    // Operators

    /// Assignment operator
    VECTOR2<T>& operator=( const VECTOR2<T>& aVector );

    /// Vector addition operator
    VECTOR2<T> operator+( const VECTOR2<T>& aVector ) const;

    /// Scalar addition operator
    VECTOR2<T> operator+( const T& aScalar ) const;

    /// Compound assignment operator
    VECTOR2<T>& operator+=( const VECTOR2<T>& aVector );

    /// Compound assignment operator
    VECTOR2<T>& operator*=( const VECTOR2<T>& aVector );

    VECTOR2<T>& operator*=( const T& aScalar );

    /// Compound assignment operator
    VECTOR2<T>& operator+=( const T& aScalar );

    /// Vector subtraction operator
    VECTOR2<T> operator-( const VECTOR2<T>& aVector ) const;

    /// Scalar subtraction operator
    VECTOR2<T> operator-( const T& aScalar ) const;

    /// Compound assignment operator
    VECTOR2<T>& operator-=( const VECTOR2<T>& aVector );

    /// Compound assignment operator
    VECTOR2<T>& operator-=( const T& aScalar );

    /// Negate Vector operator
    VECTOR2<T> operator-();

    /// Scalar product operator
    extended_type operator*( const VECTOR2<T>& aVector ) const;

    /// Multiplication with a factor
    VECTOR2<T> operator*( const T& aFactor ) const;

    /// Division with a factor
    VECTOR2<T> operator/( double aFactor ) const;

    /// Equality operator
    bool operator==( const VECTOR2<T>& aVector ) const;

    /// Not equality operator
    bool operator!=( const VECTOR2<T>& aVector ) const;

    /// Smaller than operator
    bool operator<( const VECTOR2<T>& aVector ) const;
    bool operator<=( const VECTOR2<T>& aVector ) const;

    /// Greater than operator
    bool operator>( const VECTOR2<T>& aVector ) const;
    bool operator>=( const VECTOR2<T>& aVector ) const;
};


// ----------------------
// --- Implementation ---
// ----------------------

template <class T>
VECTOR2<T>::VECTOR2()
{
    x = y = 0.0;
}


#ifdef WX_COMPATIBILITY
template <class T>
VECTOR2<T>::VECTOR2( const wxPoint& aPoint )
{
    x = T( aPoint.x );
    y = T( aPoint.y );
}


template <class T>
VECTOR2<T>::VECTOR2( const wxSize& aSize )
{
    x = T( aSize.x );
    y = T( aSize.y );
}
#endif

template <class T>
VECTOR2<T>::VECTOR2( T aX, T aY )
{
    x = aX;
    y = aY;
}


template <class T>
T VECTOR2<T>::EuclideanNorm() const
{
    return sqrt( (extended_type) x * x + (extended_type) y * y );
}


template <class T>
typename VECTOR2<T>::extended_type VECTOR2<T>::SquaredEuclideanNorm() const
{
    return (extended_type) x * x + (extended_type) y * y;
}


template <class T>
VECTOR2<T> VECTOR2<T>::Perpendicular() const
{
    VECTOR2<T> perpendicular( -y, x );
    return perpendicular;
}


template <class T>
VECTOR2<T>& VECTOR2<T>::operator=( const VECTOR2<T>& aVector )
{
    x = aVector.x;
    y = aVector.y;
    return *this;
}


template <class T>
VECTOR2<T>& VECTOR2<T>::operator+=( const VECTOR2<T>& aVector )
{
    x += aVector.x;
    y += aVector.y;
    return *this;
}


template <class T>
VECTOR2<T>& VECTOR2<T>::operator*=( const VECTOR2<T>& aVector )
{
    x *= aVector.x;
    y *= aVector.y;
    return *this;
}


template <class T>
VECTOR2<T>& VECTOR2<T>::operator*=( const T& aScalar )
{
    x *= aScalar;
    y *= aScalar;
    return *this;
}


template <class T>
VECTOR2<T>& VECTOR2<T>::operator+=( const T& aScalar )
{
    x += aScalar;
    y += aScalar;
    return *this;
}


template <class T>
VECTOR2<T>& VECTOR2<T>::operator-=( const VECTOR2<T>& aVector )
{
    x -= aVector.x;
    y -= aVector.y;
    return *this;
}


template <class T>
VECTOR2<T>& VECTOR2<T>::operator-=( const T& aScalar )
{
    x -= aScalar;
    y -= aScalar;
    return *this;
}


template <class T>
VECTOR2<T> VECTOR2<T>::Resize( T aNewLength ) const
{
    if( x == 0 && y == 0 )
        return VECTOR2<T> ( 0, 0 );

    extended_type l_sq_current = (extended_type) x * x + (extended_type) y * y;
    extended_type l_sq_new = (extended_type) aNewLength * aNewLength;

    if( std::is_integral<T>::value )
    {
        return VECTOR2<T> (
            ( x < 0 ? -1 : 1 ) *
                KiROUND( std::sqrt( rescale( l_sq_new, (extended_type) x * x, l_sq_current ) ) ),
            ( y < 0 ? -1 : 1 ) *
                KiROUND( std::sqrt( rescale( l_sq_new, (extended_type) y * y, l_sq_current ) ) ) )
                    * sign( aNewLength );
    }
    else
    {
        return VECTOR2<T> (
            ( x < 0 ? -1 : 1 ) *
                std::sqrt( rescale( l_sq_new, (extended_type) x * x, l_sq_current ) ),
            ( y < 0 ? -1 : 1 ) *
                std::sqrt( rescale( l_sq_new, (extended_type) y * y, l_sq_current ) ) )
                    * sign( aNewLength );
    }
}


template <class T>
const std::string VECTOR2<T>::Format() const
{
    std::stringstream ss;

    ss << "( xy " << x << " " << y << " )";

    return ss.str();
}


template <class T>
VECTOR2<T> VECTOR2<T>::operator+( const VECTOR2<T>& aVector ) const
{
    return VECTOR2<T> ( x + aVector.x, y + aVector.y );
}


template <class T>
VECTOR2<T> VECTOR2<T>::operator+( const T& aScalar ) const
{
    return VECTOR2<T> ( x + aScalar, y + aScalar );
}


template <class T>
VECTOR2<T> VECTOR2<T>::operator-( const VECTOR2<T>& aVector ) const
{
    return VECTOR2<T> ( x - aVector.x, y - aVector.y );
}


template <class T>
VECTOR2<T> VECTOR2<T>::operator-( const T& aScalar ) const
{
    return VECTOR2<T> ( x - aScalar, y - aScalar );
}


template <class T>
VECTOR2<T> VECTOR2<T>::operator-()
{
    return VECTOR2<T> ( -x, -y );
}


template <class T>
typename VECTOR2<T>::extended_type VECTOR2<T>::operator*( const VECTOR2<T>& aVector ) const
{
    return (extended_type)aVector.x * x + (extended_type)aVector.y * y;
}


template <class T>
VECTOR2<T> VECTOR2<T>::operator*( const T& aFactor ) const
{
    VECTOR2<T> vector( x * aFactor, y * aFactor );
    return vector;
}


template <class T>
VECTOR2<T> VECTOR2<T>::operator/( double aFactor ) const
{
    if( std::is_integral<T>::value )
        return VECTOR2<T>( KiROUND( x / aFactor ), KiROUND( y / aFactor ) );
    else
        return VECTOR2<T>( x / aFactor, y / aFactor );
}


template <class T>
VECTOR2<T> operator*( const T& aFactor, const VECTOR2<T>& aVector )
{
    VECTOR2<T> vector( aVector.x * aFactor, aVector.y * aFactor );
    return vector;
}


template <class T>
typename VECTOR2<T>::extended_type VECTOR2<T>::Cross( const VECTOR2<T>& aVector ) const
{
    return (extended_type) x * (extended_type) aVector.y -
           (extended_type) y * (extended_type) aVector.x;
}


template <class T>
typename VECTOR2<T>::extended_type VECTOR2<T>::Dot( const VECTOR2<T>& aVector ) const
{
    return (extended_type) x * (extended_type) aVector.x +
           (extended_type) y * (extended_type) aVector.y;
}


template <class T>
bool VECTOR2<T>::operator<( const VECTOR2<T>& aVector ) const
{
    return ( *this * *this ) < ( aVector * aVector );
}


template <class T>
bool VECTOR2<T>::operator<=( const VECTOR2<T>& aVector ) const
{
    return ( *this * *this ) <= ( aVector * aVector );
}


template <class T>
bool VECTOR2<T>::operator>( const VECTOR2<T>& aVector ) const
{
    return ( *this * *this ) > ( aVector * aVector );
}


template <class T>
bool VECTOR2<T>::operator>=( const VECTOR2<T>& aVector ) const
{
    return ( *this * *this ) >= ( aVector * aVector );
}


template <class T>
bool VECTOR2<T>::operator==( VECTOR2<T> const& aVector ) const
{
    return ( aVector.x == x ) && ( aVector.y == y );
}


template <class T>
bool VECTOR2<T>::operator!=( VECTOR2<T> const& aVector ) const
{
    return ( aVector.x != x ) || ( aVector.y != y );
}


template <class T>
const VECTOR2<T> LexicographicalMax( const VECTOR2<T>& aA, const VECTOR2<T>& aB )
{
    if( aA.x > aB.x )
        return aA;
    else if( aA.x == aB.x && aA.y > aB.y )
        return aA;

    return aB;
}


template <class T>
const VECTOR2<T> LexicographicalMin( const VECTOR2<T>& aA, const VECTOR2<T>& aB )
{
    if( aA.x < aB.x )
        return aA;
    else if( aA.x == aB.x && aA.y < aB.y )
        return aA;

    return aB;
}


template <class T>
const int LexicographicalCompare( const VECTOR2<T>& aA, const VECTOR2<T>& aB )
{
    if( aA.x < aB.x )
        return -1;
    else if( aA.x > aB.x )
        return 1;
    else    // aA.x == aB.x
    {
        if( aA.y < aB.y )
            return -1;
        else if( aA.y > aB.y )
            return 1;
        else
            return 0;
    }
}


/**
 * Template to compare two VECTOR2<T> values for equality within a required epsilon.
 *
 * @param aFirst value to compare.
 * @param aSecond value to compare.
 * @param aEpsilon allowed error.
 * @return true if the values considered equal within the specified epsilon, otherwise false.
 */
template <class T>
typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
equals( VECTOR2<T> const& aFirst, VECTOR2<T> const& aSecond,
        T aEpsilon = std::numeric_limits<T>::epsilon() )
{
    if( !equals( aFirst.x, aSecond.x, aEpsilon ) )
    {
        return false;
    }

    return equals( aFirst.y, aSecond.y, aEpsilon );
}


template <class T>
std::ostream& operator<<( std::ostream& aStream, const VECTOR2<T>& aVector )
{
    aStream << "[ " << aVector.x << " | " << aVector.y << " ]";
    return aStream;
}

/* Default specializations */
typedef VECTOR2<double>       VECTOR2D;
typedef VECTOR2<int>          VECTOR2I;
typedef VECTOR2<unsigned int> VECTOR2U;

/* STL specializations */
namespace std
{
    // Required to enable correct use in std::map/unordered_map
    // DO NOT USE hash tables with VECTOR2 elements.  It is inefficient
    // and degenerates to a linear search.  Use the std::map/std::set
    // trees instead that utilize the less operator below
    // This function is purposely deleted after substantial testing
    template <>
    struct hash<VECTOR2I>
    {
        size_t operator()( const VECTOR2I& k ) const = delete;
    };

    // Required to enable use of std::hash with maps.
    template <>
    struct less<VECTOR2I>
    {
        bool operator()( const VECTOR2I& aA, const VECTOR2I& aB ) const;
    };
}

#endif    // VECTOR2D_H_
