/***************************************************************************
 *   Copyright (C) 2003-2004 by Raphael Langerhorst                        *
 *   raphael-langerhorst@gmx.at                                            *
 *                                                                         *
 *   Copyright (C) 2004 Gerald Degeneve <gerald.degeneve@gmx.at>           *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

#ifndef GVECTOR3H
#define GVECTOR3H

#include <cmath>

class QString;

namespace GCS
{

/**
  \class GVector3 GVector3.h
  \brief Provides a vector with 3 elements and various mathematical operations
  @author Raphael Langerhorst
  @author Gerald Degeneve
  
  @todo move all inline methods into an implmenetation file
*/

class GVector3
{
  public:
    
    /**
     * Vector data
     */
    union
    {
        /**
	 * Point coordinates.
	 */
        struct
        {
            /**
             * Point coordinate x.
             */
	    double x;
            /**
             * Point coordinate y.
             */
	    double y;
            /**
             * Point coordinate z.
             */
	    double z;
	};
	
        /**
	 * Texture coordinates.
	 */
	struct
	{
            /**
             * Texture coordinate u.
             */
	    double u;
            /**
             * Texture coordinate v.
             */
	    double v;
            /**
             * Texture coordinate w.
             */
	    double w;
	};

        /**
	 * Array of coordinates.
	 */
	double c[3];
	
    };

  public:

    /**
     * Default constructor initializes all components to 0.
     */
    GVector3();
    
    /**
     * Initializes all components to the given value.
     */
    GVector3(const double d);

    /**
     * Initializes vector to given values.
     */
    GVector3(const double x, const double y, const double z);
    
    /**
     * Initializes vector to given array.
     * @note The array must contain at least three doubles.
     */    
    GVector3(const double* component);
    
    /**
     * Copy constructor
     */
    GVector3(const GVector3& original);

    /**
     * Calculates the length of the vector.
     */
    double length() const;
    
    /**
     * Calculates the square-length of the vector.
     */
    double lengthsq() const;

    /**
     * Resets the vector to x=y=z=0.
     */
    GVector3& reset();
    
    /**
     * Sets the vector to given values.
     */
    GVector3& set(double x, double y, double z);
    
    /**
     * Copies values from given vector.
     */
    GVector3& set(const GVector3& original);

    /**
     * Normalizes the vector to a length of 1.
     * Be careful with vectors that have all components
     * set to 0.
     */
    GVector3& normalize();
    
    /**
     * Simply calls scaleXYZ(scale.x,scale.y,scale.z);
     */
    GVector3& scaleXYZ(const GVector3& scale);
    
    /**
     * This can be used for asymetric scaling. Every
     * component is multiplied with the corresponding
     * given value.
     */
    GVector3& scaleXYZ(double x, double y, double z);

    /**
     * Mathematically adds given vector to this vector.
     */
    GVector3& add(const GVector3& right);
    
    /**
     * Mathematically subtracts given vector from this vector.
     */
    GVector3& sub(const GVector3& right);
    
    /**
     * Scales the vector by given scalar.
     */
    GVector3& mul(double scalar);
    
    /**
     * Calculates the dot product of this * right.
     * @note: no involved vectors are changed since the result
     * is a scalar;
     */
    double dot(const GVector3& right) const;

    /**
     * Calculates the cross product and returns a new
     * vector.
     * @note: this operation does NOT change the current vector,
     * instead, a new one is created for the result and returned.
     */
    GVector3 cross(const GVector3& right) const;
    
    /**
     * Copies all members from given vector.
     */
    GVector3& operator = (const GVector3& original);

    /**
     * Adds this vector to given vector and
     * returns a new vector as the result.
     */
    GVector3 operator + (const GVector3& right) const;
    
    /**
     * Subtracts this vector from given vector and
     * returns a new vector as the result.
     */
    GVector3 operator - (const GVector3& right) const;
    
    /**
     * Multiplies this vector with given scalar and returns the result.
     * @note This vector is not changed by the operation.
     */
    GVector3 operator * (double factor) const;

    /**
     * Adds given vector to this vector.
     */
    GVector3& operator += (const GVector3& right);
    
    /**
     * Subtracts given vector from this vector.
     */
    GVector3& operator -= (const GVector3& right);
    
    /**
     * Compares the given vector to this vector with an epsilon of 0.00001
     * @todo add an optional parameter for epsilon.
     */
    bool operator == (const GVector3& comp) const;

    // geometric operations:
    
    /**
     * If a GVector3 is used as a point/position then this method
     * can be used to calculate distances between two points.
     * @return distance between positions
     */
    double distanceTo(const GVector3& p) const;
    
    /**
     * Calculates the angle (in radians!!) between two vectors.
     *
     * If one of the involved vector's length is 0 then 0 is returned
     *
     * @return angle between vectors in rad
     */
    double angleTo(const GVector3& v) const;
    
    /**
     * Turns this vector around given vector for given angle;
     *
     * @param angle_rad angle in radians
     */
    GVector3& turnAroundAxis(const GVector3& axis, double angle_rad);
    
    /**
     * Projects this vector onto given vector and returns this vector.
     * If the length of the given vector is 0 then
     * this vector is NOT projected and returned as is.
     */
    GVector3& projectTo(const GVector3& v);
    
    
    /**
     * @return string representation of the vector in the form x, y, z
     */
    QString toString();

};

}

#endif
