/*
 *	cliplcds.c
 *
 *	液晶シーケンサ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2016 Naoyuki Sawa
 *
 *	* Wed Sep 14 22:40:37 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	  全画面スプライトや全画面ムービーのレイヤ管理を行うために作成したモジュールです。
 *	  「液晶シーケンサ」という名前ですが、現在の所、シーケンサの機能は持っていません。
 *	  演出シーケンサ(clipdirs.c)モジュール等と組み合わせて、使用して下さい。
 *	* Sat Sep 17 21:29:14 JST 2016 Naoyuki Sawa
 *	- LcdSeqCh_Play(),及び,LcdSeqCh_SetSprNo()に、(SprNo=0)を指定出来るようにしました。
 *	  (SprNo=0)を指定した場合は、LcdSeqCh_Stop()を呼び出すのと同じ意味となります。
 *	- このように変更した理由は、演出シーケンサスクリプト等のデータ上で、(SprNo=0)によってレイヤ停止を意味する事が多く、
 *	  演出実行関数(fnAct)等でそれを判断してLcdSeqCh_Stop()を呼び分けるよりも、LcdSeqCh_Play()で判断する方がアプリケーションの実装が簡単だと思ったからです。
 *	  尚、現在の所LcdSeqCh_SetSprNo()にこの変更を行う必要は無かったのですが、LcdSeqCh_SetSprNo()もLcdSeqCh_Play()と同様とする方が判り易いのでそうしました。
 *	* Wed Oct 12 21:38:09 JST 2016 Naoyuki Sawa
 *	- LcdSeq_GetLcdSeqChInfo(),LcdSeq_GetLcdSeqCh()を、ローカル関数⇒グローバル関数に変更しました。
 *	  関数の動作自体は変えていません。
 *	- 上記の関数をグローバル関数に変更した理由は、アプリケーション側でもこれらの情報を参照したい事が有ったからです。
 *	  例えば、特定のチャネルで現在再生中のスプライト番号を参照して、それによって他の描画も変更したいような場合です。
 *	  とは言え、あまり濫用すると今後、ST_LcdSeqCh構造体,ST_LcdSeqChInfo構造体のフィールドを変更した場合に互換性の問題が生じますので、
 *	  なるべく、アプリケーションが直接LcdSeq_GetLcdSeqChInfo(),LcdSeq_GetLcdSeqCh()を呼び出す事は避けてください。
 *	- また、上記の関数をグローバル関数に変更した事で、既存のLcdSeqCh_GetCnt()は不要になってしまったのですが、残しておく事にしました。
 *	  前述の通り、アプリケーションがLcdSeq_GetLcdSeqChInfo(),LcdSeq_GetLcdSeqCh()を直接呼び出す事は望ましくないので、
 *	  LcdSeqCh_GetCnt()で済むならばLcdSeqCh_GetCnt()を使用するようにして下さい。
 */
#include "clip.h"
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
//{{2016/10/12削除:LcdSeq_GetLcdSeqChInfo(),LcdSeq_GetLcdSeqCh()を、ローカル関数⇒グローバル関数に変更しました。
//static const ST_LcdSeqChInfo* LcdSeq_GetLcdSeqChInfo(ST_LcdSeq* pLcdSeq, int iLcdSeqCh);
//static ST_LcdSeqCh* LcdSeq_GetLcdSeqCh(ST_LcdSeq* pLcdSeq, int iLcdSeqCh);
//}}2016/10/12削除:LcdSeq_GetLcdSeqChInfo(),LcdSeq_GetLcdSeqCh()を、ローカル関数⇒グローバル関数に変更しました。
static void LcdSeqCh_Draw(ST_LcdSeq* pLcdSeq, int iLcdSeqCh);
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
ST_LcdSeq* LcdSeq_New(const ST_LcdSeqInfo* pLcdSeqInfo) {
	ST_LcdSeq* pLcdSeq;
	//構造体のメモリを確保する。
	pLcdSeq = malloc(sizeof(ST_LcdSeq) + (sizeof(ST_LcdSeqCh) * pLcdSeqInfo->nLcdSeqCh));
	if(!pLcdSeq) { DIE(); }	//メモリ不足
	//構造体を初期化する。
	LcdSeq_Init(pLcdSeq, pLcdSeqInfo);
	return pLcdSeq;
}
/*--------------------------------------------------------------------------*/
void LcdSeq_Init(ST_LcdSeq* pLcdSeq, const ST_LcdSeqInfo* pLcdSeqInfo) {
	//構造体をクリアする。
	memset(pLcdSeq, 0, sizeof(ST_LcdSeq) + (sizeof(ST_LcdSeqCh) * pLcdSeqInfo->nLcdSeqCh));
	//初期化情報へのポインタを格納する。
	pLcdSeq->pLcdSeqInfo = pLcdSeqInfo;
	//初期化情報を検査する。
#ifdef  __DXLIB
	{
		int iLcdSeqCh;
		for(iLcdSeqCh = 0; iLcdSeqCh < pLcdSeq->pLcdSeqInfo->nLcdSeqCh; iLcdSeqCh++) {
			const ST_LcdSeqChInfo* pLcdSeqChInfo = LcdSeq_GetLcdSeqChInfo(pLcdSeq, iLcdSeqCh);
			if((unsigned)pLcdSeqChInfo->Mode >= (unsigned)DX_BLENDMODE_NUM) { DIE(); }
		}
	}
#endif//__DXLIB
}
/*--------------------------------------------------------------------------*/
void LcdSeq_Free(ST_LcdSeq* pLcdSeq) {
	//構造体のメモリを開放する。
	free(pLcdSeq);
}
/*--------------------------------------------------------------------------*/
void LcdSeq_Draw(ST_LcdSeq* pLcdSeq) {
	int iLcdSeqCh;
	//各チャネルについて…
	for(iLcdSeqCh = 0; iLcdSeqCh < pLcdSeq->pLcdSeqInfo->nLcdSeqCh; iLcdSeqCh++) {
		//チャネルを描画する。
		LcdSeqCh_Draw(pLcdSeq, iLcdSeqCh);
	}
}
/*--------------------------------------------------------------------------*/
void LcdSeqCh_Play(ST_LcdSeq* pLcdSeq, int iLcdSeqCh, int SprNo/*0可*/) {
	//引数のスプライト番号が0以外ならば…
	if(SprNo) {
		//チャネルの初期化情報を取得する。
		const ST_LcdSeqChInfo* pLcdSeqChInfo = LcdSeq_GetLcdSeqChInfo(pLcdSeq, iLcdSeqCh);
		//チャネルの構造体を取得する。
		ST_LcdSeqCh* pLcdSeqCh = LcdSeq_GetLcdSeqCh(pLcdSeq, iLcdSeqCh);
		//チャネルの構造体をクリアする。
		memset(pLcdSeqCh, 0, sizeof(ST_LcdSeqCh));
		//引数のスプライト番号を検査する。
		if((SprNo < 1) || (SprNo > pLcdSeq->pLcdSeqInfo->_SprNo_Max)) { DIE(); }	//スプライト番号が不正。おそらくアプリケーションのバグです。
		//スプライト番号を格納する。
		pLcdSeqCh->SprNo = SprNo;
		//デフォルト値を格納する。
		pLcdSeqCh->Mode = pLcdSeqChInfo->Mode;
		pLcdSeqCh->Pri  = pLcdSeqChInfo->Pri;
		pLcdSeqCh->PosX = pLcdSeqChInfo->PosX;
		pLcdSeqCh->PosY = pLcdSeqChInfo->PosY;
	//引数のスプライト番号が0ならば…
	} else {
		//LcdSeqCh_Stop()を呼び出すのと同じ意味となります。
		LcdSeqCh_Stop(pLcdSeq, iLcdSeqCh);
	}
}
/*--------------------------------------------------------------------------*/
void LcdSeqCh_SetMode(ST_LcdSeq* pLcdSeq, int iLcdSeqCh, int Mode) {
	//チャネルの構造体を取得する。
	ST_LcdSeqCh* pLcdSeqCh = LcdSeq_GetLcdSeqCh(pLcdSeq, iLcdSeqCh);
	//引数の描画モードを検査する。
#ifdef  __DXLIB
	if((unsigned)Mode >= (unsigned)DX_BLENDMODE_NUM) { DIE(); }	//描画モードが不正。おそらくアプリケーションのバグです。
#endif//__DXLIB
	//停止中ならば格納しない。
	if(!pLcdSeqCh->SprNo) { return; }	//ここまで
	//描画モードを格納する。
	pLcdSeqCh->Mode = Mode;
}
/*--------------------------------------------------------------------------*/
void LcdSeqCh_SetPri(ST_LcdSeq* pLcdSeq, int iLcdSeqCh, int Pri) {
	//チャネルの構造体を取得する。
	ST_LcdSeqCh* pLcdSeqCh = LcdSeq_GetLcdSeqCh(pLcdSeq, iLcdSeqCh);
	//引数の優先度を検査する。
	if((Pri < SHRT_MIN) || (Pri > SHRT_MAX)) { DIE(); }		//優先度が不正。おそらくアプリケーションのバグです。
	//停止中ならば格納しない。
	if(!pLcdSeqCh->SprNo) { return; }	//ここまで
	//優先度を格納する。
	pLcdSeqCh->Pri = Pri;
}
/*--------------------------------------------------------------------------*/
void LcdSeqCh_SetPos(ST_LcdSeq* pLcdSeq, int iLcdSeqCh, int PosX, int PosY) {
	//チャネルの構造体を取得する。
	ST_LcdSeqCh* pLcdSeqCh = LcdSeq_GetLcdSeqCh(pLcdSeq, iLcdSeqCh);
	//引数の座標を検査する。
	if(((PosX < SHRT_MIN) || (PosX > SHRT_MAX)) ||
	   ((PosY < SHRT_MIN) || (PosY > SHRT_MAX))) { DIE(); }		//座標が不正。おそらくアプリケーションのバグです。
	//停止中ならば格納しない。
	if(!pLcdSeqCh->SprNo) { return; }	//ここまで
	//座標を格納する。
	pLcdSeqCh->PosX = PosX;
	pLcdSeqCh->PosY = PosY;
}
/*--------------------------------------------------------------------------*/
void LcdSeqCh_SetSprNo(ST_LcdSeq* pLcdSeq, int iLcdSeqCh, int SprNo/*0可*/) {
	//引数のスプライト番号が0以外ならば…
	if(SprNo) {
		//チャネルの構造体を取得する。
		ST_LcdSeqCh* pLcdSeqCh = LcdSeq_GetLcdSeqCh(pLcdSeq, iLcdSeqCh);
		//引数のスプライト番号を検査する。
		if((SprNo < 1) || (SprNo > pLcdSeq->pLcdSeqInfo->_SprNo_Max)) { DIE(); }	//スプライト番号が不正。おそらくアプリケーションのバグです。
		//停止中ならば格納しない。
		if(!pLcdSeqCh->SprNo) { return; }	//ここまで
		//スプライト番号を格納する。
		pLcdSeqCh->SprNo = SprNo;
	//引数のスプライト番号が0ならば…
	} else {
		//LcdSeqCh_Stop()を呼び出すのと同じ意味となります。
		LcdSeqCh_Stop(pLcdSeq, iLcdSeqCh);
	}
}
/*--------------------------------------------------------------------------*/
int LcdSeqCh_GetCnt(ST_LcdSeq* pLcdSeq, int iLcdSeqCh) {
	//チャネルの構造体を取得する。
	ST_LcdSeqCh* pLcdSeqCh = LcdSeq_GetLcdSeqCh(pLcdSeq, iLcdSeqCh);
	//再生中ならば描画回数(0〜)を返す。停止中ならば-1を返す。
	return pLcdSeqCh->SprNo ? pLcdSeqCh->Cnt : -1;
}
/*--------------------------------------------------------------------------*/
void LcdSeq_Stop(ST_LcdSeq* pLcdSeq) {
	int iLcdSeqCh;
	//各チャネルについて…
	for(iLcdSeqCh = 0; iLcdSeqCh < pLcdSeq->pLcdSeqInfo->nLcdSeqCh; iLcdSeqCh++) {
		//チャネルを停止する。
		LcdSeqCh_Stop(pLcdSeq, iLcdSeqCh);
	}
}
/*--------------------------------------------------------------------------*/
void LcdSeqCh_Stop(ST_LcdSeq* pLcdSeq, int iLcdSeqCh) {
	//チャネルの構造体を取得する。
	ST_LcdSeqCh* pLcdSeqCh = LcdSeq_GetLcdSeqCh(pLcdSeq, iLcdSeqCh);
	//チャネルの構造体をクリアする。
	memset(pLcdSeqCh, 0, sizeof(ST_LcdSeqCh));
}
/*--------------------------------------------------------------------------*/
//{{2016/10/12追加:LcdSeq_GetLcdSeqChInfo(),LcdSeq_GetLcdSeqCh()を、ローカル関数⇒グローバル関数に変更しました。
//指定されたチャネル番号の初期化情報へのポインタを取得する。
const ST_LcdSeqChInfo* LcdSeq_GetLcdSeqChInfo(ST_LcdSeq* pLcdSeq, int iLcdSeqCh) {
	const ST_LcdSeqChInfo* const TBL_LcdSeqChInfo = (const ST_LcdSeqChInfo*)(pLcdSeq->pLcdSeqInfo + 1);
	//引数のチャネル番号を検査する。
	if((unsigned)iLcdSeqCh >= (unsigned)pLcdSeq->pLcdSeqInfo->nLcdSeqCh) { DIE(); }	//チャネル番号が不正。おそらくアプリケーションのバグです。
	//初期化情報へのポインタを返す。
	return &TBL_LcdSeqChInfo[iLcdSeqCh];
}
/*--------------------------------------------------------------------------*/
//指定されたチャネル番号の構造体へのポインタを取得する。
ST_LcdSeqCh* LcdSeq_GetLcdSeqCh(ST_LcdSeq* pLcdSeq, int iLcdSeqCh) {
	ST_LcdSeqCh* const TBL_LcdSeqCh = (ST_LcdSeqCh*)(pLcdSeq + 1);
	//引数のチャネル番号を検査する。
	if((unsigned)iLcdSeqCh >= (unsigned)pLcdSeq->pLcdSeqInfo->nLcdSeqCh) { DIE(); }	//チャネル番号が不正。おそらくアプリケーションのバグです。
	//チャネルの構造体へのポインタを返す。
	return &TBL_LcdSeqCh[iLcdSeqCh];
}
//}}2016/10/12追加:LcdSeq_GetLcdSeqChInfo(),LcdSeq_GetLcdSeqCh()を、ローカル関数⇒グローバル関数に変更しました。
/****************************************************************************
 *	ローカル関数
 ****************************************************************************/
//{{2016/10/12削除:LcdSeq_GetLcdSeqChInfo(),LcdSeq_GetLcdSeqCh()を、ローカル関数⇒グローバル関数に変更しました。
////指定されたチャネル番号の初期化情報へのポインタを取得する。
//static const ST_LcdSeqChInfo* LcdSeq_GetLcdSeqChInfo(ST_LcdSeq* pLcdSeq, int iLcdSeqCh) {
//	const ST_LcdSeqChInfo* const TBL_LcdSeqChInfo = (const ST_LcdSeqChInfo*)(pLcdSeq->pLcdSeqInfo + 1);
//	//引数のチャネル番号を検査する。
//	if((unsigned)iLcdSeqCh >= (unsigned)pLcdSeq->pLcdSeqInfo->nLcdSeqCh) { DIE(); }	//チャネル番号が不正。おそらくアプリケーションのバグです。
//	//初期化情報へのポインタを返す。
//	return &TBL_LcdSeqChInfo[iLcdSeqCh];
//}
///*--------------------------------------------------------------------------*/
////指定されたチャネル番号の構造体へのポインタを取得する。
//static ST_LcdSeqCh* LcdSeq_GetLcdSeqCh(ST_LcdSeq* pLcdSeq, int iLcdSeqCh) {
//	ST_LcdSeqCh* const TBL_LcdSeqCh = (ST_LcdSeqCh*)(pLcdSeq + 1);
//	//引数のチャネル番号を検査する。
//	if((unsigned)iLcdSeqCh >= (unsigned)pLcdSeq->pLcdSeqInfo->nLcdSeqCh) { DIE(); }	//チャネル番号が不正。おそらくアプリケーションのバグです。
//	//チャネルの構造体へのポインタを返す。
//	return &TBL_LcdSeqCh[iLcdSeqCh];
//}
//}}2016/10/12削除:LcdSeq_GetLcdSeqChInfo(),LcdSeq_GetLcdSeqCh()を、ローカル関数⇒グローバル関数に変更しました。
/*--------------------------------------------------------------------------*/
//チャネルを描画する。
static void LcdSeqCh_Draw(ST_LcdSeq* pLcdSeq, int iLcdSeqCh) {
	//チャネルの構造体を取得する。
	ST_LcdSeqCh* pLcdSeqCh = LcdSeq_GetLcdSeqCh(pLcdSeq, iLcdSeqCh);
	//停止中ならば何もしない。
	if(!pLcdSeqCh->SprNo) { return; }	//ここまで
	//描画関数を呼び出す。
	(*pLcdSeq->pLcdSeqInfo->fnDraw)(iLcdSeqCh, pLcdSeqCh);
	//描画回数を増やす。
	if(pLcdSeqCh->Cnt < USHRT_MAX/*飽和*/) { pLcdSeqCh->Cnt++; }
}
