/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005 
 *					All rights reserved
 *
 *  This file is part of GPAC / fixed point math sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  GPAC 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 General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#ifndef __M4_MATH_H_
#define __M4_MATH_H_

#ifdef __cplusplus
extern "C" {
#endif


#include "m4_config.h"

/*
	NOTE: there is a conflict on Win32 VC6 with C++ and gpac headers when including <math.h>
*/
#ifndef __cplusplus
#include <math.h>
#endif

/*winCE always uses fixed-point version*/
#ifndef M4_FIXED_POINT
/*note: 
		to turn fp on, change to M4_FIXED_POINT
		to turn fp off, change to M4_NO_FIXED_POINT
	this is needed by configure+sed to modify this file directly
*/
#define M4_NO_FIXED_POINT
#endif


/*****************************************************************************************
			FIXED-POINT SUPPORT - HARDCODED FOR 16.16 representation
	the software rasterizer also use a 16.16 representation even in non-fixed version
******************************************************************************************/

#ifdef M4_FIXED_POINT

typedef s32 Fixed;

#define FIX_ONE			0x10000L
#define INT2FIX(v)		((Fixed)( ((s32) (v) ) << 16))
#define FLT2FIX(v)		((Fixed) ((v) * FIX_ONE))
#define FIX2INT(v)		((s32)(((v)+((FIX_ONE>>1)))>>16))
#define FIX2FLT(v)		((Float)( ((Float)(v)) / ((Float) FIX_ONE)))
#define FIX_EPSILON		2
#define FIX_MAX			0x7FFFFFFF
#define FIX_MIN			-FIX_MAX
#define M4_PI2		102944
#define M4_PI		205887
#define M4_2PI		411774

Fixed m4_mulfix(Fixed a, Fixed b);
Fixed m4_muldiv(Fixed a, Fixed b, Fixed c);
Fixed m4_divfix(Fixed a, Fixed b);

Fixed m4_cos(Fixed angle);
Fixed m4_sin(Fixed angle);
Fixed m4_tan(Fixed angle);
Fixed m4_acos(Fixed angle);
Fixed m4_asin(Fixed angle);
Fixed m4_atan2(Fixed x, Fixed y);
Fixed m4_sqrt(Fixed x);
Fixed m4_ceil(Fixed a);
Fixed m4_floor(Fixed a);

#else


typedef Float Fixed;

#define FIX_ONE			1.0f
#define INT2FIX(v)		((Float) (v))
#define FLT2FIX(v)		((Float) (v))
#define FIX2INT(v)		((s32)(v))
#define FIX2FLT(v)		((Float) (v))
#define FIX_EPSILON		M4_EPSILON_FLOAT
#define FIX_MAX			M4_MAX_FLOAT
#define FIX_MIN			-M4_MAX_FLOAT

#define M4_PI2		1.5707963267949f
#define M4_PI		3.1415926535898f
#define M4_2PI		6.2831853071796f

#define m4_mulfix(_a, _b)		((_a)*(_b))
#define m4_muldiv(_a, _b, _c)	((_c) ? (_a)*(_b)/(_c) : M4_MAX_FLOAT)
#define m4_divfix(_a, _b)		((_b) ? (_a)/(_b) : M4_MAX_FLOAT)


#define m4_cos(_a) ((Float) cos(_a))
#define m4_sin(_a) ((Float) sin(_a))
#define m4_tan(_a) ((Float) tan(_a))
#define m4_atan2(_x, _y) ((Float) atan2(_y, _x))
#define m4_sqrt(_x) ((Float) sqrt(_x))
#define m4_ceil(_a) ((Float) ceil(_a))
#define m4_floor(_a) ((Float) floor(_a))
#define m4_acos(_a) ((Float) acos(_a))
#define m4_asin(_a) ((Float) asin(_a))

#endif

Fixed m4_angle_diff(Fixed a, Fixed b);


typedef struct __vec2f
{
	Fixed x;
	Fixed y;
} M4Point2D;

Fixed m4_v2d_len(M4Point2D *vec);
void m4_v2d_from_polar(M4Point2D*  vec, Fixed length, Fixed angle);

/*rectangle*/
typedef struct
{
	Fixed x, y;
	Fixed width, height;
} M4Rect;

void m4_rect_union(M4Rect *rc1, M4Rect *rc2);
void m4_rect_center(M4Rect *rc, Fixed w, Fixed h);
Bool m4_rect_overlaps(M4Rect rc1, M4Rect rc2);
Bool m4_rect_equal(M4Rect rc1, M4Rect rc2);

/*pixel-aligned rectangle*/
typedef struct
{
	s32 x, y;
	s32 width, height;
} M4IRect;

M4IRect m4_rect_pixelize(M4Rect *r);



/* matrix object used by the renderer - transformation x,y in X, Y is:
	X = m[0]*x + m[1]*y + m[2];
	Y = m[3]*x + m[4]*y + m[5];
*/
typedef struct
{
	Fixed m[6];
} M4Matrix2D;


#define m4_mx2d_init(_obj) { memset((_obj).m, 0, sizeof(Fixed)*6); (_obj).m[0] = (_obj).m[4] = FIX_ONE; }
#define m4_mx2d_copy(_obj, from) memcpy((_obj).m, (from).m, sizeof(Fixed)*6);
#define m4_mx2d_is_identity(_obj) ((!(_obj).m[1] && !(_obj).m[2] && !(_obj).m[3] && !(_obj).m[5] && ((_obj).m[0]==FIX_ONE) && ((_obj).m[4]==FIX_ONE)) ? 1 : 0)

void m4_mx2d_add_matrix(M4Matrix2D *_this, M4Matrix2D *from);
void m4_mx2d_add_translation(M4Matrix2D *_this, Fixed cx, Fixed cy);
void m4_mx2d_add_rotation(M4Matrix2D *_this, Fixed cx, Fixed cy, Fixed angle);
void m4_mx2d_add_scale(M4Matrix2D *_this, Fixed scale_x, Fixed scale_y);
void m4_mx2d_add_scale_at(M4Matrix2D *_this, Fixed scale_x, Fixed scale_y, Fixed cx, Fixed cy, Fixed angle);
void m4_mx2d_add_skew(M4Matrix2D *_this, Fixed skew_x, Fixed skew_y);
void m4_mx2d_add_skewX(M4Matrix2D *_this, Fixed angle);
void m4_mx2d_add_skewY(M4Matrix2D *_this, Fixed angle);
void m4_mx2d_inverse(M4Matrix2D *_this);
void m4_mx2d_apply_coords(M4Matrix2D *_this, Fixed *x, Fixed *y);
void m4_mx2d_apply_point(M4Matrix2D *_this, M4Point2D *pt);
/*gets enclosing rect of rect after transformed*/
void m4_mx2d_apply_rect(M4Matrix2D *_this, M4Rect *rc);


/*color matrix object - can be used by any stencil type and performs complete
color space transformation (shift, rotate, skew, add)*/
typedef struct
{
/*the matrix coefs are in rgba order eg the color RGBA is multiplied by:
	R'		m0	m1	m2	m3	m4			R
	G'		m5	m6	m7	m8	m9			G
	B'	=	m10	m11	m12	m13	m14		x	B
	A'		m15	m16	m17	m18	m19			A
	0		0	0	0	0	1			0
	Coeficients are in intensity scale (range [0, 1])
*/
	Fixed m[20];
	/*internal flag to speed up things when matrix is identity READ ONLY - DO NOT MODIFY*/
	u32 identity;
} M4ColorMatrix;


/*base color tools - color is of the form 0xAARRGGBB*/
typedef u32 M4Color;
#define M4C_ARGB(a, r, g, b) ((a)<<24 | (r)<<16 | (g)<<8 | (b))

#define M4C_A(c) (u8) ((c)>>24)
#define M4C_R(c) (u8) ( ((c)>>16) & 0xFF)
#define M4C_G(c) (u8) ( ((c)>>8) & 0xFF)
#define M4C_B(c) (u8) ( (c) & 0xFF)
#define M4C_565(r, g, b) (u16) (((r & 248)<<8) + ((g & 252)<<3)  + (b>>3))
#define M4C_555(r, g, b) (u16) (((r & 248)<<7) + ((g & 248)<<2)  + (b>>3))
#define M4C_AG(a, g) (u16) ( (a << 8) | g)
#define M4C_TO_565(c) (((M4C_R(c) & 248)<<8) + ((M4C_G(c) & 252)<<3)  + (M4C_B(c)>>3))
#define M4C_TO_555(c) (((M4C_R(c) & 248)<<7) + ((M4C_G(c) & 248)<<2)  + (M4C_B(c)>>3))
#define M4C_TO_AG(c) ( (M4C_A(c) << 8) | M4C_R(c))


void m4_cmx_init(M4ColorMatrix *_this);
void m4_cmx_set_all(M4ColorMatrix *_this, Fixed *coefs);
void m4_cmx_set(M4ColorMatrix *_this, 
				 Fixed mrr, Fixed mrg, Fixed mrb, Fixed mra, Fixed tr,
				 Fixed mgr, Fixed mgg, Fixed mgb, Fixed mga, Fixed tg,
				 Fixed mbr, Fixed mbg, Fixed mbb, Fixed mba, Fixed tb,
				 Fixed mar, Fixed mag, Fixed mab, Fixed maa, Fixed ta);
void m4_cmx_copy(M4ColorMatrix *_this, M4ColorMatrix *from);
void m4_cmx_multiply(M4ColorMatrix *_this, M4ColorMatrix *w);
M4Color m4_cmx_apply(M4ColorMatrix *_this, M4Color col);
void m4_cmx_apply_fixed(M4ColorMatrix *_this, Fixed *a, Fixed *r, Fixed *g, Fixed *b);

#define M4C_ARGB_FIXED(_a, _r, _g, _b) M4C_ARGB(FIX2INT(255*(_a)), FIX2INT(255*(_r)), FIX2INT(255*(_g)), FIX2INT(255*(_b)))

/*
		3D tools

*/

/*vector/point*/
typedef struct __vec3f
{
	Fixed x;
	Fixed y;
	Fixed z;
} M4Vec;

/*base vector operations are MACROs for faster access*/
#define m4_vec_equal(v1, v2) (((v1).x == (v2).x) && ((v1).y == (v2).y) && ((v1).z == (v2).z))
#define m4_vec_diff(res, v1, v2) { (res).x = (v1).x - (v2).x; (res).y = (v1).y - (v2).y; (res).z = (v1).z - (v2).z; }
#define m4_vec_rev(v) { (v).x = -(v).x; (v).y = -(v).y; (v).z = -(v).z; }
#define m4_vec_add(res, v1, v2) { (res).x = (v1).x + (v2).x; (res).y = (v1).y + (v2).y; (res).z = (v1).z + (v2).z; }

Fixed m4_vec_len(M4Vec v);
Fixed m4_vec_lensq(M4Vec v);
Fixed m4_vec_dot(M4Vec v1, M4Vec v2);
void m4_vec_norm(M4Vec *v);
M4Vec m4_vec_scale(M4Vec v, Fixed f);
M4Vec m4_vec_cross(M4Vec v1, M4Vec v2);


/*vector4/quat/rotation*/
typedef struct __vec4f
{
	Fixed x;
	Fixed y;
	Fixed z;
	Fixed q;
} M4Vec4;


/*matrix*/
typedef struct
{
	Fixed m[16];
} M4Matrix;


/*quaternions tools*/
#define m4_quat_len(v) m4_sqrt(m4_mulfix((v).q,(v).q) + m4_mulfix((v).x,(v).x) + m4_mulfix((v).y,(v).y) + m4_mulfix((v).z,(v).z))
#define m4_quat_norm(v) { \
	Fixed __mag = m4_quat_len(v);	\
	(v).x = m4_divfix((v).x, __mag); (v).y = m4_divfix((v).y, __mag); (v).z = m4_divfix((v).z, __mag); (v).q = m4_divfix((v).q, __mag);	\
	}	\


M4Vec4 m4_quat_to_rotation(M4Vec4 *quat);
M4Vec4 m4_quat_from_matrix(M4Matrix *mx);
M4Vec4 m4_quat_from_rotation(M4Vec4 rot);
M4Vec4 m4_quat_get_inv(M4Vec4 *quat);
M4Vec4 m4_quat_multiply(M4Vec4 *q1, M4Vec4 *q2);
M4Vec m4_quat_rotate(M4Vec4 *quat, M4Vec *vec);
M4Vec4 m4_quat_from_axis_cos(M4Vec axis, Fixed cos_a);
M4Vec4 m4_quat_slerp(M4Vec4 q1, M4Vec4 q2, Fixed frac);

/*Axis-Aligned Bounding-Box*/
typedef struct
{
	/*min_x, min_y, min_z*/
	M4Vec min_edge;
	/*max_x, max_y, max_z*/
	M4Vec max_edge;

	/*center of bbox*/
	M4Vec center;
	/*radius bbox bounding sphere*/
	Fixed radius;
	/*bbox content is valid*/
	Bool is_set;
} M4BBox;

void m4_bbox_refresh(M4BBox *b);
void m4_bbox_from_rect(M4BBox *box, M4Rect *rc);
void m4_rect_from_bbox(M4Rect *rc, M4BBox *box);
void m4_bbox_grow_point(M4BBox *box, M4Vec pt);
void m4_bbox_union(M4BBox *b1, M4BBox *b2);
Bool m4_bbox_equal(M4BBox *b1, M4BBox *b2);
Bool m4_bbox_point_inside(M4BBox *box, M4Vec *p);
/*vertices are ordered to respect p vertex indexes (vertex from bbox closer to plane)
and so that n-vertex (vertex from bbox farther from plane) is 7-p_vx_idx*/
void m4_bbox_get_vertices(M4Vec bmin, M4Vec bmax, M4Vec *vecs);


/*matrix tools - all tools assume affine matrices, except when _4x4 is specified*/
#define m4_mx_init(_obj) { memset((_obj).m, 0, sizeof(Fixed)*16); (_obj).m[0] = (_obj).m[5] = (_obj).m[10] = (_obj).m[15] = FIX_ONE; }
#define m4_mx_copy(_obj, from) memcpy(&(_obj), &(from), sizeof(M4Matrix));
/*converts 2D matrix to 3D matrix*/
void m4_mx_from_mx2d(M4Matrix *mat, M4Matrix2D *mat2D);
/*returns 1 if matrices are same*/
Bool m4_mx_equal(M4Matrix *mx1, M4Matrix *mx2);
/*translates matrix*/
void m4_mx_add_translation(M4Matrix *mat, Fixed tx, Fixed ty, Fixed tz);
/*scales matrix*/
void m4_mx_add_scale(M4Matrix *mat, Fixed sx, Fixed sy, Fixed sz);
/*rotates matrix*/
void m4_mx_add_rotation(M4Matrix *mat, Fixed angle, Fixed x, Fixed y, Fixed z);
/*multiply: mat = mat*mul*/
void m4_mx_add_matrix(M4Matrix *mat, M4Matrix *mul);
/*affine matrix inversion*/
void m4_mx_inverse(M4Matrix *mx);
void m4_mx_apply_vec(M4Matrix *mx, M4Vec *pt);
/*gets enclosing rect of rect after transformed (matrix is considered as 2D)*/
void m4_mx_apply_rect(M4Matrix *_this, M4Rect *rc);
/*creates ortho matrix*/
void m4_mx_ortho(M4Matrix *mx, Fixed left, Fixed right, Fixed bottom, Fixed top, Fixed z_near, Fixed z_far);
/*creates perspective matrix*/
void m4_mx_perspective(M4Matrix *mx, Fixed fieldOfView, Fixed aspectRatio, Fixed z_ear, Fixed z_far);
/*creates look matrix*/
void m4_mx_lookat(M4Matrix *mx, M4Vec position, M4Vec target, M4Vec upVector);
/*gets enclosing box of box after transformed*/
void m4_mx_apply_bbox(M4Matrix *mx, M4BBox *b);
/*multiply: mat = mat*mul as full 4x4 matrices*/
void m4_mx_add_matrix_4x4(M4Matrix *mat, M4Matrix *mul);
/*generic 4x4 matrix inversion - returns 0 if failure*/
Bool m4_mx_inverse_4x4(M4Matrix *mx);
/*apply 4th dim vector*/
void m4_mx_apply_vec_4x4(M4Matrix *mx, M4Vec4 *vec);
/*decomposes matrix into translation, scale, shear and rotate - only use with affine matrix */
void m4_mx_decompose(M4Matrix *mx, M4Vec *translate, M4Vec *scale, M4Vec4 *rotate, M4Vec *shear);
/*only rotates the vector*/
void m4_mx_rotate_vector(M4Matrix *mx, M4Vec *pt);
/*rotation matrix transforming local axis in given norm vectors*/
void m4_mx_rotation_matrix_from_vectors(M4Matrix *mx, M4Vec x_axis, M4Vec y_axis, M4Vec z_axis);
/*trash all z info and gets a 2D matrix*/
void m4_mx2d_from_mx(M4Matrix2D *mat2D, M4Matrix *mat);


typedef struct
{
	M4Vec normal;
	Fixed d;
} M4Plane;

void m4_mx_apply_plane(M4Matrix *mx, M4Plane *plane);
Fixed m4_plane_get_distance(M4Plane *plane, M4Vec *p);
/*returns closest point on line from a given point in space*/
M4Vec m4_closest_point_to_line(M4Vec line_pt, M4Vec line_vec, M4Vec pt);
/*return p-vertex index (vertex from bbox closer to plane) - index range from 0 to 8*/
u32 m4_plane_get_p_vertex_idx(M4Plane *p);
Bool m4_plane_intersect_line(M4Plane *plane, M4Vec *linepoint, M4Vec *linevec, M4Vec *outPoint);

/*classify box/plane position. Retturns one of the following*/
enum 
{	
	M4_BOX_FRONT, /*box is in front of the plane*/
	M4_BOX_INTER, /*box intersects the plane*/
	M4_BOX_BACK /*box is back of the plane*/
};

u32 m4_box_plane_relation(M4BBox *box, M4Plane *p);




typedef struct
{
	M4Vec orig;
	M4Vec dir;
} M4Ray;

M4Ray m4_ray(M4Vec start, M4Vec end);
void m4_mx_apply_ray(M4Matrix *mx, M4Ray *r);
/*retuns 1 if intersection and stores value in outPoint*/
Bool m4_ray_hit_box(M4Ray *ray, M4Vec min_edge, M4Vec max_edge, M4Vec *outPoint);
/*retuns 1 if intersection and stores value in outPoint - if @center is NULL, assumes center is origin(0, 0, 0)*/
Bool m4_ray_hit_sphere(M4Ray *ray, M4Vec *center, Fixed radius, M4Vec *outPoint);
/*retuns 1 if intersection with triangle and stores distance on ray value */
Bool m4_ray_hit_triangle(M4Ray *ray, M4Vec *v0, M4Vec *v1, M4Vec *v2, Fixed *dist);
/*same as above and performs backface cull (solid meshes)*/
Bool m4_ray_hit_triangle_backcull(M4Ray *ray, M4Vec *v0, M4Vec *v1, M4Vec *v2, Fixed *dist);


#ifdef __cplusplus
}
#endif


#endif		/*__M4_FIXED_H_*/

