//
//	clipmovh_uni.cs
//
//	Movieヘルパー関数
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2017 Naoyuki Sawa
//
//	* Sat Mar 25 23:03:55 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//	- /clip/tool/SofdecHelper/の、SofdecHelper関数群に相当するモジュールです。
//	  また、/clip/DxlibHelper.cppの自動停止の機能も、当モジュールに取り込みました。
//	  「CRIWARE Unity plugin」と組み合わせて使用する事を想定していますが、一応、汎用的に作ってあります。
//	  当モジュール内には、「CRIWARE Unity plugin」や「Unity」の機能を直接利用している箇所は有りません。
//	  とは言え、「CRIWARE Unity plugin」と組み合わせて使用する事が、主目的である事には違い有りません。
//	  動作確認に使用したバージョンは、「criware_sdk_unity_smartphone_v2_88_j.zip」(2017-03-03)です。
//	- 「CRIWARE Unity plugin」は、スクリプト形式で提供されており、参照設定で使用する事が出来ません。
//	  当モジュール内にスクリプト形式のままで取り込む事も出来ないので、下記の方法で使うようにしました。
//	  「CRIWARE Unity plugin」のCriAtomExPlayerに相当する機能を、IMoviePlayerとして定義しました。
//	  アプリケーションは、IMoviePlayerを実装したクラスを定義して下さい。
//	  MovieHelper_New()の引数fnCreateMoviePlayerに、そのインスタンスを作成する関数を指定して下さい。
//	- 処理のアルゴリズムは、概ね既存の/clip/tool/SofdecHelper/,及び,/clip/DxlibHelper.cppと同じです。
//	* Sun Mar 26 22:13:56 JST 2017 Naoyuki Sawa
//	- dTexDefC.exeの変更に伴い、「RegKey_Loop」⇒「RegKey_loop」に変更しました。
//	  詳細は、/clip/tool/dTexDefC/winapp.hの同日のコメントを参照して下さい。
//
using System;
namespace org.piece_me {
	public static partial class libclip {
		//*****************************************************************************
		//	定数
		//*****************************************************************************
		//プレイヤーの状態
		public enum MoviePlayerStatus {
			Stop,			//停止中			== CriMana.Player.Status.Stop
			Dechead,		//ヘッダ解析中			== CriMana.Player.Status.Dechead
			WaitPrep,		//バッファリング開始停止中	== CriMana.Player.Status.WaitPrep
			Prep,			//再生準備中			== CriMana.Player.Status.Prep
			Ready,			//再生準備完了			== CriMana.Player.Status.Ready
			Playing,		//再生中			== CriMana.Player.Status.Playing
			PlayEnd,		//再生終了			== CriMana.Player.Status.PlayEnd
			Error,			//エラー			== CriMana.Player.Status.Error
			StopProcessing,		//停止処理中			== CriMana.Player.Status.StopProcessing
		}
		//*****************************************************************************
		//	構造体
		//*****************************************************************************
		//デバイスチャネルのプレイヤーのインターフェイス
		public interface IMoviePlayer : IDisposable {
			MoviePlayerStatus GetStatus();		//≒CriManaMovieMaterial.player.status																完了復帰型の関数です。あらゆるタイミングで呼び出される可能性が有ります。
			void Prepare(int iTexNo);		//≒CriManaMovieMaterial.player.SetFile()+CriManaMovieMaterial.player.Loop()+CriManaMovieMaterial.player.additiveMode+CriManaMovieMaterial.player.Prepare()	完了復帰型の関数ではありません。この操作が可能なタイミングでのみ呼び出されるので、アプリケーション側で状態を確認する必要は有りません。
			void Start();				//≒CriManaMovieMaterial.player.Start()+CriManaMovieMaterial.player.status+CriManaMovieMaterial.isMaterialAvailable+CriManaMovieMaterial.Update()		完了復帰型の関数ではありません。この操作が可能なタイミングでのみ呼び出されるので、アプリケーション側で状態を確認する必要は有りません。
			void Stop();				//≒CriManaMovieMaterial.Stop()+CriManaMovieMaterial.player.status+CriManaMovieMaterial.Update()								完了復帰型の関数です。あらゆるタイミングで呼び出される可能性が有りますが、CriManaMovieMaterial.Stop()はあらゆるタイミングで実行可能なので、アプリケーション側で状態を確認する必要は有りません。
		}
		//-----------------------------------------------------------------------------
		//管理構造体
		public class ST_MovieHelper {
			public int				nCh;			//チャネル数
			public ST_MovieDevCh[/*nCh*/]		TBL_MovieDevCh;		//デバイスチャネル配列
		}
		//-----------------------------------------------------------------------------
		//デバイスチャネル
		public class ST_MovieDevCh {
			public IMoviePlayer			pMoviePlayer;		//プレイヤー		アプリケーション定義のオブジェクトです。MovieHelper_New()の引数fnCreateMoviePlayerで指定した関数が呼び出される事によって作成されます。
			public int				iTexNo;			//テクスチャ番号	Prepare()で格納する。Stop()で0にする。
			public int				iPrepAge;		//再生準備の古さ	Prepare()～Start()の間((iTexNo!=0)&&(!bInUse))のみ有効。
			public bool				bInUse;			//使用中フラグ		GetMoviePlayer()でtrueにする。Exec()でfalseにする。
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		//MovieHelperを動的に作成する。
		public static ST_MovieHelper MovieHelper_New(int nCh, Func<object/*arg*/,IMoviePlayer> fnCreateMoviePlayer, object arg) {
			ST_MovieHelper pMovieHelper = new ST_MovieHelper();
			MovieHelper_Init(pMovieHelper, nCh, fnCreateMoviePlayer, arg);
			return pMovieHelper;
		}
		//-----------------------------------------------------------------------------
		//MovieHelperを静的に初期化する。
		public static void MovieHelper_Init(ST_MovieHelper pMovieHelper, int nCh, Func<object/*arg*/,IMoviePlayer> fnCreateMoviePlayer, object arg) {
			//チャネル数を格納する。
			pMovieHelper.nCh = nCh;
			//デバイスチャネル配列を作成する。
			pMovieHelper.TBL_MovieDevCh = new ST_MovieDevCh[nCh];
			for(int iCh = 0; iCh < nCh; iCh++) {
				ST_MovieDevCh pMovieDevCh = new ST_MovieDevCh();
				pMovieHelper.TBL_MovieDevCh[iCh] = pMovieDevCh;
				pMovieDevCh.pMoviePlayer = fnCreateMoviePlayer.Invoke(arg);
			}
		}
		//-----------------------------------------------------------------------------
		//MovieHelperの終了処理を実行する。
		public static void MovieHelper_Exit(ST_MovieHelper pMovieHelper) {
			//プレイヤーを削除する。
			foreach(ST_MovieDevCh pMovieDevCh in pMovieHelper.TBL_MovieDevCh) {
				pMovieDevCh.pMoviePlayer.Dispose();
			}
		}
		//-----------------------------------------------------------------------------
		//周期処理を実行する。
		public static void MovieHelper_Exec(ST_MovieHelper pMovieHelper) {
			//各デバイスチャネルについて…
			foreach(ST_MovieDevCh pMovieDevCh in pMovieHelper.TBL_MovieDevCh) {
				//プレイヤーの状態によって…
				switch(pMovieDevCh.pMoviePlayer.GetStatus()) {
				default:throw new ApplicationException();
				case MoviePlayerStatus.Stop:		//停止中
					//デバイスチャネルのテクスチャ番号が格納されておらず、使用中フラグがクリアされている事を検査する。
					if((pMovieDevCh.iTexNo != 0) || pMovieDevCh.bInUse) { throw new ApplicationException(); }
					break;
				//Prepare()～Draw()の間ならば…
				case MoviePlayerStatus.Dechead:		//ヘッダ解析中
				case MoviePlayerStatus.WaitPrep:	//バッファリング開始停止中
				case MoviePlayerStatus.Prep:		//再生準備中
				case MoviePlayerStatus.Ready:		//再生準備完了
					//デバイスチャネルのテクスチャ番号が格納されており、使用中フラグがクリアされている事を検査する。
					if((pMovieDevCh.iTexNo == 0) || pMovieDevCh.bInUse) { throw new ApplicationException(); }
					break;
				//Draw()～自動停止の間ならば…
				case MoviePlayerStatus.Playing:		//再生中
				case MoviePlayerStatus.PlayEnd:		//再生終了
					//少なくともデバイスチャネルのテクスチャ番号が格納されている事を確認する。
					if(pMovieDevCh.iTexNo == 0) { throw new ApplicationException(); }	//今回のフレームでDraw()が呼ばれていなければ(pMovieDevCh.bInUse==false)なので、使用中フラグを確認してはいけない。
					//今回のフレームでDraw()が呼ばれていたら…
					if(pMovieDevCh.bInUse) {
						//次回のフレームのために、使用中フラグをクリアする。
						pMovieDevCh.bInUse = false;
					//今回のフレームでDraw()が呼ばれていなければ…
					} else {
						//再生を停止する。
						pMovieDevCh.pMoviePlayer.Stop();
						//デバイスチャネルのテクスチャ番号をクリアする。
						pMovieDevCh.iTexNo = 0;
					}
					break;
			    //	case MoviePlayerStatus.Error:		//エラー	当モジュールのアルゴリズムでは、エラーの状態は発生しないはずです。
			    //	case MoviePlayerStatus.StopProcessing:	//停止処理中	当モジュールのアルゴリズムでは、停止処理中の状態は発生しないはずです。
				}
			}
		}
		//-----------------------------------------------------------------------------
		//再生準備を開始する。
		public static bool MovieHelper_Prepare(ST_MovieHelper pMovieHelper, int iTexNo) {
			//テクスチャ番号を検査する。
			if((iTexNo < 1) || (iTexNo > int.MaxValue)) { throw new ApplicationException(); }
			//①指定されたテクスチャ番号を再生中のデバイスチャネルが有れば、何もせずにtrueを返す。
			//②指定されたテクスチャ番号を再生準備中のデバイスチャネルが有れば、世代管理を行い、再生準備が完了していればtrue,再生準備が完了していなければfalseを返す。
			//③停止中のデバイスチャネルか、又は、一番古い再生準備中のデバイスチャネルの再生準備を中断し、指定されたテクスチャ番号の再生準備を開始して、世代管理を行い、(有り得ないとは思うが)再生準備が完了していればtrue,再生準備が完了していなければfalseを返す。
			//④停止中のデバイスチャネルも、再生準備中のデバイスチャネルも無ければ、何もせずにfalseを返す。
			ST_MovieDevCh pMovieDevCh = MovieHelper_Prepare_subr1(pMovieHelper, iTexNo);
			if(pMovieDevCh == null) { return false; }	//④
			//プレイヤーの状態によって…
			switch(pMovieDevCh.pMoviePlayer.GetStatus()) {
			default:throw new ApplicationException();
		    //	case MoviePlayerStatus.Stop:		//停止中	MovieHelper_Prepare_subr1()が停止中のデバイスチャネルを返す事は無いはずです。
			case MoviePlayerStatus.Dechead:		//ヘッダ解析中
			case MoviePlayerStatus.WaitPrep:	//バッファリング開始停止中
			case MoviePlayerStatus.Prep:		//再生準備中
				return false;	//②③
			case MoviePlayerStatus.Ready:		//再生準備完了
			case MoviePlayerStatus.Playing:		//再生中
			case MoviePlayerStatus.PlayEnd:		//再生終了
				return true;	//①②(③)
		    //	case MoviePlayerStatus.Error:		//エラー	当モジュールのアルゴリズムでは、エラーの状態は発生しないはずです。
		    //	case MoviePlayerStatus.StopProcessing:	//停止処理中	当モジュールのアルゴリズムでは、停止処理中の状態は発生しないはずです。
			}
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		//①指定されたテクスチャ番号を再生中のデバイスチャネルが有れば、それを返す。
		//②指定されたテクスチャ番号を再生準備中のデバイスチャネルが有れば、世代管理を行い、それを返す。
		//③停止中のデバイスチャネルか、又は、一番古い再生準備中のデバイスチャネルの再生準備を中断し、指定されたテクスチャ番号の再生準備を開始して、世代管理を行い、それを返す。
		//④停止中のデバイスチャネルも、再生準備中のデバイスチャネルも無ければ、nullを返す。
		private static ST_MovieDevCh MovieHelper_Prepare_subr1(ST_MovieHelper pMovieHelper, int iTexNo) {
			//テクスチャ番号を検査する。
			if((iTexNo < 1) || (iTexNo > int.MaxValue)) { throw new ApplicationException(); }
			//①指定されたテクスチャ番号を再生中のデバイスチャネルが有れば、それを返す。
			//②指定されたテクスチャ番号を再生準備中のデバイスチャネルが有れば、世代管理を行い、それを返す。
			foreach(ST_MovieDevCh pMovieDevCh in pMovieHelper.TBL_MovieDevCh) {
				//指定されたテクスチャ番号を再生中,又は,再生準備中ならば…
				if(pMovieDevCh.iTexNo == iTexNo) {
					//プレイヤーの状態によって…
					switch(pMovieDevCh.pMoviePlayer.GetStatus()) {
					default:throw new ApplicationException();
				    //	case MoviePlayerStatus.Stop:		//停止中	テクスチャ番号が格納されているデバイスチャネルの、プレイヤーの状態が停止中である事は無いはずです。
					case MoviePlayerStatus.Dechead:		//ヘッダ解析中
					case MoviePlayerStatus.WaitPrep:	//バッファリング開始停止中
					case MoviePlayerStatus.Prep:		//再生準備中
					case MoviePlayerStatus.Ready:		//再生準備完了
						MovieHelper_Prepare_subr3(pMovieHelper, pMovieDevCh);
						return pMovieDevCh;	//②
					case MoviePlayerStatus.Playing:		//再生中
					case MoviePlayerStatus.PlayEnd:		//再生終了
						return pMovieDevCh;	//①
				    //	case MoviePlayerStatus.Error:		//エラー	当モジュールのアルゴリズムでは、エラーの状態は発生しないはずです。
				    //	case MoviePlayerStatus.StopProcessing:	//停止処理中	当モジュールのアルゴリズムでは、停止処理中の状態は発生しないはずです。
					}
				}
			}
			//③停止中のデバイスチャネルか、又は、一番古い再生準備中のデバイスチャネルの再生準備を中断し、指定されたテクスチャ番号の再生準備を開始して、世代管理を行い、それを返す。
			int           oldestPrepAge    = int.MinValue;
			ST_MovieDevCh oldestMovieDevCh = null;
			foreach(ST_MovieDevCh pMovieDevCh in pMovieHelper.TBL_MovieDevCh) {
				//プレイヤーの状態によって…
				switch(pMovieDevCh.pMoviePlayer.GetStatus()) {
				default:throw new ApplicationException();
				case MoviePlayerStatus.Stop:		//停止中
					//指定されたテクスチャ番号の再生準備を開始して、世代管理を行い、それを返す。
					MovieHelper_Prepare_subr2(pMovieDevCh, iTexNo);
					MovieHelper_Prepare_subr3(pMovieHelper, pMovieDevCh);
					return pMovieDevCh;	//ここまで
				case MoviePlayerStatus.Dechead:		//ヘッダ解析中
				case MoviePlayerStatus.WaitPrep:	//バッファリング開始停止中
				case MoviePlayerStatus.Prep:		//再生準備中
				case MoviePlayerStatus.Ready:		//再生準備完了
					//これまでで一番古い再生準備中のデバイスチャネルを記憶する。
					if(pMovieDevCh.iPrepAge > oldestPrepAge) {
						oldestPrepAge    = pMovieDevCh.iPrepAge;
						oldestMovieDevCh = pMovieDevCh;
					}
					break;
				case MoviePlayerStatus.Playing:		//再生中
				case MoviePlayerStatus.PlayEnd:		//再生終了
					/** no job **/
					break;
			    //	case MoviePlayerStatus.Error:		//エラー	当モジュールのアルゴリズムでは、エラーの状態は発生しないはずです。
			    //	case MoviePlayerStatus.StopProcessing:	//停止処理中	当モジュールのアルゴリズムでは、停止処理中の状態は発生しないはずです。
				}
			}
			//再生準備中のデバイスチャネルが有れば…
			if(oldestMovieDevCh != null) {
				//再生準備を中断し、指定されたテクスチャ番号の再生準備開始と世代管理を行って、それを返す。
				MovieHelper_Stop_subr(oldestMovieDevCh);
				MovieHelper_Prepare_subr2(oldestMovieDevCh, iTexNo);
				MovieHelper_Prepare_subr3(pMovieHelper, oldestMovieDevCh);
				return oldestMovieDevCh;	//ここまで
			}
			//④停止中のデバイスチャネルも、再生準備中のデバイスチャネルも無ければ、nullを返す。
			return null;
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		//指定されたデバイスチャネルで、再生準備を開始する。
		private static void MovieHelper_Prepare_subr2(ST_MovieDevCh pMovieDevCh, int iTexNo) {
			//デバイスチャネルのテクスチャ番号と使用中フラグがクリアされている事を確認する。
			if((pMovieDevCh.iTexNo != 0) || pMovieDevCh.bInUse) { throw new ApplicationException(); }
			//テクスチャ番号を検査する。
			if((iTexNo < 1) || (iTexNo > int.MaxValue)) { throw new ApplicationException(); }
			//テクスチャ番号を格納する。
			pMovieDevCh.iTexNo = iTexNo;
			//再生準備を開始する。
			pMovieDevCh.pMoviePlayer.Prepare(iTexNo);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		//世代管理を実行する。
		private static void MovieHelper_Prepare_subr3(ST_MovieHelper pMovieHelper, ST_MovieDevCh pMovieDevCh) {
			//指定されたデバイスチャネルの、再生準備の古さをクリアする。
			pMovieDevCh.iPrepAge = 0;
			//指定されたデバイスチャネル以外で、再生準備中で再生準備の古さが0のデバイスチャネルが有るか調べる。
			bool bFound = false;
			foreach(ST_MovieDevCh otherMovieDevCh in pMovieHelper.TBL_MovieDevCh) {
				if((otherMovieDevCh != pMovieDevCh) &&
				   (otherMovieDevCh.iTexNo != 0) &&
				   (otherMovieDevCh.iPrepAge == 0)) {
					//プレイヤーの状態によって…
					switch(otherMovieDevCh.pMoviePlayer.GetStatus()) {
					default:throw new ApplicationException();
				    //	case MoviePlayerStatus.Stop:		//停止中	テクスチャ番号が格納されているデバイスチャネルの、プレイヤーの状態が停止中である事は無いはずです。
					case MoviePlayerStatus.Dechead:		//ヘッダ解析中
					case MoviePlayerStatus.WaitPrep:	//バッファリング開始停止中
					case MoviePlayerStatus.Prep:		//再生準備中
					case MoviePlayerStatus.Ready:		//再生準備完了
						bFound = true;
						break;
					case MoviePlayerStatus.Playing:		//再生中
					case MoviePlayerStatus.PlayEnd:		//再生終了
						/** no job **/
						break;
				    //	case MoviePlayerStatus.Error:		//エラー	当モジュールのアルゴリズムでは、エラーの状態は発生しないはずです。
				    //	case MoviePlayerStatus.StopProcessing:	//停止処理中	当モジュールのアルゴリズムでは、停止処理中の状態は発生しないはずです。
					}
					if(bFound) { break; }
				}
			}
			//指定されたデバイスチャネル以外で、再生準備中で再生準備の古さが0のデバイスチャネルが無ければ…
			if(!bFound) { return; }	//ここまで
			//指定されたデバイスチャネル以外で、再生準備中のデバイスチャネルの再生準備の古さを増やす。
			foreach(ST_MovieDevCh otherMovieDevCh in pMovieHelper.TBL_MovieDevCh) {
				if((otherMovieDevCh != pMovieDevCh) &&
				   (otherMovieDevCh.iTexNo != 0)) {
					//プレイヤーの状態によって…
					switch(otherMovieDevCh.pMoviePlayer.GetStatus()) {
					default:throw new ApplicationException();
				    //	case MoviePlayerStatus.Stop:		//停止中	テクスチャ番号が格納されているデバイスチャネルの、プレイヤーの状態が停止中である事は無いはずです。
					case MoviePlayerStatus.Dechead:		//ヘッダ解析中
					case MoviePlayerStatus.WaitPrep:	//バッファリング開始停止中
					case MoviePlayerStatus.Prep:		//再生準備中
					case MoviePlayerStatus.Ready:		//再生準備完了
						otherMovieDevCh.iPrepAge++;	//このアルゴリズムならば、(iPrepAge≧nCh)にはならないはずなので、飽和処理は不要です。
						break;
					case MoviePlayerStatus.Playing:		//再生中
					case MoviePlayerStatus.PlayEnd:		//再生終了
						/** no job **/
						break;
				    //	case MoviePlayerStatus.Error:		//エラー	当モジュールのアルゴリズムでは、エラーの状態は発生しないはずです。
				    //	case MoviePlayerStatus.StopProcessing:	//停止処理中	当モジュールのアルゴリズムでは、停止処理中の状態は発生しないはずです。
					}
				}
			}
		}
		//-----------------------------------------------------------------------------
		//指定されたテクスチャ番号を描画可能なプレイヤーを取得する。
		public static IMoviePlayer MovieHelper_Draw(ST_MovieHelper pMovieHelper, int iTexNo) {
			//テクスチャ番号を検査する。
			if((iTexNo < 1) || (iTexNo > int.MaxValue)) { throw new ApplicationException(); }
			//指定されたテクスチャ番号を再生中,又は,再生準備中のデバイスチャネルを取得する。
			ST_MovieDevCh pMovieDevCh = MovieHelper_Prepare_subr1(pMovieHelper, iTexNo);
			//指定されたテクスチャ番号を再生中,又は,再生準備中のデバイスチャネルが無ければ、エラー停止する。
			if(pMovieDevCh == null) { throw new ApplicationException(); }
			//プレイヤーの状態によって…
			switch(pMovieDevCh.pMoviePlayer.GetStatus()) {
			default:throw new ApplicationException();
		    //	case MoviePlayerStatus.Stop:		//停止中	MovieHelper_Prepare_subr1()が停止中のデバイスチャネルを返す事は無いはずです。
			case MoviePlayerStatus.Dechead:		//ヘッダ解析中
			case MoviePlayerStatus.WaitPrep:	//バッファリング開始停止中
			case MoviePlayerStatus.Prep:		//再生準備中
			case MoviePlayerStatus.Ready:		//再生準備完了
				pMovieDevCh.pMoviePlayer.Start();
				break;
			case MoviePlayerStatus.Playing:		//再生中
			case MoviePlayerStatus.PlayEnd:		//再生終了
				/** no job **/
				break;
		    //	case MoviePlayerStatus.Error:		//エラー	当モジュールのアルゴリズムでは、エラーの状態は発生しないはずです。
		    //	case MoviePlayerStatus.StopProcessing:	//停止処理中	当モジュールのアルゴリズムでは、停止処理中の状態は発生しないはずです。
			}
			//使用中フラグをセットする。
			pMovieDevCh.bInUse = true;
			return pMovieDevCh.pMoviePlayer;
		}
		//-----------------------------------------------------------------------------
		//指定されたテクスチャ番号を再生中のデバイスチャネルが有れば、確実に再生を停止する。
		public static void MovieHelper_Stop(ST_MovieHelper pMovieHelper, int iTexNo) {
			//テクスチャ番号を検査する。
			if((iTexNo < 1) || (iTexNo > int.MaxValue)) { throw new ApplicationException(); }
			//各デバイスチャネルについて…
			foreach(ST_MovieDevCh pMovieDevCh in pMovieHelper.TBL_MovieDevCh) {
				//指定されたテクスチャ番号を再生中,又は,再生準備中ならば…
				if(pMovieDevCh.iTexNo == iTexNo) {
					//プレイヤーの状態によって…
					switch(pMovieDevCh.pMoviePlayer.GetStatus()) {
					default:throw new ApplicationException();
				    //	case MoviePlayerStatus.Stop:		//停止中	テクスチャ番号が格納されているデバイスチャネルの、プレイヤーの状態が停止中である事は無いはずです。
					case MoviePlayerStatus.Dechead:		//ヘッダ解析中
					case MoviePlayerStatus.WaitPrep:	//バッファリング開始停止中
					case MoviePlayerStatus.Prep:		//再生準備中
					case MoviePlayerStatus.Ready:		//再生準備完了
						/** no job **/
						return;	//ここまで
					case MoviePlayerStatus.Playing:		//再生中
					case MoviePlayerStatus.PlayEnd:		//再生終了
						MovieHelper_Stop_subr(pMovieDevCh);
						return;	//ここまで
				    //	case MoviePlayerStatus.Error:		//エラー	当モジュールのアルゴリズムでは、エラーの状態は発生しないはずです。
				    //	case MoviePlayerStatus.StopProcessing:	//停止処理中	当モジュールのアルゴリズムでは、停止処理中の状態は発生しないはずです。
					}
				}
			}
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		//指定されたデバイスチャネルの再生,又は,再生準備を停止する。
		private static void MovieHelper_Stop_subr(ST_MovieDevCh pMovieDevCh) {
			//少なくともデバイスチャネルのテクスチャ番号が格納されている事を確認する。
			if(pMovieDevCh.iTexNo == 0) { throw new ApplicationException(); }	//今回のフレームでDraw()が呼ばれていなければ(pMovieDevCh.bInUse==false)なので、使用中フラグを確認してはいけない。
			//再生,又は,再生準備を停止する。
			pMovieDevCh.pMoviePlayer.Stop();
			//デバイスチャネルのテクスチャ番号をクリアする。
			pMovieDevCh.iTexNo = 0;
			//デバイスチャネルの使用中フラグを確実にクリアする。
			pMovieDevCh.bInUse = false;	//今回のフレームでDraw()が呼ばれていなければ、既に(pMovieDevCh.bInUse==false)なのでダミー処理となる。
		}
	}
}
//*****************************************************************************
//	IMoviePlayerの実装例
//*****************************************************************************
#if false
using System;
using System.Reflection;
using UnityEngine;
using static org.piece_me.libclip;
using static Const;
using static Program;
public static partial class Program {
	public class MoviePlayer : IMoviePlayer {
		public CriManaMovieMaterial	criManaMovieMaterial;	//ムービーマテリアル
		//-----------------------------------------------------------------------------
		public MoviePlayer() {
			//ムービーマテリアルを作成する。
			criManaMovieMaterial = goRoot.AddComponent<CriManaMovieMaterial>();
		}
		//-----------------------------------------------------------------------------
		void IDisposable.Dispose() {
			//ムービーマテリアルを削除する。
			UnityEngine.Object.Destroy(criManaMovieMaterial);
		}
		//-----------------------------------------------------------------------------
		MoviePlayerStatus IMoviePlayer.GetStatus() {
			//プレイヤーの状態を取得する。
			return (MoviePlayerStatus)criManaMovieMaterial.player.status;
		}
		//-----------------------------------------------------------------------------
		void IMoviePlayer.Prepare(int iTexNo) {
			//テクスチャ番号を検査する。
			if((iTexNo < 1) || (iTexNo > TexNo_Max)) { throw new ApplicationException(); }
			//テクスチャのファイルパス,ループ設定,加算合成モード設定を取得する。
			VoidPtr pTexDef = REG_open_key_l(TBL_RegTbl, RegKey_TexDef, iTexNo);
			if(!pTexDef) { throw new ApplicationException(); }
			string path = REG_get_string(pTexDef, RegKey_Path);
			if(path == null) { throw new ApplicationException(); }
//{{2017/03/26変更:dTexDefC.exeの変更に伴い、「RegKey_Loop」⇒「RegKey_loop」に変更しました。
//			bool bLoop = (REG_get_value(pTexDef, RegKey_Loop) == 1);
//↓2017/03/26変更:dTexDefC.exeの変更に伴い、「RegKey_Loop」⇒「RegKey_loop」に変更しました。
			bool bLoop = (REG_get_value(pTexDef, RegKey_loop) == 1);
//}}2017/03/26変更:dTexDefC.exeの変更に伴い、「RegKey_Loop」⇒「RegKey_loop」に変更しました。
			bool bAdditiveMode = (REG_get_value(pTexDef, RegKey_additive) == 1);
			//テクスチャのファイルパス,ループ設定,加算合成モード設定を設定する。
			if(!criManaMovieMaterial.player.SetFile(binder, path)) { throw new ApplicationException(); }
			criManaMovieMaterial.player.Loop(bLoop);
			criManaMovieMaterial.player.additiveMode = bAdditiveMode;
			//再生準備を開始する。
			criManaMovieMaterial.player.Prepare();
		}
		//-----------------------------------------------------------------------------
		void IMoviePlayer.Start() {
			//再生を開始する。
			criManaMovieMaterial.player.Start();
			//マテリアルが有効になるまで待つ。
			for(;;) {
				if(criManaMovieMaterial.player.status == CriMana.Player.Status.Error) { throw new ApplicationException(); }
				if(criManaMovieMaterial.isMaterialAvailable) { break; }
				typeof(CriManaMovieMaterial).InvokeMember("Update", BindingFlags.InvokeMethod|BindingFlags.NonPublic, null, criManaMovieMaterial, null);	//CriManaMovieMaterial.Update()を呼び出すために左記の処理が必要です。criManaMovieMaterial.player.Update()ではマテリアルが更新されないのでダメです(Unityが落ちたりします)。
			}
		}
		//-----------------------------------------------------------------------------
		void IMoviePlayer.Stop() {
			//再生,又は,再生準備を停止する。
			criManaMovieMaterial.Stop();
			//プレイヤーの状態が停止中になるまで待つ。
			for(;;) {
				CriMana.Player.Status status = criManaMovieMaterial.player.status;
				if(status == CriMana.Player.Status.Error) { throw new ApplicationException(); }
				if(status == CriMana.Player.Status.Stop) { break; }
				typeof(CriManaMovieMaterial).InvokeMember("Update", BindingFlags.InvokeMethod|BindingFlags.NonPublic, null, criManaMovieMaterial, null);	//CriManaMovieMaterial.Update()を呼び出すために左記の処理が必要です。criManaMovieMaterial.player.Update()ではマテリアルが更新されないのでダメです(Unityが落ちたりします)。
			}
		}
	}
}
#endif
