#ifndef __CMATH_H
#define __CMATH_H

#include "xs_Float.h"

#define USE_CUSTOM_MATH	// we want repreducably reliable results, even at the cost of performance
#define USE_FAST_MATH	// use faster table-based sin and cos variants with limited precision (sufficient for Doom gameplay)

extern"C"
{
double c_asin(double);
double c_acos(double);
double c_atan(double);
double c_atan2(double, double);
double c_sin(double);
double c_cos(double);
double c_tan(double);
double c_cot(double);
double c_sqrt(double);
double c_sinh(double);
double c_cosh(double);
double c_tanh(double);
double c_exp(double);
double c_log(double);
double c_log10(double);
double c_pow(double, double);
}


// This uses a sine table with linear interpolation
// For in-game calculations this is precise enough
// and this code is more than 10x faster than the
// Cephes sin and cos function.

struct FFastTrig
{
	static const int TBLPERIOD = 8192;
	static const int BITSHIFT = 19;
	static const int REMAINDER = (1 << BITSHIFT) - 1;
	float sinetable[2049];

	double sinq1(unsigned);
	
public:
	FFastTrig();
	double sin(unsigned);
	double cos(unsigned);
};

extern FFastTrig fasttrig;

// This must use xs_Float to guarantee proper integer wraparound.
#define DEG2BAM(f)		((unsigned)xs_CRoundToInt((f) * (0x40000000/90.)))
#define RAD2BAM(f)		((unsigned)xs_CRoundToInt((f) * (0x80000000/3.14159265358979323846)))


inline double fastcosbam(unsigned int v)
{
	return fasttrig.cos(v);
}

inline double fastsinbam(unsigned int v)
{
	return fasttrig.sin(v);
}

inline double fastcosdeg(double v)
{
	return fasttrig.cos(DEG2BAM(v));
}

inline double fastsindeg(double v)
{
	return fasttrig.sin(DEG2BAM(v));
}

inline double fastcos(double v)
{
	return fasttrig.cos(RAD2BAM(v));
}

inline double fastsin(double v)
{
	return fasttrig.sin(RAD2BAM(v));
}

// these are supposed to be local to this file.
#undef DEG2BAM
#undef RAD2BAM

inline double sindeg(double v)
{
#ifdef USE_CUSTOM_MATH
	return c_sin(v * (3.14159265358979323846 / 180.));
#else
	return sin(v * (3.14159265358979323846 / 180.));
#endif
}

inline double cosdeg(double v)
{
#ifdef USE_CUSTOM_MATH
	return c_cos(v * (3.14159265358979323846 / 180.));
#else
	return cos(v * (3.14159265358979323846 / 180.));
#endif
}


#ifndef USE_CUSTOM_MATH
#define g_asin  asin
#define g_acos  acos
#define g_atan  atan
#define g_atan2 atan2
#define g_sin	sin
#define g_cos	cos
#define g_sindeg	sindeg
#define g_cosdeg	cosdeg
#define g_tan	tan
#define g_cot	cot
#define g_sqrt  sqrt
#define g_sinh  sinh
#define g_cosh  cosh
#define g_tanh  tanh
#define g_exp	exp
#define g_log	log
#define g_log10 log10
#define g_pow	pow
#else
#define g_asin  c_asin
#define g_acos  c_acos
#define g_atan  c_atan
#define g_atan2 c_atan2
#ifndef USE_FAST_MATH
#define g_sindeg	sindeg
#define g_cosdeg	cosdeg
#define g_sin	c_sin
#define g_cos	c_cos
#else
#define g_sindeg	fastsindeg
#define g_cosdeg	fastcosdeg
#define g_sinbam	fastsinbam
#define g_cosbam	fastcosbam
#define g_sin	fastsin
#define g_cos	fastcos
#endif
#define g_tan	c_tan
#define g_cot	c_cot
#define g_sqrt  c_sqrt
#define g_sinh  c_sinh
#define g_cosh  c_cosh
#define g_tanh  c_tanh
#define g_exp	c_exp
#define g_log	c_log
#define g_log10 c_log10
#define g_pow	c_pow
#endif



#endif