#ifndef __H_MATHS
#define __H_MATHS

#include "global.h"

extern 	UBYTE randomtable[];
extern	UBYTE rndcnt;

LONG	random(ULONG size);
void testMagnitude(void);


//Gives very fast sqrt function. fraction in lower 16 bits
#define FASTSQRT(result,source){ { 	ULONG j; \
									j=0; \
									while (source>1024) \
									{source>>=2;j++;} \
									result=sqrtable[source]<<j; \
  								}}

#ifndef _WINDOWS

extern int rcos(int a);
extern int rsin(int a);

#endif

LONG arcsin(LONG sin);
LONG arccos(LONG cos);

extern	UBYTE	rndcnt;
extern	UBYTE 	randomtable[];
extern	unsigned long 	sqrtable[];
extern	unsigned long 	acostable[];


/* Andy's macros for fixed-ish point multiplication and division */

#define FXMUL(a,b)	((a/4096)*b)
#define FXDIV(a,b)	((a/b)*4096)

#ifndef MAX
#define MAX(a,b)	(((a)>(b)) ? (a) : (b))
#endif

#ifndef MAX4
#define MAX4(a,b,c,d) MAX(MAX(a,b),MAX(c,d))
#endif

#ifndef MAX3
#define MAX3(a,b,c)	MAX(a,MAX(b,c))
#endif

#ifndef MIN
#define MIN(a,b) (((a)<(b)) ? (a) : (b))
#endif

#ifndef MIN4
#define MIN4(a,b,c,d) MIN(MIN(a,b),MIN(c,d))
#endif

#ifndef AVG4
#define AVG4(a,b,c,d) ((a+b+c+d)/4)
#endif

#define FASTSWAP(a,b){a=a^b;b=b^a;a=a^b;}

LONG	BOUND(LONG var,LONG lim);

#define SWAP(A,B,TMP) TMP=A;  A=B; B=TMP;

typedef struct {
	long vx, vy, vz;
}LVERT;
typedef struct {
	long x, y, z;
}LCOORD;

long	Magnitude(VECTOR *vect);
long 	Magnitude2D(long xx,long yy);
long 	Magnitude2D_S(short x,short y);
long	MagnitudeS(SVECTOR *v);
long	DotProduct(VECTOR *v1, VECTOR *v2);
long	DotProductS(SVECTOR *v1, SVECTOR *v2);
void	CrossProduct(VECTOR *result, VECTOR *operand1, VECTOR *operand2);
void	CalcBounce(VECTOR *vel,VECTOR *normal);
void	RotateVector2D(VECTOR *dest, VECTOR *source, LONG angle);
void	RotateVector2DXYZ(VECTOR *dest, VECTOR *source, LONG angle, BYTE axis);
int		MakeUnit(VECTOR *vect);
long FindHeight(long a,long b);

// speed < 0 means "go there NOW"
// returns YES/NO for arrival
// (VectorMoveTo can now cope with large vectors & speed without overflowing)
int VectorMoveTo(VECTOR *src, VECTOR *dest, int speed);
int VectorMoveTo2D(VECTOR *src, VECTOR *dest, int speed);

int SVectorMoveTo(SVECTOR *src, SVECTOR *dest, int speed);

UBYTE GetRandomSeed(void);
void SetRandomSeed(UBYTE seed);
int	findShortestAngle(int ang1, int ang2);

void mathsPrintMatrix(MATRIX *pM);
void mathsCalcInverseMatrix(MATRIX *pDest, MATRIX *pSrc);
void mathsMulMatrixProperly0(MATRIX *pC, MATRIX *pA, MATRIX *pB);

//void divtableInit(void);
//#define MAX_DIVS (4096)

//extern short pDivTable[];

#define LINKLISTINIT(A)		{   A.prev=A.next=&A; }

#define LINKLISTADD(A,B)	{ 	B.prev->next=A; \
								A->prev=B.prev; \
								A->next=&B;	  \
								B.prev=A;   }
#define LINKLISTSUB(A)		{   A->next->prev=A->prev; \
								A->prev->next=A->next; }


#define RANDOM256()			randomtable[(++rndcnt)&255]

#define SETVECTOR(V,x,y,z){	(V)->vx=x;(V)->vy=y;(V)->vz=z;}

#define ZEROVECTOR(vect) {(vect)->vx=(vect)->vy=(vect)->vz=0;}
//MIKEY#define ABS(A)  (((A)<0)?-(A):(A)) 
#define SCALEVECTOR(vect,scale){ 	(vect)->vx = ((vect)->vx*scale)/FRACTIONAL_DIV; \
									(vect)->vy = ((vect)->vy*scale)/FRACTIONAL_DIV; \
									(vect)->vz = ((vect)->vz*scale)/FRACTIONAL_DIV; }

#define SCALEVECTORNORMAL(vect,scale){ 	(vect)->vx = ((vect)->vx*scale); \
									(vect)->vy = ((vect)->vy*scale); \
									(vect)->vz = ((vect)->vz*scale); }

#define SUBVECTOR(result,operand1,operand2) { 	((result)->vx) = ((operand1)->vx) - ((operand2)->vx); \
												((result)->vy) = ((operand1)->vy) - ((operand2)->vy); \
												((result)->vz) = ((operand1)->vz) - ((operand2)->vz);	}

#define ADDVECTOR(result,operand1,operand2) { 	((result)->vx) = ((operand1)->vx) + ((operand2)->vx); \
												((result)->vy) = ((operand1)->vy) + ((operand2)->vy); \
												((result)->vz) = ((operand1)->vz) + ((operand2)->vz);	}

#define ADDTOVECTOR(result,operand)	{	(result)->vx += (operand)->vx; \
										(result)->vy += (operand)->vy; \
										(result)->vz += (operand)->vz; }
																

#define COPYVECTOR(U,V){	(U)->vx=(V)->vx; \
							(U)->vy=(V)->vy; \
							(U)->vz=(V)->vz; }

#define COPYVECTORALIGN(U,V){	*(ULONG*)((U)->vx)=*(ULONG*)((V)->vx); \
								(U)->vz=(V)->vz; }

int CompareVectors(VECTOR *v1, VECTOR *v2);

ULONG	AreaOfTriangle(VERT *sVert0, VERT *sVert1, VERT *sVert2);

//if you have a problem with MEMCPY (ie, with if statement, it must have {MEMCPY} around it,declare scr,dest & size, and use MEMCPY1)
//the if statement can be knocked out later 
#define FMEMCPY(DEST,SRC,SIZE)	{	{ \
									ULONG *qfsrc=(ULONG*)(SRC); \
									ULONG *qfdest=(ULONG*)(DEST); \
									LONG qfsize=(SIZE); \
								  	/*if (qfsize&3){printf("MEMCPY ERROR  SIZE NOT DIVISABLE BY 4!!!!!!!\n");VSync(0);}*/ \
									while (qfsize>0){ \
									*qfdest++=*qfsrc++; \
									qfsize-=4; \
									} \
									} \
								}


#define FMEMZERO(DEST,SIZE)	{	{ \
									ULONG *qfdest=(ULONG*)(DEST); \
									LONG qfsize=(SIZE); \
								  	/*if (qfsize&3){printf("MEMCPY ERROR  SIZE NOT DIVISABLE BY 4!!!!!!!\n");VSync(0);}*/ \
									while (qfsize>0){ \
									*qfdest++=0; \
									qfsize-=4; \
									} \
									} \
								}


/*
//									LONG qfsize=(SIZE); \
// 	if (qfsize&3){printf("MEMCPY ERROR  SIZE NOT DIVISABLE BY 4!!!!!!!\n");VSync(0);} \
//									qfsize-=4; \
*/
#define STRCPY(DEST,SRC)	{	{ \
									UBYTE *qfsrc=(UBYTE*)(SRC); \
									UBYTE *qfdest=(UBYTE*)(DEST); \
									while (*qfsrc!=0){ \
									*qfdest++=*qfsrc++; \
									} \
									*qfdest++=*qfsrc++; \
									} \
								}

#define SETRGBC(A,R,G,B,C)	*(ULONG*)&(A)=(ULONG)((R)|((G)<<8)|((B)<<16)|((C)<<24))

#define DOTPRODUCT2D(v1,v2)	((((v1)->vx)*((v2)->vx)) + (((v1)->vz)*((v2)->vz)))


// --------------------------------------------------------------------------------------------------------------------
// Constants & Macros

#define RSIN0 0
#define RCOS0 4096

#define MOD		%

#ifndef YES
#define YES (1)
#endif
#ifndef NO
#define NO  (0)
#endif

#define FRACTIONAL_BITS	12
#define FRACTIONAL_DIV	4096

#define DEG		(57.29577951)
#define RAD		(0.017453292)

#define M_PI            3.14159265358979323846
#define PI				3.141592654
#define D3DPI			(3.141592654)

#define VECTORSUBTRACT(D, A,B)	{   (D).x = (A).x - (B).x; \
									(D).y = (A).y - (B).y; \
									(D).z = (A).z - (B).z; }

#define VECTORADD(D, A,B)		{   (D).x = (A).x + (B).x; \
									(D).y = (A).y + (B).y; \
									(D).z = (A).z + (B).z; }

#define VECTORMULTIPLY(D, A,B)	{   (D).x = (A).x * (B).x; \
									(D).y = (A).y * (B).y; \
									(D).z = (A).z * (B).z; }

#define VECTORDIVIDE(D, A,B)	{   (D).x = (A).x / (B).x; \
									(D).y = (A).y / (B).y; \
									(D).z = (A).z / (B).z; }

#define VECTORSUBTRACTX(D, A, X,Y,Z) {  (D).x = (A).x - (X); \
										(D).y = (A).y - (Y); \
										(D).z = (A).z - (Z); }

#define VECTORADDX(D, A, X,Y,Z)		 {  (D).x = (A).x + (X); \
										(D).y = (A).y + (Y); \
										(D).z = (A).z + (Z); }

#define VECTORMULTIPLYX(D, A, X,Y,Z) {  (D).x = (A).x * (X); \
										(D).y = (A).y * (Y); \
										(D).z = (A).z * (Z); }

#define VECTORDIVIDEX(D, A, X,Y,Z)   {  (D).x = (A).x / (X); \
										(D).y = (A).y / (Y); \
										(D).z = (A).z / (Z); }

#define VECTORDOT(A,B)  (((A).x*(B).x)+((A).y*(B).y)+((A).z*(B).z))

//--------------------------------------------------------------------------------------------------------------------

#define READ_LONG_FROM_ODD(A) (*A++)|((*A++)<<8)|((*A++)<<16)|((*A++)<<24)
#define LONGVALUE(A) 	((*(A+3)<<24) + (*(A+2)<<16) + (*(A+1)<<8) + *(A)) 
#define SHORTVALUE(A) 	((*(A+1)<<8) + *A) 
#define V3SUM(A,B,D)		{   (D).vx = (A).vx + (B).vx; \
								(D).vy = (A).vy + (B).vy; \
								(D).vz = (A).vz + (B).vz; }




// "out" is optional, and is closest point

// If the intersect is beyond the line, returns dist to the nearest endpoint.

int mathsDistancePointToLine3D(VECTOR *p1, VECTOR *p2, VECTOR *point, VECTOR *out);
int mathsDistancePointToLine2D(VECTOR *p1, VECTOR *p2, VECTOR *point, VECTOR *out);


#endif


