#ifndef vector2t_h
#define vector2t_h

#include <math.h>




namespace math
{
  //- contents:
  template<class T> class vector2t;
  
  template <class T>
  class vector2t
  {
  public:
    typedef T scalar_type;

    scalar_type x;
    scalar_type y;
    
    static inline long dimensions() { return 2; }

    //- structors
    vector2t()                                                : x( 0 ),   y( 0 )   {}
    vector2t( const scalar_type& sx, const scalar_type& sy )  : x( sx ),  y( sy )  {}
    vector2t( const vector2t& v )                             : x( v.x ), y( v.y ) {}
    //- destructor not needed

    //- set/reset
    vector2t& set(         const scalar_type  s ) { x = y =  s; return *this; }
	  vector2t& set( const scalar_type& sx, const scalar_type& sy ) { x = sx; y = sy; return *this; }
    vector2t& clear()                             { x = y = 0; return *this; }
    //- assignment
	  vector2t& operator = ( const vector2t&    v ) { if ( this != &v ) { x = v.x; y = v.y; } return *this; }
    //- assignment with scalar
    vector2t& operator = ( const scalar_type  s ) { x = y = s;   return *this; }
    //- more assignments
	  vector2t& operator +=( const vector2t&    v ) { x += v.x; y += v.y; return *this; }
	  vector2t& operator -=( const vector2t&    v ) { x -= v.x; y -= v.y; return *this; }
	  vector2t& operator +=( const scalar_type  s ) { x += s;   y += s;   return *this; }
	  vector2t& operator -=( const scalar_type  s ) { x -= s;   y -= s;   return *this; }
	  vector2t& operator /=( const scalar_type  s ) { x /= s;   y /= s;   return *this; }
	  vector2t& operator *=( const scalar_type  s ) { x *= s;   y *= s;   return *this; }
	  vector2t& scale      ( const scalar_type  s ) { return (*this) *= s;                        }

    vector2t& offset( const scalar_type x, const scalar_type y )
    {
      this->x += x;
      this->y += y;

      return *this;
    }

    //- access components (danger!)
    scalar_type& operator[]( const int i ) { return *( (&x) + i ); }
    const scalar_type& operator[]( const int i ) const { return *( (&x) + i ); }

    //- randomize
	  vector2t& randomize( const vector2t& v ) { 
      x = ( rand() % 10000 ) * v.x / scalar_type(10000);   
      y = ( rand() % 10000 ) * v.y / scalar_type(10000);   
      return *this; 
    }
	  vector2t& randomize( const vector2t& vmin, const vector2t& vmax ) { 
      x = ( rand() % 10000 ) * ( vmax.x - vmin.x ) / scalar_type(10000) + vmin.x;   
      y = ( rand() % 10000 ) * ( vmax.y - vmin.y ) / scalar_type(10000) + vmin.y;   
      return *this; 
    }
    vector2t& unit_randomize() { //testen!
      do {
        x = ( rand() % 10000 ) / scalar_type(5000) - 1;
        y = ( rand() % 10000 ) / scalar_type(5000) - 1;
      } while( length_squared() > scalar_type(1) );
      return *this; 
    }

// ////////////////////////////////////////////////
// //-    bald ersetzen durch externe Matrix-Operationen
// ////////////////////////////////////////////////
    vector2t& RotateZ( const double winkel )
    { 
      vector2t vTemp( *this ); 
      double w( winkel * double( 3.1415926535 / 180 ) );
      x = T( vTemp.x * cos( w ) - vTemp.y * sin( w ) ); 
      y = T( vTemp.x * sin( w ) + vTemp.y * cos( w ) );
      return *this; 
    }

// ////////////////////////////////////////////////
// //-    bald ersetzen durch Matrix-Operationen Ende
// ////////////////////////////////////////////////

    vector2t& flip_x()    
    { 
      x = -x; 
      return *this; 
    }

    vector2t& flip_y()    
    { 
      y = -y; 
      return *this; 
    }

    vector2t& flip_z()    
    { 
      return *this; 
    }

    vector2t normalize() 
    { 
      float   len = length();

      if ( len == 0 )
      {
        return *this;
      }
      return operator/=( len );
    }
 
    vector2t normalized() const
    { 
      if ( length() <= 0.0f )
      {
        return vector2t( *this );
      }
      return vector2t( *this ) / length(); 
    }

    ////////////////////////////////////////////////
    //- const-functions
    ////////////////////////////////////////////////
    vector2t operator +( const scalar_type s )  const { return vector2t(  x + s,   y + s    ); }
    vector2t operator -( const scalar_type s )  const { return vector2t(  x - s,   y - s    ); }
    vector2t operator *( const scalar_type s )  const { return vector2t(  x * s,   y * s    ); }
    vector2t operator /( const scalar_type s )  const { return vector2t(  x / s,   y / s    ); }
    vector2t operator +( const vector2t&   v )  const { return vector2t(  x + v.x, y + v.y  ); }
    vector2t operator -( const vector2t&   v )  const { return vector2t(  x - v.x, y - v.y  ); }
    vector2t operator -()                       const { return vector2t(  -x,      -y       ); }
   
    //- length and magnitude are the same
    float length()                              const 
    { 
      return sqrtf( (float)length_squared() ); 
    }

    scalar_type length_squared()                const 
    { 
      return x * x + y * y; 
    }
    
    float length( const vector2t& rhs ) const 
    { 
      return ( *this - rhs ).length();
    }

    scalar_type length_squared( const vector2t& rhs )                const 
    { 
      return ( *this - rhs ).length_squared();
    }
    
    //- comparisons
    bool operator==(     const vector2t&   v )  const { return  x == v.x && y == v.y; }
    bool operator!=(     const vector2t&   v )  const { return  !operator==( v ); }

    bool operator< (     const vector2t&   v) const
    {
	    return ( x < v.x ||
		    !(v.x < x ) && y < v.y );
    }

// ////////////////////////////////////////////////
// //-    bald ersetzen durch externe Matrix-Operationen
// ////////////////////////////////////////////////
    vector2t RotatedZ( const T winkel ) const
    { vector2t vTemp( *this ); 
      double w( winkel * 3.1415926535 / 180 );
      vTemp.x = T( x * cos( w ) - y * sin( w ) ); 
      vTemp.y = T( x * sin( w ) + y * cos( w ) );
      return vTemp; }
// ////////////////////////////////////////////////
// //-    bald ersetzen durch Matrix-Operationen Ende
// ////////////////////////////////////////////////

    //- mirror
    vector2t flipped_x() const { return vector2t( -x,  y ); }
    vector2t flipped_y() const { return vector2t(  x, -y ); }

    vector2t<float>    unit() const 
    { 
      return vector2t<float>( (float)x, (float)y ).normalized(); 
    }

    float dot( const vector2t& v ) const 
    { 
      return (float)x * (float)v.x + (float)y * (float)v.y; 
    }

    //- angle (radiant) between 2 vectors.
    float angle( const vector2t& v2 ) 
    { 
      return 180.0f * acosf( unit().dot( v2.unit() ) ) / 3.1415926f; 
    }

    // Winkel von Position zu Position v2
    float AngleTowards( const vector2t& v2 ) 
    {
      return ( v2 - *this ).angle();
    }

    float angle() const
    { 
      return atan2f( (float)y, (float)x ) * 180.0f / 3.1415926f;
    }

    scalar_type cross( const vector2t& v ) const 
    { 
      return x * v.y - y * v.x;
    }
    //- scale
    friend inline vector2t operator*( const scalar_type& s, const vector2t& v ) { return vector2t( v.x * s, v.y * s ); }


    /////////////////////////////////////////////////////////////////////
    //-     neu:
    /////////////////////////////////////////////////////////////////////

    float distance( const vector2t& rhs ) const { return ( rhs - *this ).length(); }
    float DistanceSquared( const vector2t& rhs ) const 
    { 
      return ( rhs - *this ).length_squared(); 
    }



	  vector2t& interpolate( const vector2t& rhs, const scalar_type& s ) 
    { 
      x += s * ( rhs.x - x );
      y += s * ( rhs.y - y );
      return *this; 
    }

	  vector2t& interpolate( const vector2t& v1, const vector2t& v2, const scalar_type& s ) 
    {
      x = v1.x + s * ( v2.x - v1.x );
      y = v1.y + s * ( v2.y - v1.y );
      return *this; 
    }

    vector2t& truncate( const float& maximum ) 
    { 
      float l = length();
      if ( l > maximum ) *this *= maximum / l;
      return *this; 
    }

    scalar_type perpendicular( const vector2t& v2 ) const
    {
      return x * v2.y - y * v2.x;
    }

    vector2t ProjectOn( const vector2t& V2 ) const
    {
      vector2t    NormV2( V2.normalized() );
      return NormV2 * NormV2.dot( *this );
    }

    vector2t NormalRH() const
    {
      return vector2t( -y, x );
    }

    vector2t NormalLH() const
    {
      return vector2t( y, -x );
    }
  };

}

#endif //_PJ_vector2t_h