/*
 *	clipcbkq.h
 *
 *	コールバックキュー
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2016 Naoyuki Sawa
 *
 *	* Thu Jan 28 21:43:35 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	- 主に、描画処理のレイヤ管理を行うために作成したモジュールです。
 *	  個々のオブジェクトの計算処理の中で、描画関数を登録しておいて、あとでまとめて描画するような使い方です。
 *	- 一つのオブジェクトが複数のレイヤに描画する場合も、異なる優先度で描画関数を複数回登録しておけば対応出来ます。
 *	  その場合、複数回登録する描画関数は、同じであっても異なっていても構いません。
 *	  同じである場合は、コールバック関数に渡された優先度やパラメータで、どのレイヤの画像を描くかを判断して下さい。
 *	- CbkQue_RemoveIf()とCbkQue_CallIf()は、条件をかなり柔軟に指定出来るようにしました。
 *	  上手く優先度を設計すれば、背景用レイヤの登録だけを削除したり、背景と前景を分けてコールバックしたり出来ます。
 *	  また、関数ポインタとパラメータを指定すれば、特定のオブジェクトが登録したノードだけを登録解除したり出来ます。
 *	  とは言え、ほとんどの場合は必要無いと思うので、CbkQue_Remove()とCbkQue_Call()だけを使えば充分かも知れません。
 *	* Sat Jan 30 21:30:05 JST 2016 Naoyuki Sawa
 *	- 全ての関数を大幅に変更しました。
 *	  変更前のコードはコメントアウトして残していません。
 *	  変更前のコードを参照する必要が生じた場合は、前回のアーカイブを参照して下さい。
 *	- 主な変更点は、以下の通りです。
 *	  ・変更前は、当モジュールの機能は、ノードを登録して呼び出す事だけでした。
 *	    ノードに付随するパラメータのための、メモリ確保や開放(必要ならば)は、アプリケーション側で行う必要が有りました。
 *	    実際に使って見たところ、パラメータとしてポインタ一つでは大抵不足であり、常にメモリ確保を行う必要が生じました。
 *	    そこで、パラメータのためのメモリ確保や開放の機能も、当モジュールに取り込む事にしました。
 *	    ノード登録もメモリ確保も同じバッファを使って行うので、もしメモリ確保を使わなくてもバッファの無駄は生じません。
 *	  ・ノードを登録解除する機能を単純化して、CbkQue_Clear()だけにしました。
 *	    変更前はCbkQue_RemoveIf()に登録解除条件を指定出来ましたが、その機能を使う事はほとんど無い事と思ったからです。
 *	  ・バッファ容量が不足した時の動作を変更しました。
 *	    変更前はエラーコードを返していましたが、変更後はエラー停止,又は,例外を投入するようにしました。
 *	    そうした理由は、アプリケーションがCbkQue_Add()/CbkQue_AddMem()を呼び出す箇所がかなり多くなると思ったからです。
 *	    アプリケーションが、全てのエラーコードを確認するのは、見落としの危険が大きいと思ったからです。
 *	    それよりも、エラー停止してバッファ容量を調整する事を促すか、又は、まとめて例外ハンドラで処理する方が安全です。
 *	- コールバックキューの応用例として、コールバックキューを用いた優先度付き描画を作成しました。
 *	  詳細は、clipcqrd.h,clipcqrd.c,及び,test.pce/app.cを参照して下さい。
 *	* Sun Jan 31 21:43:46 JST 2016 Naoyuki Sawa
 *	- 優先度が同じ場合、登録順序の通りにコールバックするようにしました。
 *	  これまでは優先度が同じ場合コールバック順が不定でしたが、登録順序の通りにコールバックする方が、使い易いと思ったからです。
 *	  この変更の影響で、優先度に指定出来る値の範囲が、(変更前:INT_MIN〜INT_MAX)⇒(変更後:SHRT_MIN〜SHRT_MAX) に狭まりました。
 *	* Sun Sep 04 21:41:17 JST 2016 Naoyuki Sawa
 *	- 64ビット版では、例外コードをアドレスでなく、一意なFourCC識別子で表すようにしました。
 *	  setjmp()/longjmp()のパラメータは(int)であり、(void*)や(intptr_t)を使えないからです。
 *	  尚、32ビット版も64ビット版と共通に変更しても良いのですが、万一既存のコードに影響が生じる事を防ぐために、32ビット版はこれまで通りとしました。
 *	* Wed Oct 19 21:15:15 JST 2016 Naoyuki Sawa
 *	- 動的コールバックキューの機能を追加しました。
 *	  動的コールバックキューとして動作するか、(これまでと同じ)静的コールバックキューとして動作するかは、作成時に使用する関数によって選択出来ます。
 *	  StaticCbkQue_New()を呼び出して作成するか、StaticCbkQue_Init()を直接呼び出して初期化すれば、静的コールバックキューになります。
 *	  DynamicCbkQue_New()を呼び出して作成すれば、動的コールバックキューになります。
 *	  上記のように、作成時に使用する関数名を変更したので、既存のアプリケーションも修正が必要となります。
 *	- 今回、動的コールバックキューの機能を追加した理由は、必要に応じてコールバックキューのサイズを拡張したいケースが生じたからです。
 *	  元々の静的コールバックキューは、組み込み環境等で動的なメモリ確保を伴わずに優先度付きのキューを管理できるようにするために作成したものでした。
 *	  そのため、アプリケーション側で静的に確保したメモリを、StaticCbkQue_Init()(変更前の関数名はCbkQue_Init())で初期化して使用する事を想定していました。
 *	  WindowsやP/ECE等、動的メモリ確保が利用し易い環境では、アプリケーション側でメモリを静的に確保しなくても良いように、StaticCbkQue_New()(変更前の関数名はCbkQue_New())も用意しましたが、
 *	  初期化時にコールバックキューのサイズが固定されてしまう点は同じであり、やや使い辛いケースが有りました。
 *	  コールバックキューのサイズが不足にならないよう、想定される最大サイズでコールバックキューを作成しておく必要が有り、予測が難しいと無駄に大きなサイズで初期化する必要が生じるからです。
 *	- そこで今回、ノードやメモリの使用量に応じて自動的にサイズを拡張する、動的コールバックキューの機能を追加する事にしました。
 *	  組み込み環境では使用し辛いですが、WindowsやP/ECE等では使い易いと思います。
 *	  ただし、組み込み環境用に、(これまでと同じ)静的コールバックキューの機能も残しておく必要が有りました。
 *	  そのための方法として、既存の静的コールバックキューとは別のモジュールで、新たに動的コールバックキューを追加する事も検討したのですが、それは出来ませんでした。
 *	  既に、DxlibHelper.cppモジュールやclipcqrd.c等が、既存の静的コールバックキューの関数名に依存してしまっているからです。
 *	  そこで、作成,及び,初期化以外のコールバックキューの関数名はそのままで、作成,及び,初期化時に両方のコールバックキューのタイプを選択する事にしました。
 *	- アプリケーションの変更方法は、おおよそ以下の通りです。
 *	  組み込み環境等で、静的コールバックキューを静的に初期化していた場合は、静的に定義していた構造体の名前をST_CbkQue⇒ST_StaticCbkQueに変更するだけで済むと思います。
 *	  WindowsやP/ECEで、CbkQue_New()でコールバックキューを作成していた場合は、StaticCbkQue_New(),又は,DynamicCbkQue_New()に変更して下さい。
 *	- 今回の変更は、変更箇所が多かったので、変更前のコードはコメントアウトして残していません。
 *	  変更前のコードを参照する必要が有る場合は、前回のアーカイブを参照して下さい。
 *	* Wed Oct 26 21:45:51 JST 2016 Naoyuki Sawa
 *	- DynamicCbkQue_Clear()の中で、MemPool_Clear()の呼び出しを忘れていたバグを修正しました。
 *	  修正前は、メモリプールから常に新しいメモリチャンクを確保し続けて、メモリ使用量が際限無く増えてしまっていました。
 *	  単純なバグですが、Windows環境ではメモリが多いため数時間放置しないとメモリ不足にならず、気付きづらいバグでした。
 */
#ifndef __CLIP_CBKQ_H__
#define __CLIP_CBKQ_H__
#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus
#define USE_CBKQUE_SEH	//このシンボルを定義すると、clipcbkq.c内のDIE()を全て、SEH_throw(CbkQueException)に置き換えます。
#ifdef  USE_CBKQUE_SEH
//{{2016/09/04変更:64ビット版では、例外コードをアドレスでなく、一意なFourCC識別子で表すようにしました。
//extern const char CbkQueException[1/*dummy*/];
//↓2016/09/04変更:64ビット版では、例外コードをアドレスでなく、一意なFourCC識別子で表すようにしました。
#ifndef _WIN64
extern const char CbkQueException[1/*dummy*/];		//32ビット版
#else //_WIN64
#define CbkQueException MAKEFOURCC('C','b','k','Q')	//64ビット版
#endif//_WIN64
//}}2016/09/04変更:64ビット版では、例外コードをアドレスでなく、一意なFourCC識別子で表すようにしました。
#endif//USE_CBKQUE_SEH
/****************************************************************************
 *	構造体
 ****************************************************************************/
//ST_StaticCbkQueとST_DynamicCbkQueの共通部分
typedef struct _ST_CbkQue {
	int			isStatic;						//0ならばST_DynamicCbkQue,0以外ならばST_StaticCbkQueである事を示す。	(ST_StaticCbkQue.bufWords),又は(ST_DynamicCbkQue.dummy)に相当する。
} ST_CbkQue;
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//静的コールバックキュー(動的メモリ確保を伴わない)
typedef struct _ST_StaticCbkQue {
	int			bufWords;						//バッファの容量(ワード数)	0以外					(ST_CbkQue.isStatic)に相当する。
	int			nodeTail;						//次にノードを登録する位置	0→bufWords				buf[]へのインデクスであり、ワード単位です。
	int			memHead;						//最後に確保したメモリの位置	0←bufWords
//	int			buf[bufWords];						//バッファ			ノード登録/メモリ確保で共用する。
} ST_StaticCbkQue;
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//動的コールバックキュー(動的メモリ確保を伴う)
typedef struct _ST_DynamicCbkQue {
	int			dummy;							//ダミー			0					(ST_CbkQue.isStatic)に相当する。
	int			nodeTail;						//次にノードを登録する位置	0→(nodeTail->len)			nodeArrayへのインデクスであり、ノードの個数単位です。
	struct _GArray*		nodeArray;						//ノード配列
	struct _ST_MemPool*	memPool;						//メモリプール
} ST_DynamicCbkQue;
/*--------------------------------------------------------------------------*/
//ノード
typedef struct _ST_CbkQueNode {
	int			priOrd;							//[31:16]=優先度(符号付き),[15:0]=登録順序(符号無し)。[31:0]を符号付き(INT_MIN〜INT_MAX)と見なして昇順にソートする。
	void			(*fn)(ST_CbkQue* pCbkQue, int pri, void* param);	//関数ポインタ
	void*			param;							//アプリケーション定義の任意のパラメータ
} ST_CbkQueNode;
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
//静的コールバックキュー(動的メモリ確保を伴わない)
ST_CbkQue* StaticCbkQue_New(int bufBytes/*4N,0不可*/);					//この関数を呼び出して作成したコールバックキューは、CbkQue_Free()を呼び出して解放して下さい。
ST_CbkQue* StaticCbkQue_Init(ST_StaticCbkQue* pCbkQue, int bufBytes/*4N,0不可*/);	//この関数を直接呼び出して静的に初期化したコールバックキューは、解放不要です。
/*--------------------------------------------------------------------------*/
//動的コールバックキュー(動的メモリ確保を伴う)
ST_CbkQue* DynamicCbkQue_New(int chunkBytes/*0可*/);					//この関数を呼び出して作成したコールバックキューは、CbkQue_Free()を呼び出して解放して下さい。
//ST_CbkQue* DynamicCbkQue_Init(ST_DynamicCbkQue* pCbkQue, int chunkBytes/*0可*/);	//この関数は、モジュール内には存在しますが、アプリケーションには公開しません。アプリケーションの観点からは、動的コールバックキューを静的に初期化する事は、無意味だからです。
/*--------------------------------------------------------------------------*/
//共通処理
void CbkQue_Free(ST_CbkQue* pCbkQue);
void CbkQue_Clear(ST_CbkQue* pCbkQue);
void CbkQue_Add(ST_CbkQue* pCbkQue, int pri/*SHRT_MIN〜SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), void* param);
void* CbkQue_AddMem(ST_CbkQue* pCbkQue, int pri/*SHRT_MIN〜SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), int memBytes);
void CbkQue_Invoke(ST_CbkQue* pCbkQue);
void CbkQue_InvokePri(ST_CbkQue* pCbkQue, int priMin/*SHRT_MIN〜SHRT_MAX*/, int priMax/*SHRT_MIN〜SHRT_MAX*/);
#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus
#endif//__CLIP_CBKQ_H__
