/*
 *	clipfsm.h
 *
 *	有限状態機械
 *
 *	* Sat Jun 20 18:44:16 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sun Jun 21 22:22:12 JST 2015 Naoyuki Sawa
 *	- 当モジュールで使用するデータを生成するための、有限状態機械マップコンパイラを作りました。
 *	  /clip/tool/dFsmMapC/を参照してください。
 *	* Mon Jun 29 21:48:11 JST 2015 Naoyuki Sawa
 *	- 遷移の名前が同じで、ガード条件が異なる、遷移セットを定義出来るようにしました。
 *	  優先順位は、「SMC - The State Machine Compiler」(http://smc.sourceforge.net/)を参考にさせて頂きました。
 *	    1-1-1. 現在の状態の、指定された遷移の、ガードが有る遷移で、ガード条件が成立した遷移	複数有る場合は順不同。現在の実装ではガード名の順としているが、その挙動に依存してはいけない。
 *	    1-1-2. 現在の状態の、指定された遷移の、ガードが無い遷移
 *	    1-2-1. 状態マップの、指定された遷移の、ガードが有る遷移で、ガード条件が成立した遷移	複数有る場合は順不同。現在の実装ではガード名の順としているが、その挙動に依存してはいけない。
 *	    1-2-2. 状態マップの、指定された遷移の、ガードが無い遷移
 *	    2-1-1. 現在の状態の、デフォルト遷移の、ガードが有る遷移で、ガード条件が成立した遷移	複数有る場合は順不同。現在の実装ではガード名の順としているが、その挙動に依存してはいけない。
 *	    2-1-2. 現在の状態の、デフォルト遷移の、ガードが無い遷移
 *	    2-2-1. 状態マップの、デフォルト遷移の、ガードが有る遷移で、ガード条件が成立した遷移	複数有る場合は順不同。現在の実装ではガード名の順としているが、その挙動に依存してはいけない。
 *	    2-2-2. 状態マップの、デフォルト遷移の、ガードが無い遷移
 *	  上記の順に検索して、実行できる遷移がなかった場合は、エラー停止します。
 *	  詳細は、/clip/tool/dFsmMapC/Sample/Sample.txt の例を参照してください。
 *	* Wed Jul 01 21:16:16 JST 2015 Naoyuki Sawa
 *	- FsmObj_CancelTrans()を追加しました。
 *	  FsmObj_ApplyTrans()から呼び出された、Act関数の中から、遷移をキャンセル出来るようになりました。
 *	- 原則として遷移をキャンセルするのは、Guard関数で判断すべきです。
 *	  しかし実際には、Act関数まで処理が進んでから、遷移をキャンセルしたいケースも有ります。(エラー検出や例外処理など)
 *	  そのようなケースに対応するために、FsmObj_CancelTrans()を追加する事にしました。
 *	- Act関数がFsmObj_CancelTrans()を使って遷移をキャンセルする事と、Guard関数が遷移を受け入れない事は、等価ではありません。
 *	  Act関数がFsmObj_CancelTrans()を使って遷移をキャンセルした場合、上記の優先順位に従った次の遷移を検索する動作は行われない事に注意してください。
 *	- FsmObj_CancelTrans()を呼び出せるのは、Act関数の中からだけです。
 *	  Enter関数,Leave関数,Guard関数の中から、呼び出してはいけません。
 *	- Act関数の中で、遷移をキャンセルすると共に、明示的に状態をセット(又は,プッシュ,ポップ)を行う事も可能です。
 *	  <例>
 *	  │Map1
 *	  │  State1
 *	  │    Trans1 State2 {}
 *	  │  end
 *	  │  State2
 *	  │    ...
 *	  │  end
 *	  │  StateE
 *	  │    ...
 *	  │  end
 *	  │end
 *	  ├────────────────────────────────────────
 *	  │void FsmFunc_Map1_State1_act(ST_FsmObj* pFsmObj, int iTrans, void* pParam) {
 *	  │  if(例外判定) {
 *	  │    FsmObj_SetState(pFsmObj, FsmState_StateE);┬明示的に状態'StateE'をセットし、
 *	  │    FsmObj_CancelTrans(); ──────────┘State2への遷移をキャンセルする。
 *	  │} }
 *	* Fri Jul 03 21:00:32 JST 2015 Naoyuki Sawa
 *	- FsmObj_CancelTrans()を削除しました。
 *	  今後は構造化例外処理(SEH)を使用してください。
 *	  構造化例外処理(SEH)を使用する方が、より柔軟に制御出来ます。
 *	  <例1>
 *	  │//Act関数側で、遷移キャンセル後のエラー状態も設定する例
 *	  │void Sample1() {
 *	  │  SEH_try {
 *	  │    FsmObj_ApplyTrans(&stFsmObj, FsmTrans_Trans1, NULL);
 *	  │  } SEH_catch(&stFsmObj) {
 *	  │  } SEH_end
 *	  │}
 *	  │void FsmFunc_Map1_State1_act(ST_FsmObj* pFsmObj, int iTrans, void* pParam) {
 *	  │  if(例外判定) {
 *	  │    FsmObj_SetState(pFsmObj, FsmState_StateE);┬明示的に状態'StateE'をセットし、
 *	  │    SEH_throw(pFsmObj); ───────────┘State2への遷移をキャンセルする。
 *	  │  }
 *	  │}
 *	  <例2>
 *	  │//呼び出し側で、遷移キャンセル後のエラー状態を設定する例
 *	  │void Sample2() {
 *	  │  SEH_try {
 *	  │    FsmObj_ApplyTrans(&stFsmObj, FsmTrans_Trans1, NULL);
 *	  │  } SEH_catch(&stFsmObj) {───────────┬遷移'Trans1'がキャンセルされたら…
 *	  │    FsmObj_SetState(&stFsmObj, FsmState_StateE);┘明示的に状態'StateE'をセットする。
 *	  │  } SEH_end
 *	  │}
 *	  │void FsmFunc_Map1_State1_act(ST_FsmObj* pFsmObj, int iTrans, void* pParam) {
 *	  │  if(例外判定) {
 *	  │    SEH_throw(pFsmObj); ─────────────遷移をキャンセルする。
 *	  │  }
 *	  │}
 */
#ifndef __CLIP_FSM_H__
#define __CLIP_FSM_H__
/****************************************************************************
 *	
 ****************************************************************************/
//状態,又は,状態マップのレジストリキーに属する、キーの名前
#define Fsm_Trans_default	0		//デフォルト遷移
//状態,又は,状態マップのレジストリキーに属する、値の名前
#define Fsm_Enter		0		//Enter関数
#define Fsm_Leave		1		//Leave関数
//遷移のレジストリキーに属する、値の名前
//#define Fsm_Guard		(3N+0)		//Guard関数
//#define Fsm_Act		(3N+1)		//Act関数
//#define Fsm_State		(3N+2)		//遷移先状態
/*--------------------------------------------------------------------------*/
#define FsmObj_StackSize	3		//状態スタックの要素数(調整可)
//FSMオブジェクト
typedef struct _ST_FsmObj {
	const void*	pMapKey;						//+ 0,4 状態マップのレジストリキー
	uint16_t	iStack;							//+ 4,2 状態スタックのスタックポインタ
	uint16_t	TBL_Stack[FsmObj_StackSize];				//+ 6,6 状態スタック				TBL_Stack[iStack]が、現在の状態に相当します。
} ST_FsmObj;									//=12
/*--------------------------------------------------------------------------*/
//アプリケーション定義の関数配列。
//先頭要素に、関数配列の要素数を格納する事。
extern const void* const TBL_FsmFunc[];
//配列要素の関数は以下のいずれかの形式です。					//状態変更	FsmObj_GetState()が返す状態	備考
typedef void FsmFunc_enter(ST_FsmObj* pFsmObj);					//不可		変更後の状態			現在の状態を変更した後に、この関数を呼びます。
typedef void FsmFunc_leave(ST_FsmObj* pFsmObj);					//不可		変更前の状態			この関数を呼んでから、現在の状態を変更します。
typedef int  FsmFunc_guard(ST_FsmObj* pFsmObj, int iTrans, void* pParam);	//不可		変更前の状態			Guard関数が(0)を返したら、この遷移を行わない。
typedef void FsmFunc_act(  ST_FsmObj* pFsmObj, int iTrans, void* pParam);	//可		変更前の状態			遷移後状態へ移行する前に、この関数を呼びます。
/****************************************************************************
 *	
 ****************************************************************************/
void FsmObj_Init(ST_FsmObj* pFsmObj, const void* pFsmKey, int iMap, int iState);
int  FsmObj_GetState(  ST_FsmObj* pFsmObj);
void FsmObj_SetState(  ST_FsmObj* pFsmObj, int iState);
void FsmObj_PushState( ST_FsmObj* pFsmObj, int iState);
void FsmObj_PopState(  ST_FsmObj* pFsmObj);
void FsmObj_ApplyTrans(ST_FsmObj* pFsmObj, int iTrans, void* pParam);
//{{2015/07/03削除:FsmObj_CancelTrans()を削除しました。今後は構造化例外処理(SEH)を使用してください。
//#if     defined(__GNUC__)
//void FsmObj_CancelTrans() __attribute__((noreturn));
//#elif                         defined(_MSC_VER)
//__declspec(noreturn) void FsmObj_CancelTrans();
//#else//!defined(__GNUC__) && !defined(_MSC_VER)
//void FsmObj_CancelTrans();
//#endif
//}}2015/07/03削除:FsmObj_CancelTrans()を削除しました。今後は構造化例外処理(SEH)を使用してください。
#endif//__CLIP_FSM_H__
