/*
 * (C) Copyright Ian D. Romanick 2006, 2007
 * All Rights Reserved.
 *
 * 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
 * on the rights to use, copy, modify, merge, publish, distribute, sub
 * license, 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 (including the next
 * paragraph) 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 NON-INFRINGEMENT.  IN NO EVENT SHALL
 * AUTHORS, COPYRIGHT HOLDERS, AND/OR THEIR SUPPLIERS 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.
 */

/**
 * \file vector.h
 * Simple vector class.
 * 
 * \author Ian Romanick
 */

#ifndef IDR_VECTOR_H
#define IDR_VECTOR_H

#ifdef _WIN32
# define _USE_MATH_DEFINES
#endif

#include <math.h>

class idr_vec4 {
public:
    /**
     * \name Various constructors for \c idr_vec4
     * Vectors can be constructed from an array of 4 floats, 4 float
     * scalars, a single float (which will be copied to all 4 elements).
     * The void constructor sets all 4 elements to 0.0.
     */
    /*@{*/
    idr_vec4(void)
    {
	v[0] = 0.0;
	v[1] = 0.0;
	v[2] = 0.0;
	v[3] = 0.0;
    }

    idr_vec4(const float *i)
    {
	v[0] = i[0];
	v[1] = i[1];
	v[2] = i[2];
	v[3] = i[3];
    }

    idr_vec4(float x, float y, float z, float w)
    {
	v[0] = x;
	v[1] = y;
	v[2] = z;
	v[3] = w;
    }

    idr_vec4(float _v)
    {
	v[0] = _v;
	v[1] = _v;
	v[2] = _v;
	v[3] = _v;
    }
    /*@}*/

    /**
     * Compute 4 element dot product between two vectors.
     */
    float dot4(const idr_vec4 &vec) const
    {
	return ((v[0] * vec.v[0]) 
		+ (v[1] * vec.v[1]) 
		+ (v[2] * vec.v[2]) 
		+ (v[3] * vec.v[3]));
    }

    
    /**
     * Negate each element of a vector.
     */
    idr_vec4 operator-(void) const
    {
	return idr_vec4(-v[0], -v[1], -v[2], -v[3]);
    }


    /**
     * Subtract one vector from another and return the result.
     */
    idr_vec4 operator-(const idr_vec4 &b) const
    {
	return idr_vec4(v[0] - b.v[0],
			v[1] - b.v[1],
			v[2] - b.v[2],
			v[3] - b.v[3]);
    }


    /**
     * Subtract one vector from another and store the result in \c this.
     */
    idr_vec4 operator-=(const idr_vec4 &b)
    {
	v[0] -= b.v[0];
	v[1] -= b.v[1];
	v[2] -= b.v[2];
	v[3] -= b.v[3];

	return idr_vec4(*this);
    }


    /**
     * Add one vector to another and store the result in \c this.
     */
    idr_vec4 operator+=(const idr_vec4 &b)
    {
	v[0] += b.v[0];
	v[1] += b.v[1];
	v[2] += b.v[2];
	v[3] += b.v[3];

	return idr_vec4(*this);
    }


    /**
     * Add one vector to another and return the result.
     */
    idr_vec4 operator+(const idr_vec4 &b) const
    {
	return idr_vec4(v[0] + b.v[0],
			v[1] + b.v[1],
			v[2] + b.v[2],
			v[3] + b.v[3]);
    }


    /**
     * Multiply a scalar by each element of a vector and return the result.
     */
    idr_vec4 operator*(float s) const
    {
	return idr_vec4(v[0] * s, v[1] * s, v[2] * s, v[3] * s);
    }


    /**
     * Multiply a scalar by each element of a vector and store the result in \c this.
     */
    idr_vec4 operator*=(float s)
    {
	v[0] *= s;
	v[1] *= s;
	v[2] *= s;
	v[3] *= s;

	return idr_vec4(*this);
    }


    /**
     * Divide a scalar by each element of a vector and return the result.
     */
    idr_vec4 operator/(float s) const
    {
	return idr_vec4(v[0] / s, v[1] / s, v[2] / s, v[3] / s);
    }


    /**
     * Divide a scalar by each element of a vector and store the result in \c this.
     */
    idr_vec4 operator/=(float s)
    {
	v[0] /= s;
	v[1] /= s;
	v[2] /= s;
	v[3] /= s;

	return idr_vec4(*this);
    }


    /**
     * Access the elements of the vector like it's an array.
     */
    float &operator[](const int idx)
    {
	return v[idx];
    }


    /**
     * Access the elements of the vector like it's an array.
     */
    float operator[](const int idx) const
    {
	return v[idx];
    }


    /**
     * Calculate the length (magnitude) of a vector.
     */
    float magnitude(void) const
    {
	return sqrt(dot4(*this));
    }


    /**
     * Normalize a vector.
     */
    idr_vec4 normalize(void) const
    {
	return idr_vec4(*this / magnitude());
    }


    /**
     * Calculate the cross product of two vectors and return the result.
     *
     * \note
     * This calculates a 3-dimensional cross product.  The fourth element is
     * set to 0.0.
     */
    idr_vec4 cross(const idr_vec4 &b) const
    {
	return idr_vec4(v[1] * b.v[2] - v[2] * b.v[1],
			v[2] * b.v[0] - v[0] * b.v[2],
			v[0] * b.v[1] - v[1] * b.v[0],
			0.0);
    }

private:
    float  v[4];
};


inline idr_vec4 lerp(const idr_vec4 &a, const idr_vec4 &b, float c)
{
    return (b * c) + (a * (1.0f - c));
}

#endif /* IDR_VECTOR_H */
