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

namespace FDK
{
	CInputMouse::CInputMouse( IntPtr hWnd, IDirectInput8 *pDI )
	{
		HRESULT hr = S_OK;

		this->Disposed = gcnew CDisposed();
		this->pDevice = NULL;
		this->_e入力デバイス種別 = E入力デバイス種別::マウス;
		this->_guid = nullptr;
		this->_ID = 0;
		this->_strデバイス名 = "";
		this->_lock入力イベント = gcnew Object();

		try
		{
			// 配列確保。

			this->bKeyPullUp = gcnew array<bool>( CInputMouse::nマウスの最大ボタン数 );
			this->bKeyPushDown = gcnew array<bool>( CInputMouse::nマウスの最大ボタン数 );
			this->bKeyState = gcnew array<bool>( CInputMouse::nマウスの最大ボタン数 );


			// ボタン状態配列を初期化。

			for( int i = 0; i < this->bKeyState->Length; i++ )
				this->bKeyState[ i ] = false;


			// マウスデバイスを生成する。

			#pragma region [ *** ]
			//-----------------
			IDirectInputDevice8 *pdev;
			tFAILEDなら例外発生( L"マウスデバイスの生成", 
				hr = pDI->CreateDevice( GUID_SysMouse, &pdev, NULL ) );
			this->pDevice = pdev;
					
			tFAILEDなら例外発生( L"マウスデバイスの協調レベルの設定",
				hr = this->pDevice->SetCooperativeLevel( (HWND)(void*)hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE ) );
					
			tFAILEDなら例外発生( L"マウスデバイスのフォーマットの設定",
				hr = this->pDevice->SetDataFormat( &c_dfDIMouse2 ) );

			DIPROPDWORD dipwd;
			dipwd.diph.dwSize = sizeof(DIPROPDWORD);
			dipwd.diph.dwHeaderSize = sizeof(DIPROPHEADER);
			dipwd.diph.dwObj = 0;
			dipwd.diph.dwHow = DIPH_DEVICE;
			dipwd.dwData = 32;

			tFAILEDなら例外発生( L"マウスデバイスのバッファサイズの設定",
				hr = this->pDevice->SetProperty( DIPROP_BUFFERSIZE, &dipwd.diph ) );
			//-----------------
			#pragma endregion


			// タイマーを生成する。

			this->timer = gcnew CTimer( CTimer::E種別::MultiMedia );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			SAFE_REF_DELETE( this->bKeyPullUp );
			SAFE_REF_DELETE( this->bKeyPushDown );
			SAFE_REF_DELETE( this->bKeyState );
			SAFE_REF_DELETE( this->timer );
			SAFE_COM_RELEASE( this->pDevice );

			throw gcnew Exception( L"マウスデバイスの生成に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	CInputMouse::~CInputMouse()
	{
		if( this->Disposed->bDispose済みまたは処理中である )
			return;		// 例外は発出しない。

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


		SAFE_REF_DELETE( this->bKeyPullUp );
		SAFE_REF_DELETE( this->bKeyPushDown );
		SAFE_REF_DELETE( this->bKeyState );
		SAFE_REF_DELETE( this->timer );
		SAFE_REF_DELETE( this->_guid );				// bDisposed チェックを行うメンバを呼び出さないように注意。
		SAFE_REF_DELETE( this->_strデバイス名 );	//

		if( nullptr != this->_list入力イベント )
		{
			for each( CInputEvent^ ev in this->_list入力イベント )
				delete ev;	// Dispsoe

			this->_list入力イベント->Clear();
			SAFE_REF_DELETE( this->_list入力イベント );
		}
		SAFE_REF_DELETE( this->_lock入力イベント );

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

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

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

		HRESULT hr = S_OK;

		lock l( this->_lock入力イベント );


		// 状態バッファをクリア。

		for( int i = 0; i < CInputMouse::nマウスの最大ボタン数; i++ )
		{
			this->bKeyPushDown[ i ] = false;
			this->bKeyPullUp[ i ] = false;
		}


		// 取得＆ポーリング＆入力イベントリストを作成。

		if( bWindowがアクティブ中 && 
			this->pDevice != NULL &&
			SUCCEEDED( this->pDevice->Acquire() ) && 
			SUCCEEDED( this->pDevice->Poll() ) )
		{
			this->_list入力イベント = gcnew List<CInputEvent^>( 32 );

			if( bバッファ入力を使用する )
			{
				#pragma region [ (A) バッファ入力 ]
				//-----------------------------
				DIDEVICEOBJECTDATA bufferedData[ 32 ];
				DWORD dwItems = 32;
				hr = this->pDevice->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), bufferedData, &dwItems, 0 );
				if( FAILED( hr ) || dwItems == 0 )
					return;

				for( DWORD i = 0; i < dwItems; i++ )
				{
					if( bufferedData[ i ].dwData > 0 )
					{
						#pragma region [ a. 押された ]
						//-----------------
						int nButton = bufferedData[ i ].dwOfs - DIMOFS_BUTTON0;
						if( nButton < 0 || nButton > CInputMouse::nマウスの最大ボタン数 )
							continue;

						CInputEvent^ ev = gcnew CInputEvent();
						ev->guid = this->guid;
						ev->ID = this->ID;
						ev->nKey = nButton;
						ev->b押された = true;
						ev->nTimeStamp = bufferedData[ i ].dwTimeStamp;
						ev->nVelocity = CInput管理::n通常音量;
						this->_list入力イベント->Add( ev );

						this->bKeyState[ nButton ] = true;
						this->bKeyPushDown[ nButton ] = true;
						//-----------------
						#pragma endregion
					}
					else
					{
						#pragma region [ b. 離された ]
						//-----------------
						int nButton = bufferedData[ i ].dwOfs - DIMOFS_BUTTON0;
						if( nButton < 0 || nButton > CInputMouse::nマウスの最大ボタン数 )
							continue;

						CInputEvent^ ev = gcnew CInputEvent();
						ev->guid = this->guid;
						ev->ID = this->ID;
						ev->nKey = nButton;
						ev->b押された = false;
						ev->nTimeStamp = bufferedData[ i ].dwTimeStamp;
						ev->nVelocity = 0;
						this->_list入力イベント->Add( ev );
							
						this->bKeyState[ nButton ] = false;
						this->bKeyPullUp[ nButton ] = true;
						//-----------------
						#pragma endregion
					}
				}
				//-----------------------------
				#pragma endregion
			}
			else
			{
				#pragma region [ (B) 状態入力 ]
				//-----------------
				DIMOUSESTATE2 state;
				hr = this->pDevice->GetDeviceState( sizeof(DIMOUSESTATE2), &state );
				if( FAILED( hr ) )
					return;

				for( int i = 0; i < CInputMouse::nマウスの最大ボタン数; i++ )
				{
					if( state.rgbButtons[ i ] != 0 )
					{
						if( this->bKeyState[ i ] == false )
						{
							#pragma region [ a. 押されている ＆ 前回は離されていた ]
							//-----------------
							CInputEvent^ ev = gcnew CInputEvent();
							ev->guid = this->guid;
							ev->ID = this->ID;
							ev->nKey = i;
							ev->b押された = true;
							ev->nTimeStamp = this->timer->nシステム時刻ms;
							ev->nVelocity = CInput管理::n通常音量;
							this->list入力イベント->Add( ev );
									
							this->bKeyState[ i ] = true;
							this->bKeyPushDown[ i ] = true;
							//-----------------
							#pragma endregion
						}
					}
					else
					{
						if( this->bKeyState[ i ] == true )
						{
							#pragma region [ b. 離されている ＆ 前回は押されていた ]
							//-----------------
							CInputEvent^ ev = gcnew CInputEvent();
							ev->guid = this->guid;
							ev->ID = this->ID;
							ev->nKey = i;
							ev->b押された = false;
							ev->nTimeStamp = this->timer->nシステム時刻ms;
							ev->nVelocity = 0;
							this->list入力イベント->Add( ev );
					
							this->bKeyState[ i ] = false;
							this->bKeyPullUp[ i ] = true;
							//-----------------
							#pragma endregion
						}
					}
				}
				//-----------------
				#pragma endregion
			}
		}
	}
}