#pragma once

#include "Cスレッドセーフキュー.h"

namespace FDK
{
	private struct STスケジューラコールバック
	{
		virtual HRESULT tサンプルを受け取る( IMFSample *pSample, LONGLONG llTarget ) = 0;
	};

	//-----------------------------------------------------------------------------
	// Cスケジューラ クラス
	//
	// サンプルが表示されるべき時刻をスケジューリングする。
	//
	// 注意: サンプルの表示は、別のオブジェクトへコールバックして行う。
	//       そのオブジェクトは、SchedulerCallback::tサンプルを表示する() を実装し、
	//       Cスケジューラ::tコールバックを設定する() で設定しておくこと。
	//
	// 一般的な設計:
	// ・スケジューラは、一般的に、サンプルの表示時刻より前にサンプルを受け取る。
	// ・スケジューラはサンプルをキューに入れ、ワーカスレッド上でそれらを FIFO 順序で表示する。
	// ・スケジューラは、ワーカスレッドとはスレッドメッセージの送信によって通信する。
	//
	// 呼び出し元は、サンプルの表示を即座に行うオプションを選択できる。(repaint などで使う）
	//-----------------------------------------------------------------------------

	private class Cスケジューラ
	{
	public:
		enum SCHEDULE_EVENT		// スケジューラスレッドへ送るイベント。
		{
			SCHEDULE_EVENT_終了 =						WM_USER,
			SCHEDULE_EVENT_サンプルをキューに入れた =	WM_USER + 1,
			SCHEDULE_EVENT_フラッシュ =					WM_USER + 2
		};

		Cスケジューラ();
		virtual ~Cスケジューラ();

		// これを設定しないとサンプルは表示されない。
		void tコールバックを設定する( STスケジューラコールバック *pコールバック構造体 );

		HRESULT tスケジューラをワーカスレッドで開始する( IMFClock *pClock );
		HRESULT tスケジューラを停止しワーカスレッドを終了する();
		HRESULT t新しいサンプルを表示するかスケジュールキューに入れる( IMFSample *pSample, BOOL bPresentNow );
		HRESULT tサンプルキューをフラッシュする();

		void tフレームレートを設定する( const MFRatio& fps );
		void tクロックレートを設定する( float fRate );
    
		const LONGLONG& ll最後のサンプル時刻() const;
		const LONGLONG& llフレーム間隔() const;

	private: 
		DWORD procスケジューラスレッドエントリ();
		HRESULT tキュー内のサンプルを可能な限り表示する( LONG *plNextSleep );
		HRESULT tサンプルの表示時刻判定と表示を行う・表示しないならキューに戻す( IMFSample *pSample, LONG *plNextSleep );

		// スケジューラスレッドエントリ。ここから lpParameter->procスケジューラスレッドエントリ() に飛ばす。
		static DWORD WINAPI SchedulerThreadProc( LPVOID lpParameter );

	private:
		const MFTIME ONE_SECOND = 10000000;
		const LONG   ONE_MSEC = 1000;

		/// <summary> 1 time ＝ 100ns。</summary>
		inline LONG TimeToMsec( const LONGLONG& time )
		{
			return (LONG) ( time / ( ONE_SECOND / ONE_MSEC ) );
		}

	private:
		Cスレッドセーフキュー<IMFSample>	m_サンプルスケジュールキュー;
		IMFClock							*m_pプレゼンテーションクロック;  // NULL可。
		STスケジューラコールバック			*m_p表示用コールバック;

		DWORD		m_nスレッドID;
		HANDLE		m_hスケジューラスレッドハンドル;
		HANDLE		m_hスケジューラスレッド準備完了イベント;
		HANDLE		m_hフラッシュ完了イベント;

		float		m_f再生レート;
		MFTIME		m_フレーム間隔;
		LONGLONG	m_フレーム間隔の４分の１;
		MFTIME		m_最後のサンプル時刻;
	};
}
