#pragma once

#include "共通\CUnknown.h"

namespace FDK
{
	// インライン・ヘルパ関数

	inline MFOffset MakeOffset( float v )
	{
		MFOffset offset;
		offset.value = short( v );
		offset.fract = WORD( 65536 * ( v - offset.value ) );
		return offset;
	}
	inline MFVideoArea MakeArea( float x, float y, DWORD width, DWORD height )
	{
		MFVideoArea area;
		area.OffsetX = MakeOffset( x );
		area.OffsetY = MakeOffset( y );
		area.Area.cx = width;
		area.Area.cy = height;
		return area;
	}
	inline LONG GetOffset( const MFOffset& offset )
	{
		return (LONG) ( (float) offset.value + offset.fract / 65536.0f );
	}
	inline HRESULT tメディアタイプの属性からフレームレートを返す( IMFMediaType *pType, MFRatio *pRatio )
	{
		return ::MFGetAttributeRatio( pType, MF_MT_FRAME_RATE, (UINT32*) &pRatio->Numerator, (UINT32*) &pRatio->Denominator );
	}
	inline HRESULT GetVideoDisplayAreaAux( IMFMediaType *pType, MFVideoArea *pArea )
	{
		HRESULT hr = S_OK;
		BOOL bPanScan = FALSE;
		UINT32 width = 0, height = 0;

		bPanScan = ::MFGetAttributeUINT32( pType, MF_MT_PAN_SCAN_ENABLED, FALSE );

		// pan/scan モードの場合、属性から pan/scan 領域の取得を試みる。
		if( bPanScan )
			hr = pType->GetBlob( MF_MT_PAN_SCAN_APERTURE, (UINT8*) pArea, sizeof( MFVideoArea ), NULL );

		// pan/scan モードではない場合、または、pan/scan 領域がなかった場合は、属性からジオメトリックアパチャの取得を試みる。
		if( !bPanScan || hr == MF_E_ATTRIBUTENOTFOUND )
			hr = pType->GetBlob( MF_MT_GEOMETRIC_APERTURE, (UINT8*) pArea, sizeof( MFVideoArea ), NULL );

		// デフォルトでは全体を使う。
		if( !bPanScan || hr == MF_E_ATTRIBUTENOTFOUND )
		{
			if( SUCCEEDED( hr = ::MFGetAttributeSize( pType, MF_MT_FRAME_SIZE, &width, &height ) ) )
				*pArea = MakeArea( 0.0, 0.0, width, height );
		}

		return S_OK;
	}

	private class MediaTypeBuilder : public CUnknown
	{
	public:
		
		// static な生成メソッド。新しいメディアタイプを作る場合はこのバージョンを使うこと。
		template <class T>
		static HRESULT Create( T** ppTypeBuilder )
		{
			if( ppTypeBuilder == NULL )
				return E_POINTER;
			
			HRESULT hr = S_OK;

			T *pTypeBuilder = new T( hr );

			if( pTypeBuilder == NULL )
				return E_OUTOFMEMORY;
			
			if( SUCCEEDED( hr ) )
			{
				*ppTypeBuilder = pTypeBuilder;
				(*ppTypeBuilder)->AddRef();
			}
		
			SAFE_COM_RELEASE( pTypeBuilder );
			return hr;
		}
		
		// 既存のメディアタイプから初期化する場合はこのバージョンを使うこと。
		template <class T>
		static HRESULT Create( IMFMediaType *pType, T** ppTypeBuilder )
		{
			if( ppTypeBuilder == NULL )
				return E_POINTER;
			
			HRESULT hr = S_OK;
			
			T *pTypeBuilder = new T( pType, hr );

			if( pTypeBuilder == NULL )
				return E_OUTOFMEMORY;
			
			if( SUCCEEDED( hr ) )
			{
				*ppTypeBuilder = pTypeBuilder;
				(*ppTypeBuilder)->AddRef();
			}
			
			SAFE_COM_RELEASE( pTypeBuilder );
			return hr;
		}
		
		/// <summary>メジャータイプの GUID を返す。</summary>
		HRESULT GetMajorType( GUID *pGuid );
		
		/// <summary>メディアデータが圧縮されているかを返す。</summary>
		HRESULT IsCompressedFormat( BOOL *pbCompressed );
		
		/// <summary>指定されたメディアタイプと同一であるか調べる。</summary>
		HRESULT IsEqual( IMFMediaType *pType, DWORD *pdwFlags );
		
		/// <summary>メディアタイプのもう１つのレプレゼンテーションを取得する。</summary>
		HRESULT GetRepresentation( GUID guidRepresentation, LPVOID *ppvRepresentation );
		
		/// <summary>GetRepresentation() で確保されたメモリを解放する。</summary>
		HRESULT FreeRepresentation( GUID guidRepresentation, LPVOID pvRepresentation );


		// ヘルパメソッド
		
		// CopyFrom メソッド; 指定されたメディアタイプの属性をすべて this にコピーする。
		HRESULT CopyFrom( MediaTypeBuilder *pType );
		HRESULT CopyFrom( IMFMediaType *pType );
		
		HRESULT SetMajorType( GUID guid );
		HRESULT GetMediaType( IMFMediaType **ppType );
		HRESULT SetSubType( GUID guid );
		HRESULT GetSubType( GUID *pGuid );
		
		/// <summary>サブタイプから FOURCC を抽出する。すべてのサブタイプがこのパターンに当てはまる訳ではない。</summary>
		HRESULT GetFourCC( DWORD *pFourCC );

		/// <summary>各サンプルが、ストリーム中の他のサンプルから独立しているかを調べる。</summary>
		HRESULT GetAllSamplesIndependent( BOOL* pbIndependent );
		
		/// <summary>各サンプルが、ストリーム中の他のサンプルから独立しているか否かを指定する。</summary>
		HRESULT SetAllSamplesIndependent( BOOL bIndependent );
		
		/// <summary>サンプルが固定サイズか？</summary>
		HRESULT GetFixedSizeSamples( BOOL *pbFixed );
		
		/// <summary>サンプルが固定サイズかどうかを設定する。</summary>
		HRESULT SetFixedSizeSamples( BOOL bFixed );
		
		/// <summary>サンプルのサイズ（バイト単位）を取得する。</summary>
		HRESULT GetSampleSize( UINT32 *pnSize );
		
		/// <summary>サンプルのサイズ（バイト単位）を設定する。</summary>
		HRESULT SetSampleSize( UINT32 nSize );
		
		/// <summary>MFWrapMediaType() 関数でラップされたメディアタイプを取得する。</summary>
		HRESULT Unwrap( IMFMediaType **ppOriginal );
		
		BOOL AllSamplesIndependent();
		BOOL FixedSizeSamples();
		UINT32 SampleSize();

	protected:
		IMFMediaType *m_pType;

		MediaTypeBuilder( IMFMediaType* pType, HRESULT& hr );
		MediaTypeBuilder( HRESULT& hr );

		BOOL IsValid( );
		IMFMediaType *GetMediaType( );
		virtual ~MediaTypeBuilder( );
	};
	class VideoTypeBuilder : public MediaTypeBuilder
	{
		friend class MediaTypeBuilder; 

	protected:

		VideoTypeBuilder( IMFMediaType *pType, HRESULT& hr );
		VideoTypeBuilder( HRESULT& hr );

	public:

		/// <summary>フレームがどのようにインタレース化されているかの状況を取得する。</summary>
		HRESULT GetInterlaceMode( MFVideoInterlaceMode *pmode );

		/// <summary>フレームがどのようにインタレース化されているかの状況を設定する。</summary>
		HRESULT SetInterlaceMode( MFVideoInterlaceMode mode );

		/// <summary>デフォルトストライドを取得するか、算出を試みる。</summmary>
		HRESULT GetDefaultStride( INT32 *pnStride );

		/// <summary>デフォルトストライドを設定する。この値は、非圧縮データフォーマットに対してのみ意味がある。</summary>
		HRESULT SetDefaultStride( INT32 nStride );
		
		/// <summary>ビデオフレームの幅と高さを取得する。</summary>
		HRESULT GetFrameDimensions( UINT32 *pdwWidthInPixels, UINT32 *pdwHeightInPixels );

		/// <summary>ビデオフレームの幅と高さを設定する。</summary>
		HRESULT SetFrameDimensions( UINT32 dwWidthInPixels, UINT32 dwHeightInPixels );

		/// <summary>データエラー率（Bitエラー数/秒）を取得する。</summary>
		HRESULT GetDataBitErrorRate( UINT32 *pRate );

		/// <summary>データエラー率（bitエラー数/秒）を設定する。</summary>
		HRESULT SetDataBitErrorRate( UINT32 rate );

		/// <summary> Retrieves the c data rate of the video stream.
		HRESULT GetAverageBitRate( UINT32 *pRate );

		/// <summary>ビデオストリームのおおよそのデータレートを設定する。</summary>
		HRESULT SetAvgerageBitRate( UINT32 rate );

		/// <summary>カスタムカラーの初期値を取得する。</summary>
		HRESULT GetCustomVideoPrimaries( MT_CUSTOM_VIDEO_PRIMARIES *pPrimaries );

		/// <summary>カスタムカラーの初期値を設定する。</summary>
		HRESULT SetCustomVideoPrimaries( const MT_CUSTOM_VIDEO_PRIMARIES& primary );

		/// <summary>フレームレート（FPS）を取得する。</summary>
		HRESULT GetFrameRate( UINT32 *pnNumerator, UINT32 *pnDenominator );
		
		/// <summary>フレームレートを比率で取得する。</summary>
		HRESULT GetFrameRate( MFRatio *pRatio );

		/// <summary>フレームレート（FPS）を設定する。</summary>
		HRESULT SetFrameRate( UINT32 nNumerator, UINT32 nDenominator );
		
		/// <summary>フレームレートを比率で設定する。</summary>
		HRESULT SetFrameRate( const MFRatio& ratio );

		/// <summary>ジオメトリックアパチャを取得する。</summary>
		HRESULT GetGeometricAperture( MFVideoArea *pArea );

		/// <summary>ジオメトリックアパチャを設定する。</summary>
		HRESULT SetGeometricAperture( const MFVideoArea& area );
		
		/// <summary>キーフレーム間の最大フレーム数を取得する。</summary>
		HRESULT GetMaxKeyframeSpacing( UINT32 *pnSpacing );

		/// <summary>キーフレーム間の最大フレーム数を設定する。</summary>
		HRESULT SetMaxKeyframeSpacing( UINT32 nSpacing );

		/// <summary>正しい信号の範囲を含む領域を取得する。</summary>
		HRESULT GetMinDisplayAperture( MFVideoArea *pArea );

		/// <summary>正しい信号の範囲を含む領域を設定する。</summary>
		HRESULT SetMinDisplayAperture( const MFVideoArea& area );

		/// <summary>ビデオメディアタイプに対する出力矩形のアスペクト比を取得する。</summary>
		HRESULT GetPadControlFlags( MFVideoPadFlags *pFlags );
		
		/// <summary>ビデオメディアタイプに対して、出力矩形のアスペクト比を設定する。</summary>
		HRESULT SetPadControlFlags( MFVideoPadFlags flags );

		/// <summary>ビデオメディアタイプに対するパレットエントリの配列を取得する。</summary>
		HRESULT GetPaletteEntries( MFPaletteEntry *paEntries, UINT32 nEntries );

		/// <summary>ビデオメディアタイプに対して、パレットエントリの配列を設定する。</summary>
		HRESULT SetPaletteEntries( MFPaletteEntry *paEntries, UINT32 nEntries );

		/// <summary>パレットエントリ数を取得する。</summary>
		HRESULT GetNumPaletteEntries( UINT32 *pnEntries );
		
		/// <summary>pan/scanモードにおいて、表示されるべきビデオの領域を取得する。</summary>
		HRESULT GetPanScanAperture( MFVideoArea *pArea );

		/// <summary>pan/scanモードにおいて、表示されるべきビデオの領域を設定する。</summary>
		HRESULT SetPanScanAperture( const MFVideoArea& area );

		/// <symmary>pan/scanモードが利用可能かどうかを問い合わせる。</summary>
		HRESULT IsPanScanEnabled( BOOL *pBool );

		/// <summary>pan/scanモードの有効・無効を設定する。</summary>
		HRESULT SetPanScanEnabled( BOOL bEnabled );

		/// <summary>ピクセルアスペクト比を取得する。</summary>
		HRESULT GetPixelAspectRatio( UINT32 *pnNumerator, UINT32 *pnDenominator );

		/// <summary>ピクセルアスペクト比を設定する。</summary>
		HRESULT SetPixelAspectRatio( UINT32 nNumerator, UINT32 nDenominator );

		/// <summary>ピクセルアスペクト比を設定する。</summary>
		HRESULT SetPixelAspectRatio( const MFRatio& ratio );

		/// <summary>所期のアスペクト比を取得する。</summary>
		HRESULT GetSourceContentHint( MFVideoSrcContentHintFlags *pFlags );
		
		/// <summary>所期のアスペクト比を設定する。</summary>
		HRESULT SetSourceContentHint( MFVideoSrcContentHintFlags nFlags );

		/// <summary>RGBをR'G'B'に変換する関数を列挙する。</summary>
		HRESULT GetTransferFunction( MFVideoTransferFunction *pnFxn );

		/// <summary>RGBをR'G'B'に変換する関数の羅列を設定する。</summary>
		HRESULT SetTransferFunction( MFVideoTransferFunction nFxn );

		/// <summary>Y'Cb'Cr'ビデオメディアタイプにおいて、クロマがサンプルされる方法を取得する。</summary>
		HRESULT GetChromaSiting( MFVideoChromaSubsampling *pSampling );

		/// <summary>Y'Cb'Cr'ビデオメディアタイプにおいて、クロマがサンプルされる方法を設定する。</summary>
		HRESULT SetChromaSiting( MFVideoChromaSubsampling nSampling );

		/// <summary>ビューのライティング状態を取得する。</summary>
		HRESULT GetVideoLighting( MFVideoLighting *pLighting );

		/// <summary>ビューのライティング状態を設定する。</summary>
		HRESULT SetVideoLighting( MFVideoLighting nLighting );

		/// <summary>ビデオメディアタイプにおける色情報の範囲を取得する。</summary>
		HRESULT GetVideoNominalRange( MFNominalRange *pRange );

		/// <summary>ビデオメディアタイプに対して、色情報の範囲を設定する。</summary>
		HRESULT SetVideoNominalRange( MFNominalRange nRange );

		/// <summary>ビデオメディアタイプにおける初期色を取得する。</summary>
		HRESULT GetVideoPrimaries( MFVideoPrimaries *pPrimaries );

		/// <summary>ビデオメディアタイプにおける初期色を設定する。</summary>
		HRESULT SetVideoPrimaries( MFVideoPrimaries nPrimaries );

		/// <summary>Y'Cb'Cr' 色空間を R'G'B' 色空間に変換する行列を列挙する。</summary>
		HRESULT GetYUVMatrix( MFVideoTransferMatrix *pMatrix );

		/// <summary>Y'Cb'Cr' 色空間を R'G'B' 色空間に変換する行列を設定する。</summary>
		HRESULT SetYUVMatrix( MFVideoTransferMatrix nMatrix );
		
		MFRatio GetPixelAspectRatio();	// デフォルトは 1:1（正方形ピクセル）。
		BOOL IsPanScanEnabled();		// デフォルトは FALSE。
		
		/// <summary>
		/// 戻り値（優先順）：
		/// 1. pan/scan モードが有効の場合には、pan/scan 領域を返す。
		/// 2. ジオメトリックアパチャを返す。
		/// 3. ビデオ領域全体を返す。
		/// </summary>
		HRESULT GetVideoDisplayArea( MFVideoArea *pArea );
	};
	class AudioTypeBuilder : public MediaTypeBuilder
	{
		friend class MediaTypeBuilder;
	
	protected:

		AudioTypeBuilder( IMFMediaType* pType, HRESULT& hr );
		AudioTypeBuilder( HRESULT& hr );
	
	public:
		
		/// <summary>平均ビットレート(bps)を取得する。</summary>
		HRESULT GetAvgerageBytesPerSecond( UINT32 *pBytes );

		/// <summary>平均ビットレート(bps)を設定する。</summary>
		HRESULT SetAvgerageBytesPerSecond( UINT32 nBytes );

		/// <summary>オーディオのサンプルビット数を取得する。</summary>
		HRESULT GetBitsPerSample( UINT32 *pBits );
		
		/// <summary>オーディオのサンプルビット数を設定する。</summary>
		HRESULT SetBitsPerSample( UINT32 nBits );

		/// <summary>ブロック境界をバイト単位で取得する。</summary>
		HRESULT GetBlockAlignment( UINT32 *pBytes );

		/// <summary>ブロック境界をバイト単位で設定する。</summary>
		HRESULT SetBlockAlignment( UINT32 nBytes );

		/// <summary>オーディオチャンネルのスピーカー配置位置を取得する。</summary>
		HRESULT GetChannelMask( UINT32 *pMask );

		/// <summary>オーディオチャンネルのスピーカー配置位置を設定する。</summary>
		HRESULT SetChannelMask( UINT32 nMask );

		/// <summary>秒ごとのオーディオサンプル数を浮動小数値で取得する。</summary>
		HRESULT GetFloatSamplesPerSecond( double *pfSampleRate );

		/// <summary>秒ごとのオーディオサンプル数を浮動小数値で設定する。</summary>
		HRESULT SetFloatSamplesPerSecond( double fSampleRate );

		/// <summary>オーディオチャンネル数を取得する。</summary>
		HRESULT GetNumChannels( UINT32 *pnChannels );

		/// <summary>オーディオチャンネル数を設定する。</summary>
		HRESULT SetNumChannels( UINT32 nChannels );

		/// <summary>オーディオデータの圧縮ブロックを含んでいるサンプルの数を取得する。</summary>
		HRESULT GetSamplesPerBlock( UINT32 *pnSamples );

		/// <summary>オーディオデータの圧縮ブロックを含んでいるサンプルの数を設定する。</summary>
		HRESULT SetSamplesPerBlock( UINT32 nSamples );

		/// <summary>秒ごとのオーディオサンプル数を整数値で取得する。</summary>
		HRESULT GetSamplesPerSecond( UINT32 *pnSampleRate );

		/// <summary>秒ごとのオーディオサンプル数を整数値で設定する。</summary>
		HRESULT SetSamplesPerSecond( UINT32 nSampleRate );

		/// <summary>各オーディオサンプルにおける有効ビット数を取得する。</summary>
		HRESULT GetValidBitsPerSample( UINT32 *pnBits );

		/// <summary>各オーディオサンプルにおける有効ビット数を設定する。</summary>
		HRESULT SetValidBitsPerSample( UINT32 nBits );
		
		UINT32 AvgerageBytesPerSecond();
		UINT32 BitsPerSample();
		UINT32 GetBlockAlignment();
		double FloatSamplesPerSecond();
		UINT32 NumChannels();
		UINT32 SamplesPerSecond();
	};
}