/*
 *  Copyright (C) 2005  Andreas Volz
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *  
 *  This library 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
 *  Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 *  File: Vector3.h
 *  Andreas Volz <linux@brachttal.net>
 *   
 */

#ifndef VECTOR3_H
#define VECTOR3_H 1

#ifdef HAVE_CONFIG_H
  #include <config.h>
#endif

#include <cmath>
#include <string>
#include <sstream>
#include <iomanip>

namespace Animorph {

template <typename T>
class Vector3
{
private:

public:
  T x, y, z;

  Vector3 () {}

  /// copy constructor
  Vector3 (const Vector3<T> &v) : x (v.x), y (v.y), z (v.z) {}

  /// construct given three values
  /*!
   * \param _x the x component for the Vector3
   * \param _y the y component for the Vector3
   * \param _z the z component for the Vector3
   */
  Vector3 (T _x, T _y, T _z) : x (_x), y (_y), z (_z) {}

  virtual ~Vector3 () {}

  /// set all components to zero
  void zero () {x = y = z = 0.0;}

  /// normalize it
  void normalize ();

  /// return the magnitude
  T getMagnitude ();

  /// Vector dot product
  T operator * (const Vector3<T> &v);

  /// copy values
  Vector3<T>& operator = (const Vector3<T>& v1);

  /*! \return inverted vector */
  Vector3<T> operator - ();

  /// vector_2 += vector_1
  Vector3<T>& operator += (const Vector3<T>& v1);

  /// vector_2 -= vector_1
  Vector3<T>& operator -= (const Vector3<T>& v1);

  /// vector_1 *= scalar
  Vector3<T>& operator *= (const T scalar);
  
  /// vector_1 /= scalar
  Vector3<T>& operator /= (const T scalar);

  const T* getAsOpenGLVector() const {return &x;}
};

typedef Vector3 <float> Vector3f;
typedef Vector3 <double> Vector3d;

/*****************************/
/* Member Template functions */
/*****************************/

template <typename T>
void Vector3<T>::normalize ()
{
  T magnitude = getMagnitude ();

  if (magnitude > 0.0)
  {
    T one_mag = 1.0 / magnitude;
    *this *= one_mag;
  }
}

template <typename T>
inline T Vector3<T>::getMagnitude ()
{
  return sqrt (x * x + y * y + z * z);
}

template <typename T>
T Vector3<T>::operator * (const Vector3<T>& v)
{
  return x * v.x + y * v.y + z * v.z;
}

/********************/
/* Member-Operators */
/********************/

template <typename T>
Vector3<T>& Vector3<T>::operator = (const Vector3<T>& v1)
{
  x = v1.x;
  y = v1.y;
  z = v1.z;

  return *this;
}

template <typename T>
Vector3<T> Vector3<T>::operator - ()
{
  Vector3f v;
  v.x = -x;
  v.y = -y;
  v.z = -z;

  return v;
}

template <typename T>
inline Vector3<T>& Vector3<T>::operator += (const Vector3<T>& v1)
{
  x += v1.x;
  y += v1.y;
  z += v1.z;

  return *this;
}

template <typename T>
inline Vector3<T>& Vector3<T>::operator -= (const Vector3<T>& v1)
{
  x -= v1.x;
  y -= v1.y;
  z -= v1.z;

  return *this;
}

template <typename T>
inline Vector3<T>& Vector3<T>::operator *= (const T scalar)
{
  x *= scalar;
  y *= scalar;
  z *= scalar;

  return *this;
}

template <typename T>
inline Vector3<T>& Vector3<T>::operator /= (const T scalar)
{
  x /= scalar;
  y /= scalar;
  z /= scalar;

  return *this;
}

/**************************************/
/* Non-Member operators and functions */
/**************************************/

/// test two Vector3 if they have the same values
template <typename T>
inline bool operator == (const Vector3<T>& v1, const Vector3<T>& v2)
{
  if ((v1.x == v2.x) &&
      (v1.y == v2.y) &&
      (v1.z == v2.z))
  {
    return true;
  }
  
  return false;  
}

/// vector_2 = vector_1 * scalar
template <typename T>
inline Vector3<T> operator * (const Vector3<T>& v1, const float scalar)
{
  Vector3<T> v3 = v1;

  return v3 *= scalar;
}

/// vector_2 = vector_1 / scalar
template <typename T>
inline Vector3<T> operator / (const Vector3<T>& v1, const float scalar)
{
  Vector3<T> v3 = v1;

  return v3 /= scalar;
}

/// vector_3 = vector_1 - vector_2
template <typename T>
inline Vector3<T> operator - (const Vector3<T>& v1, const Vector3<T>& v2)
{
  Vector3<T> v3 = v1;

  return v3 -= v2;
}

/// vector_3 = vector_1 + vector_2
template <typename T>
inline Vector3<T> operator + (const Vector3<T>& v1, const Vector3<T>& v2)
{
  Vector3<T> v3 = v1;

  return v3 += v2;
}

/// calculate the cross pruduct of two Vector3
template <typename T>
inline Vector3<T> crossProduct (const Vector3<T> &v1, const Vector3<T> &v2)
{
  return Vector3<T> (
           v1.y * v2.z - v1.z * v2.y,
           v1.z * v2.x - v1.x * v2.z,
           v1.x * v2.y - v1.y * v2.x
         );
}

/// calculate the distance between two Vector3
template <typename T>
inline T vectorDistance (const Vector3<T> &v1, const Vector3<T> &v2)
{
  Vector3<T> dist = v1 - v2;

  return dist.getMagnitude ();
}

/// << operator for output
template <typename T>
std::ostream &operator << (std::ostream &s, const Vector3<T> &v)
{
  s << "[";
  s << "x: " << v.x << " y: " << v.y << " z: " << v.z;
  s << "]";

  return s;
}

} // end namespace Animorph

#endif	// VECTOR3_H
