/*
 *	clipanms.c
 *
 *	ステートレスアニメーションシーケンサ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2016 Naoyuki Sawa
 *
 *	* Sat Mar 05 21:53:08 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sun Mar 06 21:12:57 JST 2016 Naoyuki Sawa
 *	- AnmSeqNo_GetVal()を追加しました。
 *	* Mon Mar 07 21:56:21 JST 2016 Naoyuki Sawa
 *	- AnmSeq_GetVal()の中の無駄な判定を削除して、コードを削減しました。
 *	* Tue Mar 29 21:02:48 JST 2016 Naoyuki Sawa
 *	- AnmSeq_GetLen(),AnmSeqNo_GetLen()を追加しました。
 *	  アニメーションの全体尺を取得するための関数で、tEndの値を返すだけの動作です。(ループ/非ループに関係無くtEndの値を返します)
 *	  (非ループデータならば)AnmSeq_GetVal()の戻り値が-1になるまで呼び出して全体尺を取得する事も出来るのですが、より直接的に全体尺を取得したい場合が有ったので当関数を追加する事にしました。
 *	- アプリケーションが直接pAnmSeq->tEndを参照しても構わないのですが、'dAnmSeqC.exeが出力したバイナリ形式を扱うためのユーティリティ関数'ではアプリケーションがpAnmSeqを保持していません。
 *	  従ってAnmSeqNo_GetLen()を追加する必要が有り、AnmSeqNo_GetLen()との対称性のために、AnmSeq_GetLen()も追加する事にしました。
 *	* Mon Sep 05 21:24:05 JST 2016 Naoyuki Sawa
 *	- (int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
 *	  詳細は、AnmSeq_GetVal_compar(),AnmSeq_GetVal()内のコメントを参照して下さい。
 */
#include "clip.h"
/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/
static int AnmSeq_GetVal_compar(const void* _x, const void* _y) {
//{{2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
//	int x = (int)_x;
//↓2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
#ifndef _WIN64
	int x = (int)_x;	//32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
#else //_WIN64
	int x = (intptr_t)_x;	//64ビット版は、(void*)(64ビット)⇒(int)(32ビット)にキャストしようとすると警告が出ます。元の値が32ビットの整数パラメータなので(int)にキャストしても安全ではあるのですが、警告を回避するために(void*)(64ビット)⇒(intptr_t)(64ビット)⇒暗黙の(int)(32ビット)へのキャスト、という手順を取る事にしました。
#endif//_WIN64
//}}2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
	int y = ((ST_AnmKey*)_y)->tKey;
	if(x < y) { return -1; }
	if(y > y) { return  1; }
	          { return  0; }
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int AnmSeq_GetVal(const ST_AnmSeq* pAnmSeq, int tNow, int* tNowPtr/*NULL可*/) {
	//フィールドの値を取り出しておく。
	const int tEnd = pAnmSeq->tEnd;			//最終時刻+1
	const int tRep = pAnmSeq->tRep;			//ループ戻り時刻	(tRep≧tEnd)ならばループしない。
	const int nKey = pAnmSeq->nKey;			//キーフレーム数(0〜)
	      int iVal = pAnmSeq->iVal;			//初期値		最初のキーフレーム未満の時刻の値
	const ST_AnmKey* pAnmKey = pAnmSeq->TBL_Key;	//キーフレーム配列
//{{2016/03/07削除:AnmSeq_GetVal()の中の無駄な判定を削除して、コードを削減しました。
//│ * Mon Mar 07 21:56:21 JST 2016 Naoyuki Sawa
//│ - AnmSeq_GetVal()の中の無駄な判定を削除して、コードを削減しました。
//│ - (tNow<0)の場合は初期値を返すために、'if(tNow>=0){〜'という判定を行っていたのですが、よく考えるとこの判定は不要でした。
//│   (tNow<0)ならば、upper_bound()は、pAnmKey[0]へのポインタを返すはずです。(もし(nKey=0)であっても同様で、存在しないpAnmKey[0]へのポインタを返します。)
//│   その後の処理で、一つ前のキーフレームを参照すると、pAnmKey[-1].iValはpAnmSeq->iValを指しますから、初期値を取得出来ます。
//│   結論としては、'if(tNow>=0){〜'という判定を行っても行わなくても同じ結果になるので、コード削減のために'if(tNow>=0){〜'の判定は削除する事にしました。
//	//現在時刻が0以上ならば、現在時刻を含むキーフレームを検索して、値を取得する。
//	//現在時刻が0未満ならば、iVal=初期値 のままとする。
//	if(tNow >= 0) {
//}}2016/03/07削除:AnmSeq_GetVal()の中の無駄な判定を削除して、コードを削減しました。
		//現在時刻が(最終時刻+1)以上ならば、ループ処理を行う。
		if(tNow >= tEnd) {
			//ループ区間の長さを求める。
			int tRepLen = tEnd - tRep;
			//ループ無しならば(-1)を返す。
			if(tRepLen <= 0) { return -1; }
			//ループ処理を行う。
			tNow -= tEnd;
			tNow %= tRepLen;
			tNow += tRep;
			//補正後の現在時刻を格納する。
			if(tNowPtr) { *tNowPtr = tNow; }
		}
		//(tNow＜pAnmKey->tKey)となる、最初のキーフレームを取得する。
		//(tNow＜pAnmKey->tKey)となるキーフレームが無ければ、(最後のキーフレーム+1)のポインタを取得する。	←←←upper_bound()の仕様です。
//{{2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
//		pAnmKey = upper_bound(
//			(void*)tNow,
//			pAnmKey,
//			nKey,
//			sizeof(ST_AnmKey),
//			AnmSeq_GetVal_compar);
//↓2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
#ifndef _WIN64
		pAnmKey = upper_bound(		//32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
			(void*)tNow,
			pAnmKey,
			nKey,
			sizeof(ST_AnmKey),
			AnmSeq_GetVal_compar);
#else //_WIN64
		pAnmKey = upper_bound(		//64ビット版は、(int)(32ビット)⇒(void*)(64ビット)に直接キャストしようとすると警告が出ます。(int)(32ビット)⇒(intptr_t)(64ビット)⇒(void*)(64ビット)の順でキャストすれば警告は出ません。
			(void*)(intptr_t)tNow,
			pAnmKey,
			nKey,
			sizeof(ST_AnmKey),
			AnmSeq_GetVal_compar);
#endif//_WIN64
//}}2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
		//「(tNow＜pAnmKey->tKey)となる最初のキーフレームの、一つ前のキーフレーム」又は「最後のキーフレーム」が、tNowを含むキーフレームである。
		pAnmKey--;
		//一つ前のキーフレームが有れば、キーフレームの値を取得する。
		//一つ前のキーフレームが無ければ、iVal=初期値 のままとする。
		/*if(pAnmKey >= pAnmSeq->TBL_Key)*/ { iVal = pAnmKey->iVal; }
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~この判定は行っても行わなくても同じ結果になります。
		//                               TBL_Key[-1].iValがST_AnmSeq.iValを指しているので、一つ前のキーフレームが無い時にpAnmKey->iValを取得すると、初期値が取得されるからです。
//{{2016/03/07削除:AnmSeq_GetVal()の中の無駄な判定を削除して、コードを削減しました。
//	}
//}}2016/03/07削除:AnmSeq_GetVal()の中の無駄な判定を削除して、コードを削減しました。
	//現在時刻の値を返す。
	return iVal;
}
/*--------------------------------------------------------------------------*/
int AnmSeq_GetLen(const ST_AnmSeq* pAnmSeq) {
	return pAnmSeq->tEnd;
}
/****************************************************************************
 *	dAnmSeqC.exeが出力したバイナリ形式を扱うためのユーティリティ関数
 ****************************************************************************/
#ifdef  USE_ANMSEQ_BIN
static const ST_AnmSeq* AnmSeqNo_GetAnmSeq(const void* pAnmSeqBin, int iAnmSeqNo) {
	const int* const TBL_AnmSeqNo = pAnmSeqBin;				//バイナリの先頭にアドレステーブルが有る。アドレスはデータ本体(WORD配列)のインデクスである。
	const int nAnmSeqNo = TBL_AnmSeqNo[0];					//アドレステーブルの先頭[0]に要素数が入っている。要素数はそれ自身([0])も含む。
	const int* const TBL_AnmSeq = (const int*)&TBL_AnmSeqNo[nAnmSeqNo];	//アドレステーブルの直後からデータ本体(WORD配列)が開始する。
	if((iAnmSeqNo <= 0) || (iAnmSeqNo >= nAnmSeqNo)) { DIE(); }
	return (const ST_AnmSeq*)&TBL_AnmSeq[TBL_AnmSeqNo[iAnmSeqNo]];
}
int AnmSeqNo_GetVal(const void* pAnmSeqBin, int iAnmSeqNo, int tNow, int* tNowPtr/*NULL可*/) {
	const ST_AnmSeq* pAnmSeq = AnmSeqNo_GetAnmSeq(pAnmSeqBin, iAnmSeqNo);
	return AnmSeq_GetVal(pAnmSeq, tNow, tNowPtr);
}
int AnmSeqNo_GetLen(const void* pAnmSeqBin, int iAnmSeqNo) {
	const ST_AnmSeq* pAnmSeq = AnmSeqNo_GetAnmSeq(pAnmSeqBin, iAnmSeqNo);
	return AnmSeq_GetLen(pAnmSeq);
}
#endif//USE_ANMSEQ_BIN
/****************************************************************************
 *	使用例
 ****************************************************************************/
#if 0
────────────────────────────────────────
■例1
const ST_AnmSeq stAnmSeq={
	 9,	//tEnd
	 5,	//tRep	ループ有り
	 2,	//nKey
	 1,	//iVal
};
const ST_AnmKey TBL_AnmKey[]={
	{3,2},	//TBL_Key[0]
	{6,3}	//TBL_Key[1]
};
void test() {
	int tNow = -3;
	for(;;){
		int iVal = AnmSeq_GetVal(&stAnmSeq,tNow,&tNow);
		printf("%d\t%d\n",tNow,iVal);
		tNow++;
	}
}
□結果
│	-3	 1	┐
│	-2	 1	├現在時刻が0未満ならば初期値を返す。
│	-1	 1	┘
│	 0	 1	┐
│	 1	 1	├現在時刻が最初のキーフレームの時刻未満ならば初期値を返す。
│	 2	 1	┘
│	 3	 2	┐
│	 4	 2	├TBL_Key[0]
│	 5	 2	┘
│	 6	 3	┐
│	 7	 3	├TBL_Key[1]
│	 8	 3	┘
│	 5	 2	←ループして補正後の現在時刻を格納された。
│	 6	 3
│	 7	 3
│	 8	 3
────────────────────────────────────────
■例2
const ST_AnmSeq stAnmSeq={
	 9,	//tEnd
	-1,	//tRep	ループ無し
	 2,	//nKey
	 1,	//iVal
};
const ST_AnmKey TBL_AnmKey[]={
	{3,2},	//TBL_Key[0]
	{6,3}	//TBL_Key[1]
};
void test() {
	int tNow = -3;
	for(;;){
		int iVal = AnmSeq_GetVal(&stAnmSeq,tNow,&tNow);
		printf("%d\t%d\n",tNow,iVal);
		tNow++;
	}
}
□結果
│	-3	 1	┐
│	-2	 1	├現在時刻が0未満ならば初期値を返す。
│	-1	 1	┘
│	 0	 1	┐
│	 1	 1	├現在時刻が最初のキーフレームの時刻未満ならば初期値を返す。
│	 2	 1	┘
│	 3	 2	┐
│	 4	 2	├TBL_Key[0]
│	 5	 2	┘
│	 6	 3	┐
│	 7	 3	├TBL_Key[1]
│	 8	 3	┘
│	 9	-1	←ループせずに終了したら(-1)を返す。
│	10	-1
│	11	-1
────────────────────────────────────────
#endif
