// Copyright (C) 2004-2009 Christoph Kubisch & Eike Decker
// This file is part of the "Luxinia Engine".
// For conditions of distribution and use, see copyright notice in luxiniapublicsdk.txt
//
// <Author: Christoph Kubisch>


#ifndef __LUXMATH_MISC_H__
#define __LUXMATH_MISC_H__

#include <luxmath/basetypes.h>

#ifdef __cplusplus
extern "C"{
#endif

#include <stdlib.h>

//////////////////////////////////////////////////////////////////////////

int		lxNextPowerOf2(const int size);
int		lxNearestPowerOf2(const int size);

float	lxFrand();
float	lxZigzag(const float in);
float	lxVariance(const float in,const float var, const float rand);
void	lxSwapFloat(float *a, float *b);

void	lxPermutation(int *array, int n, int *temp);

float lxGetAverageOfN (const float *array, int n); // calculates the avg of n elements in array
float lxGetMinOfN (const float *array, int n);	// returns the min of n elements in array
float lxGetMaxOfN (const float *array, int n); // returns the max of n elements in array

float lxGetIntAverageOfN (const int *array, int n); // calculates the avg of n elements in array
int	  lxGetIntMinOfN (const int *array, int n);	// returns the min of n elements in array
int   lxGetIntMaxOfN (const int *array, int n); // returns the max of n elements in array


// generic type
#define LX_ARRAY3COMPARE(a,cmp,b)	(((a)[0] cmp (b)[0] && (a)[1] cmp (b)[1] && (a)[2] cmp (b)[2]))
#define LX_ARRAY1COPY(dst,src)		((dst)[0]=(src)[0])
#define LX_ARRAY2COPY(dst,src)		((dst)[0]=(src)[0],(dst)[1]=(src)[1])
#define LX_ARRAY3COPY(dst,src)		((dst)[0]=(src)[0],(dst)[1]=(src)[1],(dst)[2]=(src)[2])
#define LX_ARRAY4COPY(dst,src)		((dst)[0]=(src)[0],(dst)[1]=(src)[1],(dst)[2]=(src)[2],(dst)[3]=(src)[3])

#define LX_ARRAY2SET(v2,x,y)			((v2)[0]=x,(v2)[1]=y)
#define LX_ARRAY3SET(v3,x,y,z)			((v3)[0]=x,(v3)[1]=y,(v3)[2]=z)
#define LX_ARRAY4SET(v4,x,y,z,w)		((v4)[0]=x,(v4)[1]=y,(v4)[2]=z,(v4)[3]=w)
#define LX_ARRAY5SET(v5,x,y,z,v,w)		((v5)[0]=x,(v5)[1]=y,(v5)[2]=z,(v5)[3]=v,(v5)[4]=w)
#define LX_ARRAY6SET(v6,x,y,z,u,v,w)	((v6)[0]=x,(v6)[1]=y,(v6)[2]=z,(v6)[3]=u,(v6)[4]=v,(v6)[5]=w)

#define LX_ARRAY2CLEAR(v2)	((v2)[0]=(v2)[1]=0)
#define LX_ARRAY3CLEAR(v3)	((v3)[0]=(v3)[1]=(v3)[2]=0)
#define LX_ARRAY4CLEAR(v4)	((v4)[0]=(v4)[1]=(v4)[2]=(v4)[3]=0)
#define LX_ARRAY5CLEAR(v5)	((v5)[0]=(v5)[1]=(v5)[2]=(v5)[3]=(v5)[4]=0)
#define LX_ARRAY6CLEAR(v6)	((v6)[0]=(v6)[1]=(v6)[2]=(v6)[3]=(v6)[4]=(v6)[5]=0)

#define LX_ARRAY2UNPACK(v) 		((v)[0]),((v)[1])
#define LX_ARRAY3UNPACK(v) 		((v)[0]),((v)[1]),((v)[2])
#define LX_ARRAY4UNPACK(v)			((v)[0]),((v)[1]),((v)[2]),((v)[3])
#define LX_ARRAYUNPACK3(v,off)		((v)[0+off]),((v)[1+off]),((v)[2+off]),((v)[3+off])
#define LX_ARRAY3TOVARS(v,x,y,z)	(x = ((v)[0]), y = ((v)[1]), z = ((v)[2]))

#define LX_ARRAY16COPY(dst, src)   ((dst)[0]  = (src)[0],  (dst)[1]  = (src)[1],  \
								(dst)[2]  = (src)[2],  (dst)[3]  = (src)[3],  \
								(dst)[4]  = (src)[4],  (dst)[5]  = (src)[5],  \
								(dst)[6]  = (src)[6],  (dst)[7]  = (src)[7],  \
								(dst)[8]  = (src)[8],  (dst)[9]  = (src)[9],  \
								(dst)[10] = (src)[10], (dst)[11] = (src)[11], \
								(dst)[12] = (src)[12], (dst)[13] = (src)[13], \
								(dst)[14] = (src)[14], (dst)[15] = (src)[15])

//////////////////////////////////////////////////////////////////////////

#define lxVector2Unpack 		LX_ARRAY2UNPACK
#define lxVector3Unpack 		LX_ARRAY3UNPACK
#define lxVector4Unpack			LX_ARRAY4UNPACK
#define lxVector3ToVar			LX_ARRAY3TOVARS
#define lxVector3Compare		LX_ARRAY3COMPARE
#define	lxMatrix44UnpackTranslation(matrix) LX_ARRAYUNPACK3(matrix,12)

// perform linear interpolation between two vectors
#define LX_ARRAY3LERPTYPE(type, out,  t,  a,  b)	(\
	out[0] = (type)((float)((a)[0])+ ((float)((b)[0])- (float)((a)[0]))* (float)t),\
	out[1] = (type)((float)((a)[1])+ ((float)((b)[1])- (float)((a)[1]))* (float)t),\
	out[2] = (type)((float)((a)[2])+ ((float)((b)[2])- (float)((a)[2]))* (float)t))

// perform linear interpolation between two vectors
#define LX_ARRAY4LERPTYPE(type, out,  t,  a,  b)	(\
	out[0] = (type)((float)((a)[0])+ ((float)((b)[0])- (float)((a)[0]))* (float)t),\
	out[1] = (type)((float)((a)[1])+ ((float)((b)[1])- (float)((a)[1]))* (float)t),\
	out[2] = (type)((float)((a)[2])+ ((float)((b)[2])- (float)((a)[2]))* (float)t),\
	out[3] = (type)((float)((a)[3])+ ((float)((b)[3])- (float)((a)[3]))* (float)t))

#define LX_ARRAY4CLAMP(color,min,max)	\
	((color)[0] = ((color)[0] > max) ? max : ((color)[0] < min) ? min : (color)[0],	\
	(color)[1] = ((color)[1] > max) ? max : ((color)[1] < min) ? min : (color)[1],	\
	(color)[2] = ((color)[2] > max) ? max : ((color)[2] < min) ? min : (color)[2],	\
	(color)[3] = ((color)[3] > max) ? max : ((color)[3] < min) ? min : (color)[3])

//////////////////////////////////////////////////////////////////////////

LUX_INLINE int	lxNextPowerOf2(const int size){
	int power = 2;

	if (size <= 1)
		return 1;

	while (power < size){
		power<<=1;
	}
	return power;
}

LUX_INLINE int	lxNearestPowerOf2(const int size)
{
	int power = 2;

	if (size <= 1){
		return 1;
	}

	while (1)
	{
		// we found the proper one
		if (size == power)
			return power;
		if (size > power && size < (power * 2))
		{
			// find towards which it is closer
			if (size >= ((power+(power*2))/2))
				return power*2;
			else
				return power;
		}
		power *= 2;
	};
}

LUX_INLINE float lxFrand()
{
	return (float)rand()/(float)RAND_MAX;
}

// zigzag width = 2, first half is a positive triangle, second half a negative
LUX_INLINE float lxZigzag(const float in){
	float calc;
	calc = (float)fmod(in,2);
	if (calc < 0){
		calc = -calc;
		if (calc > 1)
			calc -= 1.0;
		else
			calc += 1.0;
	}
	if (calc <= 0.5)
		return calc*2;
	if (calc <= 1.5)
		return 1 - (calc-0.5f)*2;
	if (calc <= 2)
		return (calc - 1.5f)*2 - 1;

	return 1;
}

LUX_INLINE float lxVariance(const float in,const float var, const float rand){
	return LX_LERP(rand,in-var,in+var);
}

LUX_INLINE void lxSwapFloat(float *a, float *b){
	float t=*b;
	*b=*a;
	*a=t;
}

#ifdef __cplusplus
}
#endif

#endif
