#include "stdafx.h"
#include "refCInput管理.h"
#include "refCInputKeyboard.h"
#include "refCInputMouse.h"
#include "refCInputMIDI.h"

namespace FDK
{
// public:

	CInput管理::CInput管理( IntPtr hWnd )
	{
		HRESULT hr = S_OK;


		// MIDI入力用コールバックの作成（全MIDI入力デバイスで共通）

		this->dgMidiInProc = gcnew MidiInProcDelegate( this, &CInput管理::midiInProc );
		this->gchMidiInProc = GCHandle::Alloc( this->dgMidiInProc );
		DWORD_PTR pdwMidiInProc = reinterpret_cast<DWORD_PTR>( Marshal::GetFunctionPointerForDelegate( this->dgMidiInProc ).ToPointer() );


		// 各メンバの初期化

		this->Disposed = gcnew CDisposed();
		this->pDI = NULL;
		this->_hWnd = hWnd;
		this->timer = gcnew CTimer( CTimer::E種別::MultiMedia );
		this->_list入力デバイス = gcnew List<IInputDevice^>( 10 );


		// DirectInput の生成

		IDirectInput8 *pdi;
		tFAILEDなら例外発生( L"DirectInput の生成",
			hr = ::DirectInput8Create( ( HINSTANCE )::GetModuleHandle( NULL ), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**) &pdi, NULL ) );
		this->pDI = pdi;


		// (1) キーボード

		this->list入力デバイス->Add( gcnew CInputKeyboard( hWnd, this->pDI ) );


		// (2) マウス

		this->list入力デバイス->Add( gcnew CInputMouse( hWnd, this->pDI ) );


		// (3) MIDI入力

		this->_nMIDIデバイス数 = ::midiInGetNumDevs();
		
		for( UInt32 i = 0; i < this->_nMIDIデバイス数; i++ )
		{
			MMRESULT mhr = 0;

			CInputMIDI^ item = gcnew CInputMIDI( i );
			this->_list入力デバイス->Add( item );

			MIDIINCAPS caps;
			mhr = ::midiInGetDevCaps( i, &caps, sizeof(MIDIINCAPS) );
			if( mhr != 0 )
				continue;	// エラー

			item->strデバイス名 = gcnew String( caps.szPname );

			HMIDIIN hMidiIn = (HMIDIIN) item->hMidiIn;
			mhr = ::midiInOpen( &hMidiIn, i, pdwMidiInProc, NULL, CALLBACK_FUNCTION );
			if( mhr == 0 && hMidiIn != 0 )
			{
				::midiInStart( hMidiIn );	// 入力受付開始
				item->hMidiIn = (UInt32) hMidiIn;
			}
		}

		// (4) ジョイスティック；未対応

	}
	CInput管理::~CInput管理()
	{
		if( this->Disposed->bDispose済みまたは処理中である )
			return;		// 例外は発出しない。

		if( !this->Disposed->t開始を宣言する() )
			return;		// 例外は発出しない。


		if( nullptr != this->_list入力デバイス )
		{
			// 全MIDI入力デバイスを停止し解放する。

			for each( IInputDevice^ device in this->_list入力デバイス )
			{
				// 停止
				if( device->e入力デバイス種別 == E入力デバイス種別::MIDI入力 )
				{
					CInputMIDI^ midi = (CInputMIDI^) device;
					::midiInStop(  (HMIDIIN) midi->hMidiIn );
					::midiInReset( (HMIDIIN) midi->hMidiIn );
					::midiInClose( (HMIDIIN) midi->hMidiIn );
				}

				// 解放
				delete device;
			}

			this->_list入力デバイス->Clear();
			SAFE_REF_DELETE( this->_list入力デバイス );
		}

		if( this->gchMidiInProc.IsAllocated )
			this->gchMidiInProc.Free();

		SAFE_REF_DELETE( this->timer );
		SAFE_REF_DELETE( this->dgMidiInProc );
		//SAFE_REF_DELETE( this->_Keyboard );	// _list入力デバイスに含まれているので解放済み。
		//SAFE_REF_DELETE( this->_Mouse );		//                   〃
		this->_hWnd = IntPtr::Zero;

		// ファイナライザを呼び出して Unmanaged リソースを解放したのち、ファイナライザの呼び出しを抑制する。
		this->!CInput管理();

		//GC::SuppressFinalize( this );   --> コンパイラが勝手にやる
	}
	CInput管理::!CInput管理()
	{
		// Unmanaged リソースを解放する。ディスポーザが呼び出されていればファイナライザは呼び出されないようコンパイラが調整する。
		SAFE_COM_RELEASE( this->pDI );
	}

	void CInput管理::tポーリング( bool bWindowがアクティブ中, bool bバッファ入力を使用する )
	{
		this->Disposed->tDispose済みまたは実行中なら例外発生( "CInput管理" );

		// 排他制御は各 tポーリング() で行う。（実際必要なのはMIDI入力だけ）

		for( int i = this->_list入力デバイス->Count - 1; i >= 0; i-- )
		{
			IInputDevice^ device = this->_list入力デバイス[ i ];
			try
			{
				device->tポーリング( bWindowがアクティブ中, bバッファ入力を使用する );
			}
			catch( Exception^ )
			{
				// ポーリングに失敗したら無視する。（削除するには該当デバイス関連スレッドとの排他制御が必要なので面倒）

				//delete device;
				//this->list入力デバイス->RemoveAt( i );
			}
		}
	}

	IInputDevice^ CInput管理::MidiIn( int ID )
	{
		this->Disposed->tDispose済みまたは実行中なら例外発生( "CInput管理" );

		for each( IInputDevice^ device in this->_list入力デバイス )
		{
			if( ( device->e入力デバイス種別 == E入力デバイス種別::MIDI入力 ) && ( device->ID == ID ) )
				return device;
		}
		return nullptr;
	}

// protected:

	void CInput管理::midiInProc( HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
	{
		if( this->Disposed->bDispose済みまたは処理中である )
			return;		// 相手は Win32 なので例外は発出しない。


		// 排他制御は CInputMIDI で行う。（実際必要なのはMIDI入力だけ）

		int nStatus = dwParam1 & 0xF0;
		Int64 time = this->timer->nシステム時刻ms;	// lock前に取得

		if( ( nullptr != this->_list入力デバイス ) && ( 0 != this->_list入力デバイス->Count ) )
		{
			for each( IInputDevice^ device in this->_list入力デバイス )
			{
				if( device->e入力デバイス種別 != E入力デバイス種別::MIDI入力 )
					continue;

				CInputMIDI^ tmidi = (CInputMIDI^) device;
				if( ((HMIDIIN)tmidi->hMidiIn) == hMidiIn )
				{
					tmidi->tメッセージからMIDI信号のみ受信( wMsg, dwInstance, dwParam1, dwParam2, time );
					break;
				}
			}
		}
	}
}
