#pragma once

#pragma warning( disable : 4355 )  // warning C4355: 'this' : ベース メンバ初期化子リストで使用されました。→ コンストラクタの m_SampleFreeCB の初期化部分で発生する。

#include "共通\ComPtrList.h"
#include "共通\CritSec.h"
#include "Cスケジューラ.h"
#include "Cサンプルプール.h"
#include "PresenterEngine.h"
#include "AsyncCallback.h"

namespace FDK
{
	private enum RENDER_STATE
	{
		RENDER_STATE_再生中 = 1,
		RENDER_STATE_停止中,
		RENDER_STATE_一時停止中,
		RENDER_STATE_シャットダウン済み,  // 初期状態
	};
	private enum FRAMESTEP_STATE
	{
		FRAMESTEP_してない,
		FRAMESTEP_してるけどクロックは始まってない,
		FRAMESTEP_クロックは始まっていてサンプルを待っている,
		FRAMESTEP_描画するサンプルが受領された,
		FRAMESTEP_サンプルの描画は完了した
	};

	
	const DWORD MFVideoRenderPrefs_DisplayChange = 0x00001000;		// EVR へ EC_DISPLAY_CHANGE を通知せよ、という独自定義定数。IVideoDisplayControl::SetRenderingPrefs() で使う。
	const DWORD MFVideoRenderPrefs_DisplaySample = 0x00001001;		// プレゼンタへ現在の最新のサンプルを表示せよ、という独自定義定数。同上。
	const DWORD MFVideoRenderPrefs_RequestNextSample = 0x00001002;	// プレゼンタに次のサンプル表示を要求せよ、という独自定義定数。同上。

	////////////////////////////

	private class CustomEVRPresenter :
		public IMFVideoDeviceID,
		public IMFVideoPresenter,	// IMFClockStateSink を継承する。
		public IMFRateSupport,
		public IMFGetService,
		public IMFTopologyServiceLookupClient,
		public IMFVideoDisplayControl,
		public IMFActivate			// IMFAttributes を継承する。
	{

	public:
		// カスタムプレゼンタのインスタンスの生成
		static HRESULT CreateInstance( IUnknown *pUnkOuter, REFIID iid, void **ppv );

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

		// IMFGetService
		STDMETHOD( GetService )( REFGUID guidService, REFIID riid, LPVOID *ppvObject );
		
		// IMFTopologyServiceLookupClient 
		STDMETHOD( InitServicePointers )( IMFTopologyServiceLookup *pLookup );
		STDMETHOD( ReleaseServicePointers )();
		
		// IMFVideoPresenter
		STDMETHOD( ProcessMessage )( MFVP_MESSAGE_TYPE eMessage, ULONG_PTR ulParam );
		STDMETHOD( GetCurrentMediaType )( IMFVideoMediaType** ppMediaType );

		// IMFClockStateSink
		STDMETHOD( OnClockStart )( MFTIME hnsSystemTime, LONGLONG llClockStartOffset );
		STDMETHOD( OnClockStop )( MFTIME hnsSystemTime );
		STDMETHOD( OnClockPause )( MFTIME hnsSystemTime );
		STDMETHOD( OnClockRestart )( MFTIME hnsSystemTime );
		STDMETHOD( OnClockSetRate )( MFTIME hnsSystemTime, float fRate );
		
		// IMFRateSupport
		STDMETHOD( GetSlowestRate )( MFRATE_DIRECTION eDirection, BOOL bThin, float *pfRate );
		STDMETHOD( GetFastestRate )( MFRATE_DIRECTION eDirection, BOOL bThin, float *pfRate );
		STDMETHOD( IsRateSupported )( BOOL bThin, float fRate, float *pfNearestSupportedRate );
		
		// IMFVideoDeviceID
		STDMETHOD( GetDeviceID )( IID* pDeviceID );
		
		// IMFVideoDisplayControl
		STDMETHOD( SetVideoPosition )( const MFVideoNormalizedRect* pnrcSource, const LPRECT prcDest );
		STDMETHOD( GetVideoPosition )( MFVideoNormalizedRect* pnrcSource, LPRECT prcDest );
		STDMETHOD( SetVideoWindow )( HWND hwndVideo );
		STDMETHOD( GetVideoWindow )( HWND* phwndVideo );
		STDMETHOD( RepaintVideo )();
		STDMETHOD( SetRenderingPrefs )(DWORD dwRenderFlags);
		STDMETHOD( GetNativeVideoSize )(SIZE* pszVideo, SIZE* pszARVideo);
		#pragma region [ 以下、E_NOTIMPL ]
		//-----------------
		STDMETHOD(GetIdealVideoSize)(SIZE* pszMin, SIZE* pszMax) { return E_NOTIMPL; }
		STDMETHOD(SetAspectRatioMode)(DWORD dwAspectRatioMode) { return E_NOTIMPL; }
		STDMETHOD(GetAspectRatioMode)(DWORD* pdwAspectRatioMode) { return E_NOTIMPL; }
		STDMETHOD(GetCurrentImage)(BITMAPINFOHEADER* pBih, BYTE** pDib, DWORD* pcbDib, LONGLONG* pTimeStamp) { return E_NOTIMPL; }
		STDMETHOD(SetBorderColor)(COLORREF Clr) { return E_NOTIMPL; }
		STDMETHOD(GetBorderColor)(COLORREF* pClr) { return E_NOTIMPL; }
		STDMETHOD(GetRenderingPrefs)(DWORD* pdwRenderFlags) { return E_NOTIMPL; }
		STDMETHOD(SetFullscreen)(BOOL bFullscreen) { return E_NOTIMPL; }
		STDMETHOD(GetFullscreen)(BOOL* pbFullscreen) { return E_NOTIMPL; }
		//-----------------
		#pragma endregion

		// IMFActivate
		STDMETHOD( ActivateObject )( REFIID riid, void **ppv );
		STDMETHOD( ShutdownObject )();
		#pragma region [ 以下、E_NOTIMPL ]
		//-----------------
		STDMETHOD(DetachObject)() { return E_NOTIMPL; }
		//-----------------
		#pragma endregion

		// IMFAttributes
		STDMETHOD(GetUINT32)(REFGUID guidKey, UINT32 *punValue);
		STDMETHOD(SetUINT32)(REFGUID guidKey, UINT32 unValue);
		#pragma region [ 以下、_ENOTIMPL ]
		//-----------------
		STDMETHOD(GetItem)(REFGUID guidKey, PROPVARIANT *pValue) { return E_NOTIMPL; }
		STDMETHOD(GetItemType)(REFGUID guidKey, MF_ATTRIBUTE_TYPE *pType) { return E_NOTIMPL; }
		STDMETHOD(CompareItem)(REFGUID guidKey, REFPROPVARIANT Value, BOOL *pbResult) { return E_NOTIMPL; }
		STDMETHOD(Compare)(IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL *pbResult) { return E_NOTIMPL; }
		STDMETHOD(GetUINT64)(REFGUID guidKey, UINT64 *punValue) { return E_NOTIMPL; }
		STDMETHOD(GetDouble)(REFGUID guidKey, double *pfValue) { return E_NOTIMPL; }
		STDMETHOD(GetGUID)(REFGUID guidKey, GUID *pguidValue) { return E_NOTIMPL; }
		STDMETHOD(GetStringLength)(REFGUID guidKey, UINT32 *pcchLength) { return E_NOTIMPL; }
		STDMETHOD(GetString)(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32 *pcchLength) { return E_NOTIMPL; }
		STDMETHOD(GetAllocatedString)(REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength) { return E_NOTIMPL; }
		STDMETHOD(GetBlobSize)(REFGUID guidKey, UINT32 *pcbBlobSize) { return E_NOTIMPL; }
		STDMETHOD(GetBlob)(REFGUID guidKey, UINT8 *pBuf,UINT32 cbBufSize, UINT32 *pcbBlobSize) { return E_NOTIMPL; }
		STDMETHOD(GetAllocatedBlob)(REFGUID guidKey, UINT8 **ppBuf, UINT32 *pcbSize) { return E_NOTIMPL; }
		STDMETHOD(GetUnknown)(REFGUID guidKey, REFIID riid, LPVOID *ppv) { return E_NOTIMPL; }
		STDMETHOD(SetItem)(REFGUID guidKey, REFPROPVARIANT Value) { return E_NOTIMPL; }
		STDMETHOD(DeleteItem)(REFGUID guidKey) { return E_NOTIMPL; }
		STDMETHOD(DeleteAllItems)() { return E_NOTIMPL; }
		STDMETHOD(SetUINT64)(REFGUID guidKey, UINT64 unValue) { return E_NOTIMPL; }
		STDMETHOD(SetDouble)(REFGUID guidKey, double fValue) { return E_NOTIMPL; }
		STDMETHOD(SetGUID)(REFGUID guidKey,REFGUID guidValue) { return E_NOTIMPL; }
		STDMETHOD(SetString)(REFGUID guidKey, LPCWSTR wszValue) { return E_NOTIMPL; }
		STDMETHOD(SetBlob)(REFGUID guidKey, const UINT8 *pBuf, UINT32 cbBufSize) { return E_NOTIMPL; }
		STDMETHOD(SetUnknown)(REFGUID guidKey, IUnknown *pUnknown) { return E_NOTIMPL; }
		STDMETHOD(LockStore)() { return E_NOTIMPL; }
		STDMETHOD(UnlockStore)() { return E_NOTIMPL; }
		STDMETHOD(GetCount)(UINT32 *pcItems) { return E_NOTIMPL; }
		STDMETHOD(GetItemByIndex)( UINT32 unIndex, GUID *pguidKey, PROPVARIANT *pValue) { return E_NOTIMPL; }
		STDMETHOD(CopyAllItems)(IMFAttributes *pDest) { return E_NOTIMPL; }
		//-----------------
		#pragma endregion

	protected:

		// FrameStep: フレームステッピング関連の情報を持つ。
		// 注意: この構造体の目的は、１変数にデータを組織化して簡略化することである。
		struct FrameStep
		{
			FrameStep() : state(FRAMESTEP_してない), steps(0), pSampleNoRef(NULL)
			{
			}

			FRAMESTEP_STATE     state;          // Current frame-step state.
			CVideoSampleList    samples;        // List of pending samples for frame-stepping.
			DWORD               steps;          // Number of steps left.
			DWORD_PTR           pSampleNoRef;   // Identifies the frame-step sample.
		};

		Atom<RENDER_STATE>          m_RenderState;          // Rendering state.
		FrameStep                   m_FrameStep;            // Frame-stepping information.

		CritSec                     m_ObjectLock;           // Serializes our public methods.  
		
		// サンプルとスケジューリング
		Cスケジューラ               m_scheduler;            // Manages scheduling of samples.
		Cサンプルプール             m_SamplePool;           // Pool of allocated samples.
		DWORD                       m_TokenCounter;         // Counter. Incremented whenever we create new samples.

		// レンダリングステート
		BOOL                        m_bSampleNotify;        // Did the mixer signal it has an input sample?
		BOOL                        m_bRepaint;             // Do we need to repaint the last sample?
		BOOL                        m_bPrerolled;           // Have we presented at least one sample?
		BOOL                        m_bEndStreaming;        // Did we reach the end of the stream (EOS)?

		MFVideoNormalizedRect       m_nrcSource;            // Source rectangle.
		float                       m_fRate;                // Playback rate.

		// 削除可能なオブジェクト
		PresenterEngine             *m_pPresenterEngine;	// Rendering engine. (Never null if the constructor succeeds.)

		// COM インターフェース
		IMFClock                    *m_pClock;               // The EVR's clock.
		IMFTransform                *m_pMixer;               // The mixer.
		IMediaEventSink             *m_pMediaEventSink;      // The EVR's event-sink interface.
		IMFMediaType                *m_pMediaType;           // Output media type

		volatile long				n参照カウンタ;			// IUnknown 用参照カウンタ

		CustomEVRPresenter( HRESULT &hr );
		~CustomEVRPresenter();

		HRESULT tシャットダウン状態ならエラーを返す();
		BOOL b再生中または一時停止中である();
		BOOL IsScrubbing() const;	// IsScrubbing: Scrubbing occurs when the frame rate is 0.
		HRESULT tEVRにイベントを通知する( long EventCode, LONG_PTR Param1, LONG_PTR Param2 );
		float GetMaxRate( BOOL bThin );
		float MFOffsetToFloat( const MFOffset& offset );	// MFOffsetToFloat: Convert a fixed-point to a float.

		// ミキサー操作
		HRESULT tミキサのDeviceIDを確認し転送元矩形を設定する( IMFTransform *pMixer );

		// フォーマット
		HRESULT CreateOptimalVideoType( IMFMediaType* pProposedType, IMFMediaType **ppOptimalType );
		HRESULT SetMediaType( IMFMediaType *pMediaType );
		HRESULT IsMediaTypeSupported( IMFMediaType *pMediaType );

		// メッセージハンドリング
		HRESULT Flush();
		HRESULT RenegotiateMediaType();
		HRESULT ProcessInputNotify();
		HRESULT BeginStreaming();
		HRESULT EndStreaming();
		HRESULT CheckEndOfStream();

		// サンプル管理
		void    ProcessOutputLoop();
		HRESULT ProcessOutput();
		HRESULT DeliverSample( IMFSample *pSample, BOOL bRepaint );
		HRESULT TrackSample( IMFSample *pSample );
		void    ReleaseResources();

		// フレームステッピング
		HRESULT PrepareFrameStep( DWORD cSteps );
		HRESULT StartFrameStep();
		HRESULT DeliverFrameStepSample( IMFSample *pSample );
		HRESULT CompleteFrameStep( IMFSample *pSample );
		HRESULT CancelFrameStep();

		// サンプル解放コールバック
		AsyncCallback<CustomEVRPresenter>   m_SampleFreeCB;	// ビデオサンプルが解放されたときに呼び出されるIMFAsyncCallbackコールバックオブジェクト。
		HRESULT OnSampleFree( IMFAsyncResult *pResult );	// 上記コールバックオブジェクトが IMFAsyncCallback::Invoke 時に呼び出すメソッド。

	private:

		RECT	m_rcDest;
		MFRatio g_DefaultFrameRate;

		RECT    CorrectAspectRatio( const RECT& src, const MFRatio& srcPAR, const MFRatio& destPAR );
		BOOL    AreMediaTypesEqual( IMFMediaType *pType1, IMFMediaType *pType2 );
		HRESULT ValidateVideoArea( const MFVideoArea& area, UINT32 width, UINT32 height );
		HRESULT SetDesiredSampleTime( IMFSample *pSample, const LONGLONG& hnsSampleTime, const LONGLONG& hnsDuration );
		HRESULT ClearDesiredSampleTime( IMFSample *pSample );
		BOOL    IsSampleTimePassed( IMFClock *pClock, IMFSample *pSample );
		HRESULT SetMixerSourceRect( IMFTransform *pMixer, const MFVideoNormalizedRect& nrcSource );

		UINT32	uテクスチャの透明度;	// 0:透明〜255:不透明
		UINT32	uテクスチャの加算合成;	// 0:OFF, 他:ON
	};
}
