/*
 *	clipcntm.h
 *
 *	汎用カウンタ管理
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2016 Naoyuki Sawa
 *
 *	* Sun Apr 13 21:02:48 JST 2014 Naoyuki Sawa
 *	- 1st リリース。
 *	- Sound Mixer(clipsndx.*)を元にして作成しました。
 *	  Sound Mixer(clipsndx.*)と処理内容が似ていますが、下記の相違のために共通化することは難しく、別機能として分けることにしました。
 *	  ・Sound Mixerは8ビット値であるが、汎用カウンタ管理は8ビットでは足りない。
 *	  ・Sound Mixerは各チャネルのサウンドチャネルマスクが必要だが、汎用カウンタ管理には必要無い。
 *	  ・Sound Mixerは各チャネルの減衰量の合計を求める必要があるが、汎用カウンタ管理には必要無い。
 *	  ・Sound Mixerは割込処理から呼ばれる可能性を考慮して排他制御の必要があるが、汎用カウンタ管理は(その目的から考えて)割込処理から呼ばれることは無く排他制御は必要無い。
 *	* Wed Feb 04 21:22:53 JST 2015 Naoyuki Sawa
 *	- Hewは要素数[0]の配列に対応していないので、要素数[1]として調整しました。
 *	  P/ECE開発環境(GCC)やWindows(VC++6.0)は、要素数[0]の配列に対応しているので、関係有りません。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 *	* Thu Feb 04 21:41:31 JST 2016 Naoyuki Sawa
 *	- 値の範囲を、(0〜UINT_MAX)⇒(INT_MIN〜INT_MAX)に変更しました。
 *	  これまでは、メモリ節約のために(0〜UINT_MAX)としていたのですが、実用上この範囲では足りない事が判ったので、(INT_MIN〜INT_MAX)に変更する事にしました。
 *	  今回の変更によって、メモリ使用量が2倍になりました。
 *	  しかし、極端に多くのチャネルを使うようなアプリケーションでなければ、大きな問題にはならないと思います。
 *	- 関数名を、旧:CntMgr_Init()⇒新:CntMgr_New()、旧:CntMgr_InitStatic()⇒新:CntMgr_Init() に変更しました。
 *	  最近作成している他のモジュールの関数名に合わせるためです。
 *	  今後は、まだ古い名前のままになっている他の古いモジュールの関数も、上記の関数名に合わせて行く予定です。
 *	* Wed Mar 02 21:54:20 JST 2016 Naoyuki Sawa
 *	- '前の変化が終わる前に、次の変化を開始した時'の動作を変更しました。
 *	  変更前は、前の変化の途中の現在値から、新しい目標値へ、新しい変化速度で変化を開始していました。
 *	  変更後は、前の変化を即完了させて、前の変化の目標値(即完了した結果の値)から、新しい目標値へ、新しい変化速度で変化を開始するようにしました。
 *	  要するに、前の変化が完了しているか未完了であるかに関係無く、新しい変化の動作は同じになります。
 *	- これまで、'前の変化が終わる前に、次の変化を開始した時'に、前の変化の途中の現在値からスムーズに繋がるようにしていたのは、意図的なものだったのですが、
 *	  しばらく使って見た結果、見た目にそのメリットがわかる事はほとんど無かった事と、それよりもデメリットの方が大きい事が判ったので、変更する事にしました。
 *	  変更前の動作で、問題が生じたのは、以下のようなケースです。
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,   0, 0,0);	//即、0にする。
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,1000,10,1);	//0⇒1000に向けて、毎フレーム10づつ変化を開始する。
 *	  │	〜〜〜 この間の時間の長短が、ユーザーの操作タイミングに依存すると仮定する 〜〜〜
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,1010,1,1);	//カウンタが既に1000に到達しているつもりで、1000⇒1010へ向けて、1づつ変化を開始したつもり。
 *	  │							//しかし例えば、ユーザーが素早く操作すると、カウンタの現在値がまだ100あたりの可能性も有る。
 *	  │							//変更前の動作では、カウンタの現在値から、新しい目標値へ、新しい変化速度で変化を開始していた。
 *	  │							//つまり、100⇒1010へ向けて、1づつ変化を開始する事になり、完了までに非常に時間が掛かる事になる。	←←←問題点
 *	- 上記の問題を避けるために、前の変化を確実に完了させてから、新しい変化を開始させる事にしました。
 *	  前の変化を即完了させるために、大きな変化が生じる事も有るのですが、この点が気になる事はほとんど無いと思います。(TAPミキサーも同様の挙動にしています。)
 *	  それよりも、タイミングに依存した動作の不安定さが無くなる事のメリットの方が大きいと思います。
 *	  変更後の動作では、上述の例の操作を行うと、以下の動作になります。
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,   0, 0,0);	//即、0にする。
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,1000,10,1);	//0⇒1000に向けて、毎フレーム10づつ変化を開始する。
 *	  │	〜〜〜 この間の時間の長短が、ユーザーの操作タイミングに依存すると仮定する 〜〜〜
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,1010,1,1);	//カウンタの現在値に関係なく、前の変化が即完了して1000になる。
 *	  │							//その後、1000から1010へ向けて、1づつ変化を開始する。
 *	* Wed Oct 19 21:15:15 JST 2016 Naoyuki Sawa
 *	- 汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
 *	- CntMgr_GetErrTerm()を追加しました。
 */
#ifndef __CLIP_CNTM_H__
#define __CLIP_CNTM_H__
#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus
/****************************************************************************
 *	構造体
 ****************************************************************************/
//汎用カウンタ管理チャネル構造体
typedef struct _ST_CntMgrCh {
	int		ValNow;				//+ 0, 4 現在値		INT_MIN〜INT_MAX
	int		ValTo;				//+ 4, 4 目標値		INT_MIN〜INT_MAX
	int		Rate;				//+ 8, 4 変化速度	0,1〜INT_MAX		CntMgr_Exec()毎に現在値が(Rate/Scale)の速度で変化する。
	int		Scale;				//+12, 4 変化速度	0,1〜INT_MAX		CntMgr_Exec()毎に現在値が(Rate/Scale)の速度で変化する。
	int		ErrTerm;			//+16, 4 誤差項		0〜(Scale-1)
} ST_CntMgrCh;						//=20
/*--------------------------------------------------------------------------*/
//汎用カウンタ管理構造体
typedef struct _ST_CntMgr {
	int		nCntMgrCh;			//+ 0, 4  汎用カウンタ管理チャネル数
#ifndef __RENESAS__
	ST_CntMgrCh	TBL_CntMgrCh[0/*nCntMgrCh*/];	//+ 4,20N 汎用カウンタ管理チャネル構造体配列
#else //__RENESAS__
	ST_CntMgrCh	TBL_CntMgrCh[1/*nCntMgrCh*/];	//+ 4,20N 汎用カウンタ管理チャネル構造体配列		//Hewは要素数[0]の配列に対応していないので、要素数[1]として調整しました。
#endif//__RENESAS__
} ST_CntMgr;						//= 4+20N */
/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/
//汎用カウンタ管理を作成します。
//[in]
//	nCntMgrCh	汎用カウンタ管理チャネル数
//[out]
//	戻り値		汎用カウンタ管理
//[note]
//	- 当関数は、malloc()を使用して、汎用カウンタ管理構造体のメモリを動的に確保します。
//	  動的なメモリ確保を行いたくない場合は、当関数の代りに、CntMgr_Init()を使用してください。
ST_CntMgr* CntMgr_New(int nCntMgrCh);
/*--------------------------------------------------------------------------*/
//静的に確保したメモリを使用して、汎用カウンタ管理を作成します。
//[in]
//	pCntMgr		静的に確保したメモリ
//	nCntMgrCh	汎用カウンタ管理チャネル数
//[note]
//	- 当関数を使用して作成した汎用カウンタ管理は、CntMgr_Free()による開放は不要です。
//[使用例]
//	│struct {
//	│  ST_CntMgr   cntMgr;
//	│  ST_CntMgrCh cntMgrCh[3];
//	│} stCntMgr;
//	│CntMgr_Init(&stCntMgr.cntMgr, 3);
void CntMgr_Init(ST_CntMgr* pCntMgr, int nCntMgrCh);
/*--------------------------------------------------------------------------*/
//汎用カウンタ管理を開放します。
//[in]
//	pCntMgr		汎用カウンタ管理
//			本関数のみ、pCntMgr引数にNULLを指定可能です。
//			「CntMgr_Free(NULL)」の呼び出しは安全であり、何も行いません。
//[note]
//	- アプリケーション終了直前ならば、明示的に汎用カウンタ管理を開放する必要はありません。
//	  汎用カウンタ管理はメモリリソースのみ使用し、割り込みフック等は行っていないからです。
#define CntMgr_Free free
/*--------------------------------------------------------------------------*/
//汎用カウンタ管理の周期処理を行います。
//[in]
//	pCntMgr		汎用カウンタ管理
//[note]
//	- 各汎用カウンタ管理チャネルの現在値変化処理を行います。
//	- アプリケーションが定期的に、当関数を呼び出してください。
//	  周期時間はアプリケーション定義ですが、画面更新周期(1F)が適切です。
void CntMgr_Exec(ST_CntMgr* pCntMgr);
/*--------------------------------------------------------------------------*/
//汎用カウンタ管理チャネルの現在値変化を開始します。
//[in]
//	pCntMgr		汎用カウンタ管理
//	iCntMgrCh	汎用カウンタ管理チャネル番号	0〜(nCntMgrCh-1)
//	ValTo		目標値				INT_MIN〜INT_MAX
//	Rate		変化速度			0,1〜INT_MAX		CntMgr_Exec()毎に現在値が(Rate/Scale)の速度で変化する。
//	Scale		変化速度			0,1〜INT_MAX		CntMgr_Exec()毎に現在値が(Rate/Scale)の速度で変化する。
//[note]
//	- Rate,又は,Scaleの片方にでも0を指定すると、即変化します。
//	- 前の変化が完了する前に当関数を呼んだ場合、前の変化を即完了してから新しい変化を開始します。	//{{2016/03/02コメント追加:'前の変化が終わる前に、次の変化を開始した時'の動作を変更しました。}}
void CntMgr_SetVal(ST_CntMgr* pCntMgr, int iCntMgrCh, int ValTo, int Rate, int Scale);
/*--------------------------------------------------------------------------*/
//汎用カウンタ管理チャネルの現在値を取得します。
//[in]
//	pCntMgr		汎用カウンタ管理
//	iCntMgrCh	汎用カウンタ管理チャネル番号	0〜(nCntMgrCh-1)
//[out]
//	戻り値		現在値
//[note]
//	- 個別の汎用カウンタ管理チャネルの現在値を取得する場合は、CntMgr_GetVal()を使用するのが簡単ですが、
//	  全ての汎用カウンタ管理チャネルの現在値を取得する場合は、CntMgr_GetValAll()を使用する方が効率が良いです。
int CntMgr_GetVal(ST_CntMgr* pCntMgr, int iCntMgrCh);
/*--------------------------------------------------------------------------*/
//全ての汎用カウンタ管理チャネルの現在値を取得します。
// [in]
//	pCntMgr		汎用カウンタ管理
//	ValNow		全ての汎用カウンタ管理チャネルの現在値を格納するための配列
//[out]
//	戻り値		ValNowに、全ての汎用カウンタ管理チャネルの減衰量を格納する
//[note]
//	- 個別の汎用カウンタ管理チャネルの現在値を取得する場合は、CntMgr_GetVal()を使用するのが簡単ですが、
//	  全ての汎用カウンタ管理チャネルの現在値を取得する場合は、CntMgr_GetValAll()を使用する方が効率が良いです。
void CntMgr_GetValAll(ST_CntMgr* pCntMgr, int ValNow[/*nCntMgrCh*/]);
/*--------------------------------------------------------------------------*/
//{{2016/10/19追加:CntMgr_GetErrTerm()を追加しました。
//汎用カウンタ管理チャネルの誤差項を、浮動小数点数で取得します。
//[in]
//	pCntMgr		汎用カウンタ管理
//	iCntMgrCh	汎用カウンタ管理チャネル番号	0〜(nCntMgrCh-1)
//[out]
//	戻り値		誤差項				-(1.0-ε)〜0.0〜(1.0-ε)
//[note]
//	- 変化中でなければ、0.0を返します。
//	  変化中であり、変化方向が増加ならば、0.0⇒ (1.0-ε)の範囲の値を返します。
//	  変化中であり、変化方向が減少ならば、0.0⇒-(1.0-ε)の範囲の値を返します。
//	- 当関数を追加した目的は、計数カウンタのようなアニメーションを作成するためです。
//	  現在の値と、隣の値(+1,又は,-1)を、当関数で取得した値を使って補間して描画すれば、計数カウンタのようなアニメーションになります。
//	  補間する方法は、変化する桁のスクロール,又は,全体のクロスフェード等が考えられます。
//	  変化する桁は、最下位桁だけとは限らない事に注意して下さい。(桁上がり,桁下がりする場合が有るため)
double CntMgr_GetErrTerm(ST_CntMgr* pCntMgr, int iCntMgrCh);
//}}2016/10/19追加:CntMgr_GetErrTerm()を追加しました。
/****************************************************************************
 *	使用例
 ****************************************************************************/
//□使用例
//│enum{//汎用ｶｳﾝﾀ管理ﾁｬﾈﾙ定義
//│CntMgrCh_MdGet,//獲得ﾒﾀﾞﾙ数
//│CntMgrCh_GmLft,//残りｹﾞｰﾑ数
//│CntMgrCh_GmCnt,//消化ｹﾞｰﾑ数
//│CntMgrCh_SIZE};
//│ST_CntMgr* pCntMgr;//汎用ｶｳﾝﾀ管理
//│int TBL_Val[CntMgrCh_SIZE];//内部値
//│void app_main(){//ﾃｽﾄ関数
//│  int ValNow[CntMgrCh_SIZE];
//│  pCntMgr=CntMgr_New(CntMgrCh_SIZE);//汎用ｶｳﾝﾀ管理を初期化
//│  CntMgr_SetVal(pCntMgr,CntMgrCh_GmLft,TBL_Val[CntMgrCh_GmLft]=50,0,0);//残りｹﾞｰﾑ数(即反映)
//│  for(;;){
//│    schedule();
//│    surface_clear(&surface,0);
//│    if(joy&TRG_A){//遊技開始想定
//│      CntMgr_SetVal(pCntMgr,CntMgrCh_MdGet,min(max(0,TBL_Val[CntMgrCh_MdGet]-= 3),999),1,2);//獲得ﾒﾀﾞﾙ数(1/2[f]の頻度で変化)
//│      CntMgr_SetVal(pCntMgr,CntMgrCh_GmLft,min(max(0,TBL_Val[CntMgrCh_GmLft]--  ), 99),1,2);//残りｹﾞｰﾑ数(1/2[f]の頻度で変化)
//│      CntMgr_SetVal(pCntMgr,CntMgrCh_GmCnt,min(max(0,TBL_Val[CntMgrCh_GmCnt]++  ), 99),1,2);//消化ｹﾞｰﾑ数(1/2[f]の頻度で変化)
//│    }
//│    if(joy&TRG_B){//小役入賞想定
//│      CntMgr_SetVal(pCntMgr,CntMgrCh_MdGet,min(max(0,TBL_Val[CntMgrCh_MdGet]+=15),999),1,2);//獲得ﾒﾀﾞﾙ数(1/2[f]の頻度で変化)
//│    }
//│    CntMgr_Exec(pCntMgr);//汎用ｶｳﾝﾀ管理の周期処理
//│    CntMgr_GetValAll(pCntMgr,ValNow);//汎用ｶｳﾝﾀ管理の現在値を取得
//│    render_printf(&render,0, 0,0,3,"           内部値 表示値");
//│    render_printf(&render,0,10,0,3,"獲得ﾒﾀﾞﾙ数 %6d %6d",TBL_Val[CntMgrCh_MdGet],ValNow[CntMgrCh_MdGet]);
//│    render_printf(&render,0,20,0,3,"残りｹﾞｰﾑ数 %6d %6d",TBL_Val[CntMgrCh_GmLft],ValNow[CntMgrCh_GmLft]);
//│    render_printf(&render,0,30,0,3,"消化ｹﾞｰﾑ数 %6d %6d",TBL_Val[CntMgrCh_GmCnt],ValNow[CntMgrCh_GmCnt]);
//│  }
//│}
#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus
#endif//__CLIP_CNTM_H__
