#ifndef _MATH_LIN_QUAT_H
#define _MATH_LIN_QUAT_H

#include "math/lin/vec3.h"

namespace Lin {

class Matrix4x4;

class Quaternion
{
public:
	float x,y,z,w;

	Quaternion() { }
	Quaternion(const float _x, const float _y, const float _z, const float _w) {
		x=_x; y=_y; z=_z; w=_w;
	}
	void setIdentity()
	{
		x=y=z=0; w=1.0f;
	}
	void setXRotation(const float r) { w = cosf(r / 2); x = sinf(r / 2); y = z = 0; }
	void setYRotation(const float r) { w = cosf(r / 2); y = sinf(r / 2); x = z = 0; }
	void setZRotation(const float r) { w = cosf(r / 2); z = sinf(r / 2); x = y = 0; }
	void toMatrix(Matrix4x4 *out) const;
	static Quaternion fromMatrix(Matrix4x4 &m);

	Quaternion operator *(Quaternion &q) const
	{
		return Quaternion(
			(w * q.w) - (x * q.x) - (y * q.y) - (z * q.z),
			(w * q.x) + (x * q.w) + (y * q.z) - (z * q.y),
			(w * q.y) + (y * q.w) + (z * q.x) - (x * q.z),
			(w * q.z) + (z * q.w) + (x * q.y) - (y * q.x)
			);
	}
	Quaternion operator -()
	{
		return Quaternion(-x,-y,-z,-w);
	}
	void setRotation(Vec3 axis, float angle)
	{
		axis /= axis.length();
		angle *= .5f;
		float sine = sinf(angle);
		w = cosf(angle);
		x = sine * axis.x;
		y = sine * axis.y;
		z = sine * axis.z;
	}
	void toAxisAngle(Vec3 &v, float &angle)
	{
		normalize();
		if (w==1.0f && x==0.0f && y==0.0f && z==0.0f)
		{
			v = Vec3(0,1,0);
			angle = 0.0f;
			return;
		}
		float cos_a = w;
		angle = acosf(cos_a) * 2;
		float sin_a = sqrtf( 1.0f - cos_a * cos_a );
		if (fabsf(sin_a) < 0.00005f) sin_a = 1;
		float inv_sin_a=1.0f/sin_a;
		v.x = x * inv_sin_a;
		v.y = y * inv_sin_a;
		v.z = z * inv_sin_a;
	}
	enum {
		QUAT_SHORT,
		QUAT_LONG,
		QUAT_CW,
		QUAT_CCW
	};
	Quaternion slerp(const Quaternion &to, const float a) const;
	Quaternion multiply(const Quaternion &q) const;
	float &operator [] (int i) {
		return *((&x) + i);
	}
	float operator [] (int i) const {
		return *((&x) + i);
	}
	//not sure about this, maybe mag is supposed to sqrt
	float magnitude() const {
		return x*x + y*y + z*z + w*w;
	}
	void normalize()	{
		float f = 1.0f/sqrtf(magnitude());
		x*=f; y*=f; z*=f; w*=f;
	}
};

}  // namespace Lin

#endif	// _MATH_LIN_QUAT_H
