/*!
@author d金魚
@since 2004/2/4
@brief 三角関数の処理とか
*/
#ifndef DKUTIL_MATH_TRIGONOMETRIC_FUNCTION_HPP
#define DKUTIL_MATH_TRIGONOMETRIC_FUNCTION_HPP
//#include "oldmath/dKingyoRandom.h"

#include <dkutil/dktl/singleton.hpp>
#include <dkutil/macro.hpp>
#include <math.h>
#include <time.h>

namespace dkutil{
#ifdef USE_DKINGYO_SINTABLESIZE_256
#	define DKINGYO_SINTABLESIZE 256
#else
#	define DKINGYO_SINTABLESIZE 360
#endif
#define DKINGYO_SINTABLEMASK		255//(DKINGYO_SINTABLESIZE - 1)
/// Multiplier for degrees to radians
#define DKUTIL_DTOR    0.0174532925199432957692369077
/// Multiplier for radians to degrees
#define DKUTIL_RTOD    57.295779513082320876798154814

/*!
d金魚が使いそうな数学ロジックを集めたもの。
内部の変数はstaticなのでいくら宣言してもメモリは圧迫しないと思うよ。<br>
<br>
<b>■最近知った　私の理解が間違っているかもしれない　数学用語</b>
<br>
degree：でぃぐりー：普通は0から360度の角度の単位？　学校で教えられているやつ<br>
radian：らじあん：私にはまったくわからない角度単位 degreeな角度 * π / 180 で算出されるもの<br>
<br>
そして、Ｃ言語ではもっぱら”ラジアン”ばっかり使われる^^;私が中学生の時は超戸惑ったよ！<br>
このクラスは私が扱いやすいようにDegreeを扱うのに非常に都合がよくできていたりする^^;<br>
*/


template<typename FLOATING__ = float>
class SineCosine{
public:
	typedef FLOATING__ floating_type;
protected:
	static floating_type    sintable[DKINGYO_SINTABLESIZE];

public:
	const floating_type *getsintable()const{return sintable;}
	size_t getsintablesize()const{return DKINGYO_SINTABLESIZE;}

	SineCosine(){
		static bool re=triginit();
	}
	//virtual ~SineCosine()
	//{}
#ifdef USE_DKINGYO_SINTABLESIZE_256
	bool triginit(void)
	{
			double  theta;
			for (int i = 0; i < DKINGYO_SINTABLESIZE; ++i)
			{
					theta = (double)i/((floating_type)DKINGYO_SINTABLESIZE / (2.0f * getPAI()));
					sintable[i] = (floating_type)sin(theta);
			}
			return true;
	}
	///@param theta[in] ラジアン単位の角度
	inline floating_type    fsin(floating_type theta)
	{
		int angle = get360to256(RadianToDegree(theta));
		return sintable[angle & DKINGYO_SINTABLEMASK];
	
	}
	///@param theta[in] ラジアン単位の角度
	inline floating_type    fcos(floating_type theta)
	{
		int angle = get360to256(RadianToDegree(theta));
		return sintable[(angle+(DKINGYO_SINTABLESIZE>>2)) & DKINGYO_SINTABLEMASK];
	}
	/*!
	@param angle[in] degreeな角度
	@return sin値
	*/
	inline floating_type AngleToCos( int angle ){
		//angle = get360to256(angle);
		//int a=(angle+(SINTABLESIZE>>2)) & SINTABLEMASK;
		//if(a>254) MB("ERROR");
		return sintable[(get360to256(angle)+(DKINGYO_SINTABLESIZE>>2)) & DKINGYO_SINTABLEMASK];
	}
	///@param angle[in] dgreeな角度 @return cos値
	inline floating_type AngleToSin( int angle ){ return sintable[get360to256(angle)];}

#	else //360度型

	bool triginit(void)
	{
			for (int i = 0; i < DKINGYO_SINTABLESIZE; ++i)
			{
				sintable[i] = (floating_type)::sin(DKUTIL_DTOR * i);
			}
			return true;
	}
	///@param theta[in] ラジアン単位の角度
	inline floating_type    fsin(floating_type theta)const
	{
		unsigned int angle = DKUTIL_RTOD* theta;
		return sintable[angle];
	
	}
	///@param theta[in] ラジアン単位の角度
	inline floating_type    fcos(floating_type theta)const
	{
		int angle = DKUTIL_RTOD * theta;
		return AngleToCos(angle);
		//return sintable[(angle + (DKINGYO_SINTABLESIZE>>2)) ];
	}
	/*!
	@param angle[in] degreeな角度
	@return sin値
	*/
	inline floating_type AngleToCos( int angle )const {
		//360度単位だと、素直にこうするしかないのね･･･
		/*if(angle < 90){
			angle += DKINGYO_SINTABLESIZE;
		}*/
		if(angle > 270){
			angle -= DKINGYO_SINTABLESIZE;
		}
		return sintable[angle + 90];
	}
	///@param angle[in] dgreeな角度 @return cos値
	inline floating_type AngleToSin( int angle )const{
		return sintable[angle];
	}

#endif


	///360度単位から256度単位に直してくれる
	static inline int get360to256( int angle )
	{
		return ((angle * 256 ) / 360);
	}



	///ディグリーな角度？をラジアンな角度にしてくれる！？
	static inline double DegreeToRadian(int angle){
		return angle * DKUTIL_DTOR;
	}
	//inline floating_type DegreeToRadian(int angle){	return angle * getPAI() / 180;}
	///ラジアンな角度？をディグリーな角度にしてくれる！？
	static inline int RadianToDegree(double radian){
		return (int)(radian * DKUTIL_RTOD);
	}
	//inline int RadianToDegree(floating_type radian){ return (int)(radian * 180 / getPAI());}

	///@return float型のπをゲットする
	static inline float getPAI(){
		return 3.14159265f;
	}
	///@return double型のπをゲットする
	static inline double getDoublePAI(){
		return 3.1415926535897932f;
	}	
	///AngleToCosとAngleToSinを両方やってくれるもの。
	inline void SinCos( long angle, floating_type *pSin, floating_type *pCos )
	{
		*pSin = AngleToSin(angle);
		*pCos = AngleToCos(angle);
	}
	template<typename T>
	void getRotationPointDegree( T *x,T *y, long Angle )const
	{
		floating_type fSin = AngleToSin( Angle );
		floating_type fCos = AngleToCos( Angle );

		floating_type fPx = (fCos * (*x)) - (fSin * (*y));
		floating_type fPy = (fSin * (*x)) + (fCos * (*y));

		*x = fPx;
		*y = fPy;
	}
	template<typename T>
	void getRotationPointRadian(T *x,T *y, double RadAngle )const
	{
		getRotationPointDegree(x,y,RadianToDegree(RadAngle));
	}
};



template<typename FLOATING__>
SineCosine<FLOATING__>::floating_type SineCosine<FLOATING__>::sintable[DKINGYO_SINTABLESIZE];


typedef SineCosine<float> sine_float_table;
typedef SineCosine<double> sine_double_table;
	
inline sine_float_table *GetSingletonSinCos(){
	static singleton<sine_float_table> t;
	return t.get();
}
}//end of dkutil namespace



#endif



