//////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) Fredrik Åsgård. All rights reserved.
//
//  File:		gfmath.h
//  Content:	Math structures and functions
//
//////////////////////////////////////////////////////////////////////////////////

#ifndef _CG_MATH_H
#define _CG_MATH_H

namespace gravity
{

	#define CG_1BYPI		0.318309886f	// 1 / PI
	#define CG_DIG			3				// # of decimal digits of precision
	#define CG_EPSILON		4.8875809e-4f	// smallest such that 1.0 + epsilon != 1.0
	#define CG_MANT_DIG		11				// # of bits in mantissa
	#define CG_MAX			6.550400e+004	// max value
	#define CG_MAX_10_EXP	4				// max decimal exponent
	#define CG_MAX_EXP		15				// max binary exponent
	#define CG_MIN			6.1035156e-5f	// min positive value
	#define CG_MIN_10_EXP	(-4)			// min decimal exponent
	#define CG_MIN_EXP		(-14)			// min binary exponent
	#define CG_PI			3.141592654f	// PI
	#define CG_RADIX		2				// exponent radix
	#define CG_ROUNDS		1				// addition rounding: near

	#define deg2rad( deg ) (deg * (CG_PI / 180.0f))
	#define rad2deg( rad ) (rad * (180.0f / CG_PI))

	class cgMatrix;
	class cgPlane;
	class cgQuaternion;
	class cgVector2;
	class cgVector3;
	class cgVector4;
	class cgViewport;

	float sin( float  x );
	float cos( float  x );
	float tan( float  x );
	float cot( float  x );
	float asin( float  x );
	float acos( float  x );
	float atan( float  x );

	float sqr( float  x );
	float sqrt( float  x );

	float fabs( float x );

	class cgColor
	{
	public:
		float r, g, b, a;

		cgColor();
		cgColor( const float * );
		cgColor( float r, float g, float b );
		cgColor( float r, float g, float b, float a );

		operator float * ();
		operator const float * () const;

		cgColor& operator += ( const cgColor& );
		cgColor& operator -= ( const cgColor& );
		cgColor& operator *= ( float );
		cgColor& operator /= ( float );

		cgColor operator + () const;
		cgColor operator - () const;

		cgColor operator + ( const cgColor& ) const;
		cgColor operator - ( const cgColor& ) const;
		cgColor operator * ( float ) const;
		cgColor operator / ( float ) const;

		friend cgColor operator * ( float, const cgColor& );

		bool operator == ( const cgColor& ) const;
		bool operator != ( const cgColor& ) const;

		void add				( const cgColor *c );
		void adjustContrast		( float a );
		void adjustSaturation	( float a );
		void clamp				(  );
		void lerp				( const cgColor *c, float s );
		void modulate			( const cgColor *c );
		void negative			(  );
		void scale				( float s );
		void subtract			( const cgColor *c );
	};

	class cgMatrix
	{
	public:
		float m11, m12, m13, m14;
		float m21, m22, m23, m24;
		float m31, m32, m33, m34;
		float m41, m42, m43, m44;

		cgMatrix();
		cgMatrix( const float * );
		cgMatrix( float m11, float m12, float m13, float m14,
			float m21, float m22, float m23, float m24,
			float m31, float m32, float m33, float m34,
			float m41, float m42, float m43, float m44 );

		operator float* ();
		operator const float* () const;

		void operator = ( const cgMatrix& );
		void operator = ( float );

		cgMatrix& operator += ( const cgMatrix& );
		cgMatrix& operator -= ( const cgMatrix& );
		cgMatrix& operator *= ( const cgMatrix& );
		cgMatrix& operator *= ( float );
		cgMatrix& operator /= ( float );

		cgMatrix operator + () const;
		cgMatrix operator - () const;

		cgMatrix operator + ( const cgMatrix& ) const;
		cgMatrix operator - ( const cgMatrix& ) const;
		cgMatrix operator * ( const cgMatrix& ) const;
		cgMatrix operator * ( float ) const;
		cgMatrix operator / ( float ) const;

		friend cgMatrix operator * ( float, const cgMatrix& );

		bool operator == ( const cgMatrix& ) const;
		bool operator != ( const cgMatrix& ) const;

		void	affineTransformation	( float scaling, const cgVector3 *rotationCenter, const cgQuaternion *rotation, const cgVector3 *translation );
		void	affineTransformation2D	( float scaling, const cgVector2 *rotationCenter, float rotation, cgVector2 *translation );
		bool	decompose				( cgVector3 *pOutScale, cgQuaternion *pOutRotation, cgVector3 *pOutTranslation );
		float	determinant				(  );
		void	identity				(  );
		void	inverse					(  );
		bool	isIdentity				(  );
		void	lookAtLH				( const cgVector3 *eye, const cgVector3 *center, const cgVector3 *up );
		void	lookAtRH				( const cgVector3 *eye, const cgVector3 *center, const cgVector3 *up );
		void	multiply				( const cgMatrix *m );
		void	multiplyTranspose		( const cgMatrix *m );
		void	orthoLH					( float w, float h, float zn, float zf );
		void	orthoOffCenterLH		( float l, float r, float b, float t, float zn, float zf );
		void	orthoOffCenterRH		( float l, float r, float b, float t, float zn, float zf );
		void	orthoRH					( float w, float h, float zn, float zf );
		void	perspectiveFovLH		( float fovy, float aspect, float zn, float zf );
		void	perspectiveFovRH		( float fovy, float aspect, float zn, float zf );
		void	perspectiveLH			( float w, float h, float zn, float zf );
		void	perspectiveOffCenterLH	( float l, float r, float b, float t, float zn, float zf );
		void	perspectiveOffCenterRH	( float l, float r, float b, float t, float zn, float zf );
		void	perspectiveRH			( float w, float h, float zn, float zf );
		void	reflect					( const cgPlane *p );
		void	rotationAxis			( const cgVector3 *v, float angle );
		void	rotationQuaternion		( const cgQuaternion *q );
		void	rotationX				( float angle );
		void	rotationY				( float angle );
		void	rotationYawPitchRoll	( float yaw, float pitch, float roll );
		void	rotationZ				( float angle );
		void	scaling					( float sx, float sy, float sz );
		void	shadow					( const cgVector4 *light, const cgPlane *p );
		void	transformation			( const cgVector3 *scalingCenter, const cgQuaternion *scalingRotation, const cgVector3 *scaling, const cgVector3 *rotationCenter, const cgQuaternion *rotation, const cgVector3 *translation );
		void	transformation2D		( const cgVector2 *scalingCenter, float scalingRotation, const cgVector2* scaling, const cgVector2* rotationCenter, float rotation, const cgVector2* translation );
		void	translation				( float x, float y, float z );
		void	transpose				(  );
	};

	class cgPlane
	{
	public:
		float a, b, c, d;

		cgPlane();
		cgPlane( const float * );
		cgPlane( float a, float b, float c, float d );

		operator float* ();
		operator const float* () const;

		cgPlane& operator *= ( float );
		cgPlane& operator /= ( float );

		cgPlane operator + () const;
		cgPlane operator - () const;

		cgPlane operator * ( float ) const;
		cgPlane operator / ( float ) const;

		friend cgPlane operator * ( float, const cgPlane& );

		bool operator == ( const cgPlane& ) const;
		bool operator != ( const cgPlane& ) const;

		float	dot					( const cgVector4 *v );
		float	dotCoord			( const cgVector3 *v );
		float	dotNormal			( const cgVector3 *v );
		void	fromPointNormal		( const cgVector3 *point, const cgVector3 *normal );
		void	fromPoints			( const cgVector3 *v1, const cgVector3 *v2, const cgVector3 *v3 );
		cgVector3*	intersectLine	( const cgVector3 *v1, const cgVector3 *v2 );
		void	normalize			(  );
		void	scale				( float s );
		void	transform			( const cgMatrix *m );
	};

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

		cgQuaternion();
		cgQuaternion( const float * );
		cgQuaternion( float x, float y, float z, float w );

		operator float* ();
		operator const float* () const;

		void operator = ( const cgQuaternion& );
		void operator = ( float );

		cgQuaternion& operator += ( const cgQuaternion& );
		cgQuaternion& operator -= ( const cgQuaternion& );
		cgQuaternion& operator *= ( const cgQuaternion& );
		cgQuaternion& operator *= ( float );
		cgQuaternion& operator /= ( float );

		cgQuaternion operator + () const;
		cgQuaternion operator - () const;

		cgQuaternion operator + ( const cgQuaternion& ) const;
		cgQuaternion operator - ( const cgQuaternion& ) const;
		cgQuaternion operator * ( const cgQuaternion& ) const;
		cgQuaternion operator * ( float ) const;
		cgQuaternion operator / ( float ) const;

		friend cgQuaternion operator * ( float, const cgQuaternion& );

		bool operator == ( const cgQuaternion& ) const;
		bool operator != ( const cgQuaternion& ) const;

		void	baryCentric				( const cgQuaternion *q1, const cgQuaternion *q2, float f, float g );
		void	conjugate				(  );
		float	dot						( const cgQuaternion *q );
		void	exp						(  );
		void	identity				(  );
		void	inverse					(  );
		bool	isIdentity				(  );
		float	length					(  );
		float	lengthSq				(  );
		void	ln						(  );
		void	multiply				( const cgQuaternion *q );
		void	normalize				(  );
		void	rotationAxis			( const cgVector3 *v, float angle );
		void	rotationMatrix			( const cgMatrix *m);
		void	rotationYawPitchRoll	( float yaw, float pitch, float roll );
		void	slerp					( const cgQuaternion *q, float t );
		void	squad					( const cgQuaternion *qa, const cgQuaternion *qb, const cgQuaternion *qc, float t );
	};

	class cgVector2
	{
	public:
		float x, y;

		cgVector2();
		cgVector2( const float * );
		cgVector2( float x, float y );

		operator float* ();
		operator const float* () const;

		cgVector2& operator += ( const cgVector2& );
		cgVector2& operator -= ( const cgVector2& );
		cgVector2& operator *= ( float );
		cgVector2& operator /= ( float );

		cgVector2 operator + () const;
		cgVector2 operator - () const;

		cgVector2 operator + ( const cgVector2& ) const;
		cgVector2 operator - ( const cgVector2& ) const;
		cgVector2 operator * ( float ) const;
		cgVector2 operator / ( float ) const;

		friend cgVector2 operator * ( float, const cgVector2& );

		bool operator == ( const cgVector2& ) const;
		bool operator != ( const cgVector2& ) const;

		void	add					( const cgVector2 *v );
		void	baryCentric			( const cgVector2 *v1, const cgVector2 *v2, float f, float g );
		void	catmullRom			( const cgVector2 *v1, const cgVector2 *v2, const cgVector2 *v3, float s );
		float	ccw					( const cgVector2 *v ) const;
		float	dot					( const cgVector2 *v ) const;
		void	hermite				( const cgVector2 *v1, const cgVector2 *v2, const cgVector2 *v3, float s );
		float	lenght				(  );
		float	lenghtSqr			(  );
		void	lerp				( const cgVector2 *v, float s );
		void	maximize			( const cgVector2 *v );
		void	minimize			( const cgVector2 *v );
		void	normalize			(  );
		void	scale				( float s );
		void	subtract			( const cgVector2 *v );
		cgVector4*	transform		( const cgMatrix *m );
		void	transformCoord		( const cgMatrix *m );
		void	transformNormal		( const cgMatrix *m );
	};

	class cgVector3
	{
	public:
		float x, y, z;

		cgVector3();
		cgVector3( const float * );
		cgVector3( float x, float y, float z );

		operator float* ();
		operator const float* () const;

		cgVector3& operator += ( const cgVector3& );
		cgVector3& operator -= ( const cgVector3& );
		cgVector3& operator *= ( float );
		cgVector3& operator /= ( float );

		cgVector3 operator + () const;
		cgVector3 operator - () const;

		cgVector3 operator + ( const cgVector3& ) const;
		cgVector3 operator - ( const cgVector3& ) const;
		cgVector3 operator * ( float ) const;
		cgVector3 operator / ( float ) const;

		friend cgVector3 operator * ( float, const cgVector3& );

		bool operator == ( const cgVector3& ) const;
		bool operator != ( const cgVector3& ) const;

		void	add					( const cgVector3 *v );
		void	baryCentric			( const cgVector3 *v1, const cgVector3 *v2, float f, float g );
		void	catmullRom			( const cgVector3 *v1, const cgVector3 *v2, const cgVector3 *v3, float s );
		void	cross				( const cgVector3 *v );
		float	dot					( const cgVector3 *v );
		void	hermite				( const cgVector3 *v1, const cgVector3 *v2, const cgVector3 *v3, float s );
		float	lenght				(  );
		float	lenghtSqr			(  );
		void	lerp				( const cgVector3 *v, float s );
		void	maximize			( const cgVector3 *v );
		void	minimize			( const cgVector3 *v );
		void	normalize			(  );
		void	project				( const cgViewport *pViewport, const cgMatrix *pProjection, const cgMatrix *pView, const cgMatrix *pWorld );
		void	scale				( float s );
		void	subtract			( const cgVector3 *v );
		cgVector4*	transform		( const cgMatrix *m );
		void	transformCoord		( const cgMatrix *m );
		void	transformNormal		( const cgMatrix *m );
		void	unproject			( const cgViewport *pViewport, const cgMatrix *pProjection, const cgMatrix *pView, const cgMatrix *pWorld );
	};

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

		cgVector4();
		cgVector4( const float * );
		cgVector4( float x, float y, float z, float w );

		operator float* ();
		operator const float* () const;

		cgVector4& operator += ( const cgVector4& );
		cgVector4& operator -= ( const cgVector4& );
		cgVector4& operator *= ( float );
		cgVector4& operator /= ( float );

		cgVector4 operator + () const;
		cgVector4 operator - () const;

		cgVector4 operator + ( const cgVector4& ) const;
		cgVector4 operator - ( const cgVector4& ) const;
		cgVector4 operator * ( float ) const;
		cgVector4 operator / ( float ) const;

		friend cgVector4 operator * ( float, const cgVector4& );

		bool operator == ( const cgVector4& ) const;
		bool operator != ( const cgVector4& ) const;

		void	add					( const cgVector4 *v );
		void	baryCentric			( const cgVector4 *v1, const cgVector4 *v2, float f, float g );
		void	catmullRom			( const cgVector4 *v1, const cgVector4 *v2, const cgVector4 *v3, float s );
		void	cross				( const cgVector4 *v );
		float	dot					( const cgVector4 *v );
		void	hermite				( const cgVector4 *v1, const cgVector4 *v2, const cgVector4 *v3, float s );
		float	lenght				(  );
		float	lenghtSqr			(  );
		void	lerp				( const cgVector4 *v, float s );
		void	maximize			( const cgVector4 *v );
		void	minimize			( const cgVector4 *v );
		void	normalize			(  );
		void	scale				( float s );
		void	subtract			( const cgVector4 *v );
		void	transform			( const cgMatrix *m );
	};

	class cgViewport
	{
	public:
		float x, y, width, height;
	};

}

#endif // _CG_MATH_H