/*
	Ninja Library
	
	COPYRIGHT ( C ) SEGA ENTERPRISES, LTD.
*/

#include "NuMotionMake.h"



/*	モーション情報構造体（スロット）		*/
NUS_CUR_MOTION_INFO		_nu_motion_info_[NUD_MOTION_INFO_MAX];

/*	現在処理中のモーション情報		*/
NUS_CUR_MOTION_INFO		*_nu_motinfo_ptr_;

/*
	func:	NUS_MDATA(n) 配列の、「n」 を指定する。
	
	in:		_nu_motinfo_ptr_->mdata		= motion->mdata;		//NUS_MDATA(n) の先頭
			(NUS_MDATA(n) 配列への参照ポインタ)
			
			MDataCnt						= 使用する MATA(n) の n
			
	note:	・ポインタは 4 バイトであると仮定する。
			・NUS_MDATA(n)	は次の形式であることを仮定する。
			
			typedef struct {
				void		*p[n];
				Uint32		nb[n];
			} NUS_MDATA(n);
*/


void (*_nu_motion_callback_)(NJS_CNK_OBJECT *obj) = NULL;

#pragma	inline(_nuSetMDataCnt)
static void _nuSetMDataCnt( int MDataCnt )
{
	unsigned char			*pMData;
	
	MDataCnt		*= 4;
	
	_nu_motinfo_ptr_->mdata_nbkeys_ofs	= MDataCnt;
	_nu_motinfo_ptr_->mdata_size		= MDataCnt + MDataCnt;
	
	
	pMData			= (unsigned char *)_nu_motinfo_ptr_->mdata;
	
	_nu_motinfo_ptr_->key			= (void **)(pMData);
	_nu_motinfo_ptr_->nbkeys		= (Uint32 *)(pMData + _nu_motinfo_ptr_->mdata_nbkeys_ofs);
	_nu_motinfo_ptr_->cnt			= 0;
}

/*
	func:	NUS_MDATA(n) 配列への参照ポインタが一つ進められる。
			オブジェクトモーションにおいて、次のノードに進むために使用する。
	
	note:	_nuSetMDataCnt( MDataCnt );		を呼び出しておくこと。
*/
void nuSetNextMotionNodeEx( void )
{
	unsigned char			*pMData;
	
	pMData						= (unsigned char *)_nu_motinfo_ptr_->mdata;
	pMData						+= _nu_motinfo_ptr_->mdata_size;
	_nu_motinfo_ptr_->mdata		= (void *)pMData;
	
	_nu_motinfo_ptr_->key		= (void **)(pMData);
	_nu_motinfo_ptr_->nbkeys	= (Uint32 *)(pMData + _nu_motinfo_ptr_->mdata_nbkeys_ofs);
	_nu_motinfo_ptr_->cnt		= 0;
}


/*
	func:	現在のモーションパラメータを設定する。
			NUS_MDATA(n) 配列への参照ポインタが、その先頭にセットされる。
			
	in:		_nu_motinfo_ptr_	= モーション情報構造体へのポインタ（内容はどうでもよい）
			
			motion		= モーション
			frame		= フレーム番号
			
	out:	_nu_motinfo_ptr_	= モーション情報
	
	注意:	この関数は、_nu_motinfo_ptr_ に対して出力を行う。
			nuSetCurMotionInfoSlot() などを用いて必ず初期化すること。
*/
void nuStartMotionEx( const NUS_MOTION *motion, Float frame )
{
	int							spline_type;
    static const NUS_MOTION		DummyMotion = {
        NULL,           //mdata
        0,				//nbFrame
        (Uint16)0,		//type
        (Uint16)0		//inp_fn
    };
    
    if ( motion == NULL ) {
        motion  = &DummyMotion;
    }
	
	
	/*
		motion->type	= NUD_MTYPE_POS など
		motion->inp_fn	= 補完係式(NUD_MTYPE_LINEAR など) と NUS_MDATA(n) の n
	*/
	spline_type					= ( motion->inp_fn & NUD_MTYPE_MASK ) > 0;
	
	_nu_motinfo_ptr_->mdata		= motion->mdata;
	_nu_motinfo_ptr_->nbframes	= motion->nbFrame;
	_nu_motinfo_ptr_->type		= (Uint32)motion->type;
	
	_nu_motinfo_ptr_->frame		= frame;
	_nu_motinfo_ptr_->cnt		= 0;
	
	
	_nuSetMDataCnt( motion->inp_fn & ( ~NUD_MTYPE_MASK ) );
	
	//	Float3 補完関数の選択
	_nu_motinfo_ptr_->int_f3_func_p		= _nu_motion_int_f3_func_table_[spline_type];
	
	//	Angle3 補完関数の選択
	_nu_motinfo_ptr_->int_a3_func_p		= _nu_motion_int_a3_func_table_[spline_type];
	
	//	Angle1 補完関数の選択
	_nu_motinfo_ptr_->int_a1_func_p		= _nu_motion_int_a1_func_table_[0];

	//	Sangle3 補完関数の選択
	_nu_motinfo_ptr_->int_sa3_func_p		= _nu_motion_int_sa3_func_table_[spline_type];

}

#if	0
/*
	func:	_nuSetMotionKeySlot	を呼び出す前に呼び出す。
			motion->type を参照し、０番から SlotMax で指定されたキースロット番号−１までの
			全キースロットの位置を計算し、_nu_motinfo_ptr_->slot_loc に格納する。
*/
void _nuInitMotionKeySlot( const NUS_MOTION *motion, int SlotMax )
{
	int				i;
	Uint16			data_type;
	Uint32			Location;
	Uint32			*pSlotLoc;
	Uint32			*pTypeBits;
	static Uint32	TypeBits[NUD_MTYPE_SLOT_MAX]	= {
		NUD_MTYPE_POS_0 | NUD_MTYPE_VEC_0,
		NUD_MTYPE_ANG_1 | NUD_MTYPE_QUAT_1 | NUD_MTYPE_SANG_1,
		NUD_MTYPE_SCL_2,
		NUD_MTYPE_VEC_3 | NUD_MTYPE_TARGET_3,
		0,
		0,
		NUD_MTYPE_ROLL_6,
		NUD_MTYPE_ANGLE_7,
		NUD_MTYPE_RGB_8,
		NUD_MTYPE_INTENSITY_9|NUD_MTYPE_POINT_9,
		NUD_MTYPE_SHAPEID,
	};
	
	data_type				= motion->type;
	
	Location	= 0;
	pSlotLoc	= &_nu_motinfo_ptr_->slot_loc[0];
	pTypeBits	= &TypeBits[0];
	for ( i = 0; i < SlotMax; i++ ) {
		*pSlotLoc++		= Location;
		
		if ( data_type & (*pTypeBits++) ) {
			Location++;
		}
	}
}

/*
	func:	あらかじめ、_nuInitMotionKeySlot	を呼び出しておく。
			引数 KeySlot で指定されたスロットをカレントスロットにする。
			
			本関数を呼び出さなければ、スロットを参照する関数を呼び出すたびに次のスロットが
			参照されるように成る。
*/
void _nuSetMotionKeySlot( int KeySlot )
{
	_nu_motinfo_ptr_->cnt	= _nu_motinfo_ptr_->slot_loc[KeySlot];
}
#endif

/*
	func:	モーション関数群が読み書きするモーション情報構造体を指定する。
	
	expl.:	この指定は、次にこの関数や、nuSetCurMotionInfoSlot() が呼び出されるまで
			有効である。
*/
void nuSetCurMotionInfoPtr( const NUS_CUR_MOTION_INFO *motion_info_ptr )
{
	_nu_motinfo_ptr_	= motion_info_ptr;
}

/*
	func:	モーション関数群が読み書きするモーション情報構造体のスロット番号を指定する。
	
	in:		Slot	= モーション構造体のスロット番号
					  0 〜 NUD_MOTION_INFO_MAX - 1
	
	expl.:	この指定は、次にこの関数や、nuSetCurMotionInfoPtr() が呼び出されるまで
			有効である。
*/
void nuSetCurMotionInfoSlot( int Slot )
{
	_nu_motinfo_ptr_	= &_nu_motion_info_[Slot];
}

//	_nuMotionSearchKey : このファイル内ではインライン展開するが、外部にも関数宣言するので関数呼び出しも出来る。
#pragma	inline(_nuMotionSearchKey)
Uint32 _nuMotionSearchKey( const void *key, int KeySize, Uint32 nbkeys, Uint32 frame )
{
	Uint32		nb_a;
	Uint32		nb_b;
	Uint32		nb_mid;
	
	nb_a	= 0;
	nb_b	= nbkeys;
	while ( nb_b - nb_a > 1 ) {
		nb_mid = ( nb_a + nb_b ) >> 1;
		
		if ( frame >= *(Uint32*)( ((unsigned char *)key) + (nb_mid*KeySize) ) ) {
			nb_a = nb_mid;
		}
		else {
			nb_b = nb_mid;
		}
	}
	
	return	nb_a;
}

void _nuGetMotionLinearKeys( const void *key, int KeySize, Uint32 nbkeys, Float frame,
							 const void **pKey1, const void **pKey2, Float *pRate )
{
	Uint32			keyno;
	Sint32			DiffKeyNo;
	Uint32			keyframe1;
	Uint32			keyframe2;
	Uint32			*key1;
	Uint32			*key2;
	
	keyno	= _nuMotionSearchKey( key, KeySize, nbkeys, (Uint32)frame );
	
	key1	= (Uint32 *)( ((unsigned char *)key) + keyno * KeySize );
	if ( keyno < nbkeys - 1 ) {
		key2	= (Uint32 *)( ((unsigned char *)key) + (keyno+1) * KeySize );
	}
	else {
		key2	= (Uint32 *)key;
	}
	//key2	= (Uint32 *)( ((unsigned char *)key) + ((keyno+1) % nbkeys) * KeySize );
	
	*pKey1	= key1;
	*pKey2	= key2;
	
	keyframe1	= *key1;
	keyframe2	= *key2;
	
	if ( ( DiffKeyNo = (Sint32)keyframe2 - (Sint32)keyframe1 ) > 0 ) {
		*pRate	= ( frame - (Float)keyframe1 ) / (Float)DiffKeyNo;
	}
	else {
		*pRate	= frame - (Float)keyframe1;
	}
}


#pragma	inline(_nuMotionSearchKey16)
Uint32 _nuMotionSearchKey16( const void *key, int KeySize, Uint32 nbkeys, Uint16 frame )
{
	Uint32		nb_a;
	Uint32		nb_b;
	Uint32		nb_mid;
	
	nb_a	= 0;
	nb_b	= nbkeys;
	while ( nb_b - nb_a > 1 ) {
		nb_mid = ( nb_a + nb_b ) >> 1;
		
		if ( frame >= *(Uint16*)( ((unsigned char *)key) + (nb_mid*KeySize) ) ) {
			nb_a = nb_mid;
		}
		else {
			nb_b = nb_mid;
		}
	}
	
	return	nb_a;
}

void _nuGetMotionLinearKeys16( const void *key, int KeySize, Uint32 nbkeys, Float frame,
							 const void **pKey1, const void **pKey2, Float *pRate )
{
	Uint32			keyno;
	Sint16			DiffKeyNo;
	Uint16			keyframe1;
	Uint16			keyframe2;
	Uint16			*key1;
	Uint16			*key2;
	
	keyno	= _nuMotionSearchKey16( key, KeySize, nbkeys, (Uint16)frame );
	
	key1	= (Uint16 *)( ((unsigned char *)key) + keyno * KeySize );
	if ( keyno < nbkeys - 1 ) {
		key2	= (Uint16 *)( ((unsigned char *)key) + (keyno+1) * KeySize );
	}
	else {
		key2	= (Uint16 *)key;
	}
	//key2	= (Uint32 *)( ((unsigned char *)key) + ((keyno+1) % nbkeys) * KeySize );
	
	*pKey1	= key1;
	*pKey2	= key2;
	
	keyframe1	= *key1;
	keyframe2	= *key2;
	
	if ( ( DiffKeyNo = (Sint16)keyframe2 - (Sint16)keyframe1 ) > 0 ) {
		*pRate	= ( frame - (Float)keyframe1 ) / (Float)DiffKeyNo;
	}
	else {
		*pRate	= frame - (Float)keyframe1;
	}
}

void
nuSetMotionCallback( void (*func)(NJS_CNK_OBJECT *obj) )
{
	_nu_motion_callback_ = func;
}
