#pragma once

namespace FDK
{
	private class CMediaSessionNative : IMFAsyncCallback
	{
	public:

		#pragma region [ Status ]
		//-------------------------
		class CStatus
		{
		public:
			enum EStatus
			{
				再生終了,		// 初期状態、または MESessionEnded イベント受信後。
				再生準備中,		// 非同期メソッド　IMFMediaSession::Start() 実行後。
				再生中,			// MESessionStarted イベント受信後。
				停止準備中,		// 非同期メソッド IMFMediaSession::Stop() 実行後。
				停止中,			// MESessionStopped イベント受信後。
				一時停止準備中,	// 非同期メソッド IMFMediaSession::Pause() 実行後。
				一時停止中,		// MESessionPaused イベント受信後。
				閉鎖待ち		// MESessionClosed イベントの発火を待っている状態。
			};

			CStatus()
			{
				this->nowStatus = EStatus::再生終了;
			}

			EStatus Get()
			{
				AutoLock l( this->csStatus );
				return this->nowStatus;
			}
			void Set( EStatus newStatus )
			{
				AutoLock l( this->csStatus );
				this->nowStatus = newStatus;
			}

		private:
			EStatus nowStatus;
			CritSec csStatus;

		} Status;
		//-------------------------
		#pragma endregion

		struct STMSEVENT	// PostMessage() 用構造体。
		{
			CMediaSessionNative* pMediaSessionNative;
			IMFMediaEvent* pEvent;
		};

		CMediaSessionNative();
		CMediaSessionNative( HWND hWnd, DWORD dwMediaSessionイベント処理スレッドID, LPCWSTR stgファイル名, bool b音声あり, bool bループする );
		~CMediaSessionNative();

		void t構築する( HWND hWnd, DWORD dwMediaSessionイベント処理スレッドID, LPCWSTR stgファイル名, bool b音声あり, bool bループする );
		void t再生する( UINT64 n再生開始位置100ns );	// 再生開始位置は 100ns 単位。
		void t再生処理が完了するまで待機する();
		void t停止する();
		void t一時停止する();
		void t再生を再開する();
		
		bool b再生中である();													// スレッドセーフ
		void t画像サイズを取得する( SIZE *pSize );
		UInt64 n現在のクロックを返す();
		void t透明度を設定する( UINT32 u透明度 );
		void t加算合成する( bool b加算合成する );
		void t次のサンプルを要求する();
		void t最新のサンプルを描画する( LPRECT prc描画先 );

		HRESULT tMediaSessionイベントを処理する( IMFMediaEvent* pEvent );

		// IMFAsyncCallback 実装

		STDMETHOD( GetParameters )( DWORD *pdwFlags, DWORD *pdwQueue );
		STDMETHOD( Invoke )( IMFAsyncResult *pAsyncResult );

		// IUnknown 実装
		
		STDMETHOD( QueryInterface )( REFIID riid, void **ppvObject );
		STDMETHOD_( ULONG, AddRef )();
		STDMETHOD_( ULONG, Release )();

	private:
		volatile long n参照カウンタ;

		IMFMediaSession			*pMediaSession;
		IMFSequencerSource		*pSequencerSource;
		IMFMediaSource			*pSequencerMediaSource;
		IMFVideoDisplayControl	*pControl;
		IMFAttributes			*pAttributes;

		struct STセグメント
		{
			TOPOID					TopologyID;
			MFSequencerElementId	SegmentID;
			IMFTopology*			pTopology;
			bool					b再生中;
		} Segments[ 2 ];

		Atom<bool> bループする;
		Atom<int> n表示中のindex;	// "再生中"ではなく"表示中"。TOPOSTATUS_ENDED が来た後も表示は続くことがある。

		HANDLE evMESessionClosed;
		HANDLE evMESessionStarted;
		HANDLE evMESessionStopped;

		DWORD dwイベント処理スレッドID;			// 構築時に指定されたMediaSessionイベント処理スレッドID。
		TCHAR stgファイル名[ MAX_PATH ];		// ファイル名の部分だけ。（例："StageFailed.mp4"）
		CritSec csファイル名;					// stgファイル名[] アクセス用のクリティカルセクション

	private:
		void tトポロジを作成する( HWND hWnd, LPCWSTR stgファイル名, bool b音声あり, IMFTopology** ppTopology );
		void t次のセグメントをMediaSessionに登録する( IMFPresentationDescriptor *pPD );
		void t必要ならセグメントローテーションを行う();
		void tEVRへデバイス変更通知を送る();

	public:

		// 全インスタンスの管理。

		static class AllInstances
		{
		public:
			static const int MAX_MEDIASESSION_INSTANCES = 50;
			static bool b初期化済み;
			static CritSec csInstances;
			static CMediaSessionNative* Instances[ MAX_MEDIASESSION_INSTANCES ];

			static void t追加( CMediaSessionNative *pInstance );
			static void t削除( CMediaSessionNative *pInstance );

			static void tEVRへデバイス変更通知を送る();
			static void t次のサンプルを要求する();
			static void t停止する();
		};
	};
}