/**
 * @file
 * @brief 入力（キーボード・マウス・ジョイスティック）の実装にかかわるクラス、定数定義などを行う.
 *
 * @author S.F.
 * @version $Id:
 *
 * Copyright (C) 2000-2002 Satoshi Fujiwara. All Rights Reserved.
 */

/// 二重インクルード防止マクロ
#if !defined(AFX_INPUTInputImpl_H__CAF65806_66F9_4CFF_89EC_66E36A0FB746__INCLUDED_)
#define AFX_INPUTInputImpl_H__CAF65806_66F9_4CFF_89EC_66E36A0FB746__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "sf.h"
#include "Input.h"
extern "C" {
namespace sf {
	namespace system {
		namespace input {

/** Inputインターフェースの実装.
 *  初期化部分はＳＤＫのサンプルを参考にしています。
 *  というかほとんどそのまんまですが．．．。*/
class InputImpl : public Input  
{
	// LIFECYCLE //
public:
	/// マウスリスナーリスト配列型定義
	typedef std::vector<input::mouse::Listener* > MouseListenerList;	
	/// キーボードリスナーリスト配列型定義
	typedef std::vector<input::keyboard::Listener*  > KeyboardListenerList;	
	/// コンストラクタ
	InputImpl();

	/** コンストラクタ
	 * @param hwnd ウィンドウハンドル */
	InputImpl(HWND hwnd);
	
	/// デストラクタ
	virtual ~InputImpl();
	// OPERATORS //
	// OPERATIONS //
	void update(void);
	
	/// DirectInputからのコールバック 
	static 	BOOL CALLBACK 
		enumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext )
	{
		return ((InputImpl *)pContext)->enumJoysticksCallback(pdidInstance);
	};
	
	/// DirectInputからのコールバック
	static BOOL CALLBACK enumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
                                VOID* pContext )
	{ return ((InputImpl *)pContext)->enumAxesCallback(pdidoi);};
	
	/** 左マウスボタンが押された.
	 * SystemImpl::wndFunc()よりWM_LBUTTONDOWNを受け取り、それをmouse::Listenerたちに通知します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT		onLButtonDown(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);
	
	/** 左マウスボタンがダブルクリックされた.
	 * SystemImpl::wndFunc()よりWM_LBUTTONDBLCLKを受け取り、mouse::Listenerたちに通知します。
 	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT     onLButtonDoubleClick(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);
	
	/** 左マウスボタンが離された.
	 * SystemImpl::wndFunc()よりWM_LBUTTONUPを受け取り、mouse::Listenerたちに通知します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT		onLButtonUp(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);

	/** 右マウスボタンがダブルクリックされた.
	 * SystemImpl::wndFunc()よりWM_RBUTTONDBLCLKを受け取り、mouse::Listenerたちに通知します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT     onRButtonDoubleClick(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);
	
	/** 右マウスボタンが押された.
	 * SystemImpl::wndFunc()よりWM_RBUTTONDOWNを受け取り、mouse::Listenerたちに通知します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT		onRButtonDown(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);
	
	/** 右マウスボタンが離された.
	 * SystemImpl::wndFunc()よりWM_RBUTTONUPを受け取り、mouse::Listenerたちに通知します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT		onRButtonUp(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);
	
	/** マウスカーソルが動いた.
	 * SystemImpl::wndFunc()よりWM_MOUSEMOVEを受け取り、mouse::Listenerたちに通知します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT		onMouseMove(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);

	/** マウスホイールが変化した.
	 * SystemImpl::wndFunc()よりWM_MOUSEWHEELを受け取り、mouse::Listenerたちに通知します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT		onMouseWheel(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);

	/** WM_ONSETCURSORを受け取る.
	 * SystemImpl::wndFunc()よりWM_ONSETCURSORを受け取ります。
	 * このシステムでは、マウスカーソルを消去・表示のコントロールのため、このメッセージを受け取っています。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM 
	 * @note WM_SETCURSOR イベントは、マウス入力がキャプチャされず、ウィンドウ内でカーソルを移動するときに発生するメッセージです。*/
	LRESULT		onSetCursor(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);

	/** マウスリスナーのメソッドを呼び出す.
	 * ウィンドウメッセージを受け取ってmouse::Listenerの各メソッドを呼び出す作業を簡略化します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM 
	 * @param pMethod mouse::Listenerのメソッドのポインタ　*/
	LRESULT		callMouseListenerMethod(const HWND hwnd, const WPARAM wParam, const LPARAM lParam,void(input::mouse::Listener::*pMethod)(const int,const int,const DWORD));
	
	// キーボードメッセージハンドラ
	/** キーボードが押された.
	 * system::SystemImpl::wndFunc()よりWM_KEYDOWNを受け取り、keyboard::Listenerたちに通知します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT		onKeyDown(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);
	
	/** キーボードが離された.
	 * system::SystemImpl::wndFunc()よりWM_KEYUPを受け取り、keyboard::Listenerたちに通知します。
	 * @param hwnd ウィンドウハンドル 
	 * @param wParam WPARAM 
	 * @param lParam LPARAM */
	LRESULT		onKeyUp(const HWND hwnd, const WPARAM wParam, const LPARAM lParam);

	/** ウィンドウメッセージマップへ登録を行う. 
	 * ルーティングしたいウィンドウメッセージを登録します。*/
	void		addWindowMessageMap();

	/** MouseListenerの追加を行う. 
	 * mouse::ListenerをInputImpl::mMouseListenerQに追加します。
	 * @param pListener mouse::Listenerのポインタ */
	void addMouseListener(input::mouse::Listener * const pListener){
			mMouseListenerQ.push_back(pListener);				
	};
	

	/** MouseListenerの削除を行う 
	 * @param pListener mouse::Listenerのポインタ*/
	void removeMouseListener(input::mouse::Listener * const pListener){
		MouseListenerList::iterator it;
		for(it = mMouseListenerList.begin();
			it != mMouseListenerList.end();it++){
			if(pListener == *it){
//				mMouseListenerList.erase(it);
				*it = NULL;
				break;
			}
		}

		for(it = mMouseListenerQ.begin();
			it != mMouseListenerQ.end();it++){
			if(pListener == *it){
				*it = NULL;
				return;
			}
		}
	};

	/** KeyboardListenerの追加を行う 
	 * @param pListener keyboard::Listener のポインタ */
	void addKeyboardListener(input::keyboard::Listener * const pListener){
			mKeyboardListenerQ.push_back(pListener);				
	};
	

	/** KeyboardListenerの削除を行う
	 * @param pListener keyboard::Listenerのポインタ */
	void removeKeyboardListener(input::keyboard::Listener * const pListener){
		KeyboardListenerList::iterator it;
		for(it = mKeyboardListenerList.begin();
			it != mKeyboardListenerList.end();it++){
			if(pListener == *it){
//				mKeyboardListenerList.erase(it);
				*it = NULL;
				break;
			}
		}

		for(it = mKeyboardListenerQ.begin();
			it != mKeyboardListenerQ.end();it++){
			if(pListener == *it){
				*it = NULL;
				return;
			}
		}
	};

	// ACCESS //
	const bool left(const bool bTrig = false) const 
		{ if(!bTrig) return mbLeft; else return ((mbLeft ^ mbBeforeLeft) & mbLeft) ;};
	const bool right(const bool bTrig = false) const 
		{ if(!bTrig) return mbRight; else return ((mbRight ^ mbBeforeRight) & mbRight) ;};
	const bool up(const bool bTrig = false) const
		{ if(!bTrig) return mbUp; else return ((mbUp ^ mbBeforeUp) & mbUp) ;};
	const bool down(const bool bTrig = false) const
		{ if(!bTrig) return mbDown; else return ((mbDown ^ mbBeforeDown) & mbDown) ;};
	const bool buttonA(const bool bTrig = false) const
		{ if(!bTrig) return mbButtonA; else return ((mbButtonA ^ mbBeforeButtonA) & mbButtonA) ;};
	const bool buttonB(const bool bTrig = false) const
		{ if(!bTrig) return mbButtonB; else return ((mbButtonB ^ mbBeforeButtonB) & mbButtonB) ;};
	const bool buttonC(const bool bTrig = false) const
		{ if(!bTrig) return mbButtonC; else return ((mbButtonC ^ mbBeforeButtonC) & mbButtonC) ;};
	const bool buttonD(const bool bTrig = false) const 
		{ if(!bTrig) return mbButtonD; else return ((mbButtonD ^ mbBeforeButtonD) & mbButtonD) ;};
	const bool start(const bool bTrig = false) const
		{ if(!bTrig) return mbStart; else return ((mbStart ^ mbBeforeStart) & mbStart) ;};
	const bool exit(const bool bTrig = false) const 
		{ if(!bTrig) return mbExit; else return ((mbExit ^ mbBeforeExit) & mbExit) ;};
	
	void showMouseCursor(void){mbMouseCursor = true;};
	void hideMouseCursor(void){mbMouseCursor = false;};
	
	const bool mouseCursorVisibility  (void) const{return mbMouseCursor;};

	// INQUIRY //
	const bool isEnabled(void) const {return mbEnabled;};

	void initialize(void);
private:
	/** 終了時の処理を行う.
	 * 終了時に開放しなくてはいけないものはここで開放します。*/
	void uninitialize(void);
	/** ジョイスティックの初期化を行う
	 * @exception RecoverbleErrorException ジョイスティックの初期化に失敗した場合 */
	void initJoystick(void);
	/** キーボードの初期化を行う 
	 * @exception RecoverbleErrorException キーボードの初期化に失敗した場合 */
	void initKeyboard(void);

	/// DirectInputからのコールバックを処理する 
	BOOL enumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance);
	/// DirectInputからのコールバックを処理する 
	BOOL enumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi);
	
	
	bool mbEnabled;///< Soundインターフェースが現在使用可かどうか
	bool mbMouseCursor;///< マウスカーソルが表示されているかいないか
	
	LPDIRECTINPUT8		 mpDI;///< DirectInput8 インターフェース		   
	
	LPDIRECTINPUTDEVICE8 mpJoystick;///< ジョイスティックインターフェースのポインタ
	LPDIRECTINPUTDEVICE8 mpKeyboard;///< キーボードデバイスのポインタ
	
	DIDEVCAPS				mDIJoystickDevCaps;///< デバイス能力
	DIJOYSTATE2				mDIJoyState;///< ジョイスティックの状態
	BYTE    mDIKeys[256];   ///< DirectInput keyboard state buffer 

	HWND					mHWND;///< ウィンドウハンドル

	// マウスイベントコールバック用
	MouseListenerList mMouseListenerList;///< mouse::Listenerの配列
	MouseListenerList mMouseListenerQ;///< mouse::Listenerの配列(一時保持用)

	KeyboardListenerList mKeyboardListenerList;///< keyboard::Listenerの配列
	KeyboardListenerList mKeyboardListenerQ;///< keyboard::Listenerの配列(一時保持用)

	bool mbUp;///< 上ボタン
	bool mbDown;///< 下ボタン
	bool mbLeft;///< 左ボタン
	bool mbRight;///< 右ボタン
	
	bool mbButtonA;///< ボタンA
	bool mbButtonB;///< ボタンB
	bool mbButtonC;///< ボタンC
	bool mbButtonD;///< ボタンD
	
	bool mbStart;///< STARTボタン
	bool mbExit;///< EXITボタン

	bool mbBeforeUp;///< 上ボタンの1フレーム前の状態
	bool mbBeforeDown;///< 下ボタンの1フレーム前の状態
	bool mbBeforeLeft;///< 左ボタンの1フレーム前の状態
	bool mbBeforeRight;///< 右ボタンの1フレーム前の状態
	
	bool mbBeforeButtonA;///< ボタンAの1フレーム前の状態
	bool mbBeforeButtonB;///< ボタンBの1フレーム前の状態
	bool mbBeforeButtonC;///< ボタンCの1フレーム前の状態
	bool mbBeforeButtonD;///< ボタンDの1フレーム前の状態

	bool mbBeforeStart;///< STARTボタンの1フレーム前の状態
	bool mbBeforeExit;///< EXITボタンの1フレーム前の状態
};
		} // namespace input
	} // namespace system
} // namespace sf
}
#endif // !defined(AFX_INPUTInputImpl_H__CAF65806_66F9_4CFF_89EC_66E36A0FB746__INCLUDED_)
