/*  $Id: cl_vector.cpp,v 1.18 2003/08/21 15:24:21 mbn Exp $
**
**  CL_Vector class
**  Copyright (C) 1999  Daniel Vogel
**  For a total list of contributers see the file CREDITS.
**
**  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., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
**
*/

#include "Core/precomp.h"
#include "API/Core/Math/cl_vector.h"
#include "API/Core/System/cl_assert.h"
#include <cmath>

CL_Vector::CL_Vector(float x, float y, float z, float w)
{
	this->x = x;
	this->y = y;
	this->z = z;
	this->w = w;
}

CL_Vector::CL_Vector(const CL_Vector &other)
{
	x = other.x;
	y = other.y;
	z = other.z;
	w = other.w;
}

float CL_Vector::norm() const
{
	#ifdef WIN32
	return (float)sqrt(x*x+y*y+z*z);
	#else
	return std::sqrt(x*x+y*y+z*z);
	#endif
}

void CL_Vector::normalize()
{
	float f = norm();
	if (f!=0)
	{
		x /= f;
		y /= f;
		z /= f;
	}
}

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

float CL_Vector::angle(const CL_Vector& v) const
{
	#ifdef WIN32
	return (float)acos(dot(v)/(norm()*v.norm()));  
	#else
	return std::acos(dot(v)/(norm()*v.norm()));  
	#endif
}

CL_Vector CL_Vector::cross(const CL_Vector& v) const
{
	CL_Vector tmp = CL_Vector(y * v.z - z * v.y,
				  z * v.x - x * v.z,
				  x * v.y - y * v.x);
	return tmp;  
}

// quick hack, same as glRotatef(angle, a);
CL_Vector CL_Vector::rotate(float angle, const CL_Vector& a) const
{
	CL_Vector tmp = CL_Vector();

	#ifdef WIN32
	float s = (float)sin(angle);
	float c = (float)cos(angle);
	#else
	float s = std::sin(angle);
	float c = std::cos(angle);
	#endif

	tmp.x = x*(a.x*a.x*(1-c)+c)     + y*(a.x*a.y*(1-c)-a.z*s) + z*(a.x*a.z*(1-c)+a.y*s);
	tmp.y = x*(a.y*a.x*(1-c)+a.z*s) + y*(a.y*a.y*(1-c)+c)     + z*(a.y*a.z*(1-c)-a.x*s);
	tmp.z = x*(a.x*a.z*(1-c)-a.y*s) + y*(a.y*a.z*(1-c)+a.x*s) + z*(a.z*a.z*(1-c)+c);
	return tmp;  
}

void CL_Vector::round()
{
	x = int(x+0.5f);
	y = int(y+0.5f);
	z = int(z+0.5f);
	w = int(w+0.5f);
}

CL_Vector CL_Vector::operator * (float s) const
{
	return CL_Vector(s * x,
		 	 s * y,
			 s * z,
			 s * w);
}

CL_Vector operator * (float s, const CL_Vector& v)
{
	return CL_Vector(s * v.x,
			 s * v.y,
			 s * v.z,
			 s * v.w);
}

void CL_Vector::operator += (const CL_Vector& v)
{
	x += v.x;
	y += v.y;
	z += v.z;
	w += v.z;
}

void CL_Vector::operator -= (const CL_Vector& v)
{
  	x -= v.x;
	y -= v.y;
	z -= v.z;
	w -= v.w;
}

void CL_Vector::operator *= (float s)
{
	x *= s;
	y *= s;
	z *= s;
	w *= s;
}

CL_Vector CL_Vector::operator + (const CL_Vector& v) const
{
	return CL_Vector(x + v.x,
			 y + v.y,
			 z + v.z,
			 w + v.w);
}

CL_Vector CL_Vector::operator - (const CL_Vector& v) const
{
	return CL_Vector(x - v.x,
			 y - v.y,
			 z - v.z,
			 w - v.z);
}

CL_Vector CL_Vector::operator - () const
{
	return CL_Vector(-x,
			 -y,
			 -z,
			 -w);
}

CL_Vector& CL_Vector::operator = (const CL_Vector& v)
{ 
	x = v.x;
	y = v.y;
	z = v.z;
	w = v.w;
	return *this;
}
 
bool CL_Vector::operator == (const CL_Vector& v) const
{
	return ((x == v.x) && (y == v.y) && (z == v.z) && (w == v.w));
}

bool CL_Vector::operator != (const CL_Vector& v) const
{
	return !(operator == (v));
}

float & CL_Vector::operator [] (int n)
{
	switch (n)
	{
		case 0:	return x;
		case 1: return y;
		case 2: return z;
		case 3: return w;
	}
	cl_assert(false);
	return x;			// dummy
}

std::ostream& operator << (std::ostream& os, const CL_Vector& v)
{
	os << v.x << " " << v.y << " " << v.z;
	return os;
}
