/*
 * 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 Kicad Developers, see change_log.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
 */

#ifndef VECTOR2D_H_
#define VECTOR2D_H_

#include <cmath>
#include <wx/gdicmn.h>


/// Forward declaration for template friends
//template<class T> class VECTOR2;


/**
 * Class VECTOR2
 * defines a general 2D-vector.
 *
 * This class uses templates to be universal. Several operators are provided to help easy implementing
 * of linear algebra equations.
 *
 */
template<class T> class VECTOR2
{
public:
    T x, y;

    // Constructors

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

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

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

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

    /// Destructor
    // virtual ~VECTOR2();

    /**
     * Function Euclidean Norm
     * computes 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();

    /**
     * Function Perpendicular
     * computes the perpendicular vector
     * @return Perpendicular vector
     */
    VECTOR2<T> Perpendicular();

    /**
     * Function Angle
     * computes the angle of the vector
     * @return vector angle
     */
    T Angle();

    // Operators

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

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

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

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

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

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

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

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

    /// Cross product operator
    T operator^( const VECTOR2<T>& aVector );

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

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

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

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

    /// Casting to int vector
    // operator VECTOR2<int>();

    /// Type casting operator for the class wxPoint
    //operator wxPoint();

    // friend ostream& operator<< <T> ( ostream &stream, const VECTOR2<T>& vector );
};


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

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

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

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

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

// Not required at the moment for this class
//template<class T> VECTOR2<T>::~VECTOR2()
//{
//    // TODO Auto-generated destructor stub
//}

template<class T> T VECTOR2<T>::EuclideanNorm()
{
    return sqrt( ( *this ) * ( *this ) );
}

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

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

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

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>::operator wxPoint()
//{
//    wxPoint point;
//    point.x = (int) x;
//    point.y = (int) y;
//    return point;
//}
//

//// Use correct rounding for casting to wxPoint
//template<> VECTOR2<double>::operator wxPoint()
//{
//    wxPoint point;
//    point.x = point.x >= 0 ? (int) ( x + 0.5 ) : (int) ( x - 0.5 );
//    point.y = point.y >= 0 ? (int) ( y + 0.5 ) : (int) ( y - 0.5 );
//    return point;
//}

// Use correct rounding for casting double->int
//template<> VECTOR2<double>::operator VECTOR2<int>()
//{
//    VECTOR2<int> vector;
//    vector.x = vector.x >= 0 ? (int) ( x + 0.5 ) : (int) ( x - 0.5 );
//    vector.y = vector.y >= 0 ? (int) ( y + 0.5 ) : (int) ( y - 0.5 );
//    return vector;
//}

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

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

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

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

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

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> T VECTOR2<T>::operator^( const VECTOR2<T>& aVector )
{
    return x * aVector.y - y * aVector.x;
}

template<class T> bool VECTOR2<T>::operator<( const VECTOR2<T>& aVector )
{
    // VECTOR2<T> vector( aVector );
    // need a specialization for T = int because of overflow:
    // return (double( x ) * x + double( y ) * y) < (double( o.x ) * o.x + double( o.y ) * y);
    return ( *this * *this ) < ( aVector * aVector );
}

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

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

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

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

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


/**
 * Class BOX2
 * is a description of a rectangle in a cartesion coordinate system.
 */
template<class T> class BOX2
{
public:
    BOX2() : x(0), y(0), width(0), height(0) {}

    BOX2( T aX, T aY, T aWidth, T aHeight ):
        x( aX ), y( aY ), width( aWidth ), height( aHeight )
    {}

    BOX2( const VECTOR2<T>& aPos, const VECTOR2<T>& aSize ) :
        x( aPos.x ), y( aPos.y ), width( aSize.x ), height( aSize.y )
    {}

    BOX2( const wxPoint& aPos, const wxSize& aSize ) :
        x( aPos.x ), y( aPos.y ), width( aSize.x ), height( aSize.y )
    {}

    /*
    BOX2( const EDA_RECT& aRect ):
        x( aRect.x ), y( aRect.y ), width( aRect.width ), height( aRect.height )
    {}
    */

    /// Constructor with a wxPoint as argument?

    VECTOR2<T>  GetSize() const         { return VECTOR2<T> ( width, height ); }
    VECTOR2<T>  GetPosition() const     { return VECTOR2<T> ( x, y ); }

    T GetLeft() const { return x; }
    void SetLeft( T n ) { width += x - n; x = n; }
    void MoveLeftTo( T n ) { x = n; }

    T GetTop() const { return y; }
    void SetTop( T n ) { height += y - n; y = n; }
    void MoveTopTo( T n ) { y = n; }

    T GetBottom() const { return y + height; }
    void SetBottom( T n ) { height += n - ( y + height ); }
    void MoveBottomTo( T n ) { y = n - height; }

    T GetRight() const { return x + width; }
    void SetRight( T n ) { width += n - ( x + width ); }
    void MoveRightTo( T n ) { x = n - width; }

    VECTOR2<T> GetLeftTop() const { return VECTOR2<T>( x , y ); }
    void SetLeftTop( const VECTOR2<T>& pt ) { width += x - pt.x; height += y - pt.y; x = pt.x; y = pt.y; }
    void MoveLeftTopTo( const VECTOR2<T> &pt ) { x = pt.x; y = pt.y; }

    VECTOR2<T> GetLeftBottom() const { return VECTOR2<T>( x, y + height ); }
    void SetLeftBottom( const VECTOR2<T>& pt ) { width += x - pt.x; height += pt.y - (y + height); x = pt.x; }
    void MoveLeftBottomTo( const VECTOR2<T>& pt ) { x = pt.x; y = pt.y - height; }

    VECTOR2<T> GetRightTop() const { return VECTOR2<T>( x + width, y ); }
    void SetRightTop( const VECTOR2<T>& pt ) { width += pt.x - ( x + width ); height += y - pt.y; y = pt.y; }
    void MoveRightTopTo( const VECTOR2<T>& pt ) { x = pt.x - width; y = pt.y; }

    VECTOR2<T> GetRightBottom() const { return VECTOR2<T>( x + width, y + height ); }
    void SetRightBottom( const VECTOR2<T>& pt ) { width += pt.x - ( x + width ); height += pt.y - ( y + height); }
    void MoveRightBottomTo( const VECTOR2<T>& pt ) { x = pt.x - width; y = pt.y - height; }

    VECTOR2<T> GetCentre() const { return VECTOR2<T>( x + width/2, y + height/2 ); }
    void SetCentre( const VECTOR2<T>& pt ) { MoveCentreTo( pt ); }
    void MoveCentreTo( const VECTOR2<T>& pt ) { x += pt.x - (x + width/2), y += pt.y - (y + height/2); }

    /**
     * Function Normalize
     * ensures that the height ant width are positive.
     */
    void Normalize()
    {
        if( height < 0 )
        {
            height = -height;
            y -= height;
        }

        if( width < 0 )
        {
            width = -width;
            x -= width;
        }
    }

    T   x, y, width, height;
};


typedef VECTOR2<double>     DPOINT;
typedef DPOINT              DSIZE;

typedef BOX2<double>        DBOX;


#endif // VECTOR2D_H_
