#pragma once

namespace FDK
{
	// IMFAsyncCallback の特殊な実装。
	//
	// コールバックの実装の他に、「親」オブジェクトとその「メソッド」を指定することができ、
	// IMFAsyncCallback::Invoke 時には、その「親」の「メソッド」が呼び出される。
	template<class T>	// T は親オブジェクトの型。IUnknown を実装していること。
	private class AsyncCallback : public IMFAsyncCallback
	{
	public: 

		typedef HRESULT( T::*InvokeFn )( IMFAsyncResult *pAsyncResult );

		T *m_pParent;
		InvokeFn m_pInvokeFn;

		// コンストラクタで、IMFAsyncCallback を実装する親インスタンスと、IMFAsyncCallback::Invoke の呼び出し先を指定する。
		AsyncCallback( T *pParent, InvokeFn fn )
		{
			Debug::Assert( pParent != NULL );

			this->m_pParent = pParent;
			this->m_pInvokeFn = fn;
		}
		~AsyncCallback()
		{
		}

		#pragma region [ IUnknown の実装 ]
		//-----------------
		STDMETHODIMP_( ULONG ) AddRef()
		{
			// 親クラスへ丸投げ。
			return m_pParent->AddRef();
		}
		STDMETHODIMP_( ULONG ) Release()
		{
			// 親クラスへ丸投げ。
			return m_pParent->Release();
		}
		STDMETHODIMP QueryInterface( REFIID iid, void** ppv )
		{
			if( ppv == NULL )
				return E_POINTER;

			if( iid == __uuidof( IUnknown ) )
			{
				*ppv = static_cast<IUnknown*>( static_cast<IMFAsyncCallback*>( this ) );
			}
			else if( iid == __uuidof( IMFAsyncCallback ) )
			{
				*ppv = static_cast<IMFAsyncCallback*>( this );
			}
			else
			{
				*ppv = NULL;
				return E_NOINTERFACE;
			}

			this->AddRef();

			return S_OK;
		}
		//-----------------
		#pragma endregion

		#pragma region [ IMFAsyncCallback の実装 ]
		//-----------------
		STDMETHODIMP GetParameters( DWORD *pdwFlags, DWORD *pdwQueue )
		{
			// オプションなので未実装。
			return E_NOTIMPL;
		}
		STDMETHODIMP Invoke( IMFAsyncResult *pAsyncResult )
		{
			return ( m_pParent->*m_pInvokeFn )( pAsyncResult );
		}
		//-----------------
		#pragma endregion
	};
}
