//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * ジョイスティックヘッダ
 * @author Junpee
 */

#ifndef JOYSTICK_H_
#define JOYSTICK_H_

#include <Input/Joystick/JoystickAxis.h>
#include <Input/Joystick/JoystickState.h>
#include <Input/Joystick/JoystickDevice.h>

namespace Lamp{

//------------------------------------------------------------------------------
/**
 * ジョイスティック
 */
class Joystick : public JoystickAxis{
friend class LampInput;
friend class BufferedInput;
public:
	//--------------------------------------------------------------------------
	// 定数
	//--------------------------------------------------------------------------
	/// 最大視点コントローラ数
	static const int maxPOVCount = JoystickState::maxPOVCount;

	/// 最大スライダ数
	static const int maxSliderCount = JoystickState::maxSliderCount;

	/// 最大ボタン数
	static const int maxButtonCount = JoystickState::maxButtonCount;

	/// 軸データ最大値
	static const int maxAxisValue = JoystickState::maxAxisValue;

	/// 軸データ最小値
	static const int minAxisValue = JoystickState::minAxisValue;

	/// 視点コントローラ最大値
	static const int maxPOVValue = JoystickState::maxPOVValue;

	//--------------------------------------------------------------------------
	// 軸
	//--------------------------------------------------------------------------
	/**
	 * 軸数の取得
	 * @return 軸数
	 */
	virtual int getAxisCount() const{ return device_->getAxisCount(); }

	/**
	 * スライダ以外の軸数取得
	 * @return スライダ以外の軸数
	 */
	virtual int getAxisCountWithoutSlider() const{
		return (device_->getAxisCount() - device_->getSliderCount());
	}

	//--------------------------------------------------------------------------
	/**
	 * 軸を持つか
	 * @param axis 軸
	 * @return 軸を持っていればture
	 */
	virtual bool hasAxis(Axis axis) const{
		if(axis == axisX){ return hasXAxis(); }
		if(axis == axisY){ return hasYAxis(); }
		if(axis == axisZ){ return hasZAxis(); }
		if(axis == axisRotationX){ return hasXRotation(); }
		if(axis == axisRotationY){ return hasYRotation(); }
		if(axis == axisRotationZ){ return hasZRotation(); }
		if(axis == axisSlider0){ return (getSliderCount() >= 1); }
		if(axis == axisSlider1){ return (getSliderCount() >= 2); }
		Assert(false);
		return false;
	}

	/**
	 * 軸の値取得
	 * @return 軸の値
	 */
	virtual float getAxis(Axis axis) const{
		if(axis == axisX){ return getXAxis(); }
		if(axis == axisY){ return getYAxis(); }
		if(axis == axisZ){ return getZAxis(); }
		if(axis == axisRotationX){ return getXRotation(); }
		if(axis == axisRotationY){ return getYRotation(); }
		if(axis == axisRotationZ){ return getZRotation(); }
		if(axis == axisSlider0){ return getSlider(0); }
		if(axis == axisSlider1){ return getSlider(1); }
		Assert(false);
		return 0.f;
	}

	//--------------------------------------------------------------------------
	/**
	 * X軸を持つか
	 * @return X軸を持てばtrue
	 */
	virtual bool hasXAxis() const{ return device_->hasXAxis(); }

	/**
	 * X軸の取得
	 * @return 1から-1の値をとるX軸
	 */
	virtual float getXAxis() const{
		return correctAxisValue(state_.getXAxis());
	}

	/**
	 * 前回のX軸の取得
	 * @return 1から-1の値をとるX軸
	 */
	virtual float getPreXAxis() const{
		return correctAxisValue(preState_.getXAxis());
	}

	//--------------------------------------------------------------------------
	/**
	 * Y軸を持つか
	 * @return Y軸を持てばtrue
	 */
	virtual bool hasYAxis() const{ return device_->hasYAxis(); }

	/**
	 * Y軸の取得
	 * @return 1から-1の値をとるY軸
	 */
	virtual float getYAxis() const{
		return correctAxisValue(state_.getYAxis());
	}

	/**
	 * 前回のY軸の取得
	 * @return 1から-1の値をとるY軸
	 */
	virtual float getPreYAxis() const{
		return correctAxisValue(preState_.getYAxis());
	}

	//--------------------------------------------------------------------------
	/**
	 * Z軸を持つか
	 * @return Z軸を持てばtrue
	 */
	virtual bool hasZAxis() const{ return device_->hasZAxis(); }

	/**
	 * Z軸の取得
	 * @return 1から-1の値をとるZ軸
	 */
	virtual float getZAxis() const{
		return correctAxisValue(state_.getZAxis());
	}

	/**
	 * 前回のZ軸の取得
	 * @return 1から-1の値をとるZ軸
	 */
	virtual float getPreZAxis() const{
		return correctAxisValue(preState_.getZAxis());
	}

	//--------------------------------------------------------------------------
	/**
	 * X回転を持つか
	 * @return X回転を持てばtrue
	 */
	virtual bool hasXRotation() const{ return device_->hasXRotation(); }

	/**
	 * X回転の取得
	 * @return 1から-1の値をとるX回転
	 */
	virtual float getXRotation() const{
		return correctAxisValue(state_.getXRotation());
	}

	/**
	 * 前回のX回転の取得
	 * @return 1から-1の値をとるX回転
	 */
	virtual float getPreXRotation() const{
		return correctAxisValue(preState_.getXRotation());
	}

	//--------------------------------------------------------------------------
	/**
	 * Y回転を持つか
	 * @return Y回転を持てばtrue
	 */
	virtual bool hasYRotation() const{ return device_->hasYRotation(); }

	/**
	 * Y回転の取得
	 * @return 1から-1の値をとるY回転
	 */
	virtual float getYRotation() const{
		return correctAxisValue(state_.getYRotation());
	}

	/**
	 * 前回のY回転の取得
	 * @return 1から-1の値をとるY回転
	 */
	virtual float getPreYRotation() const{
		return correctAxisValue(preState_.getYRotation());
	}

	//--------------------------------------------------------------------------
	/**
	 * Z回転を持つか
	 * @return Z回転を持てばtrue
	 */
	virtual bool hasZRotation() const{ return device_->hasZRotation(); }

	/**
	 * Z回転の取得
	 * @return 1から-1の値をとるZ回転
	 */
	virtual float getZRotation() const{
		return correctAxisValue(state_.getZRotation());
	}

	/**
	 * 前回のZ回転の取得
	 * @return 1から-1の値をとるZ回転
	 */
	virtual float getPreZRotation() const{
		return correctAxisValue(preState_.getZRotation());
	}

	//--------------------------------------------------------------------------
	// 視点コントローラ
	//--------------------------------------------------------------------------
	/**
	 * 視点コントローラ数の取得
	 * @return 視点コントローラ数
	 */
	virtual int getPOVCount() const{ return device_->getPOVCount(); }

	/**
	 * 視点コントローラ値の取得
	 * @param id 視点コントローラID
	 * @return 視点コントローラ値
	 */
	virtual int getPOV(int id) const{ return state_.getPOV(id); }

	/**
	 * 前回の視点コントローラ値の取得
	 * @param id 視点コントローラID
	 * @return 視点コントローラ値
	 */
	virtual int getPrePOV(int id) const{ return preState_.getPOV(id); }

	/**
	 * 視点コントローラが中心か
	 * @param id 視点コントローラID
	 * @return 視点コントローラが中心ならtrue
	 */
	virtual bool isPOVCenter(int id) const{
		return ((state_.getPOV(id) & 0xffff) == 0xffff);
	}

	//--------------------------------------------------------------------------
	// スライダー
	//--------------------------------------------------------------------------
	/**
	 * スライダ数の取得
	 * @return スライダ数
	 */
	virtual int getSliderCount() const{ return device_->getSliderCount(); }

	/**
	 * スライダ値の取得
	 * @param id スライダID
	 * @return 1から-1の値をとるスライダ値
	 */
	virtual float getSlider(int id) const{
		return correctSliderValue(state_.getSlider(id));
	}

	//--------------------------------------------------------------------------
	// ボタン
	//--------------------------------------------------------------------------
	/**
	 * ボタン数の取得
	 * @return ボタン数
	 */
	virtual int getButtonCount() const{ return device_->getButtonCount(); }

	/**
	 * ボタンが押されているか
	 * @param id 対象ボタンID
	 * @return ボタンが押されていればtrue
	 */
	virtual bool buttonPressed(int id) const{
		return state_.buttonPressed(id);
	}

	/**
	 * ボタンが下がった
	 * @param id ボタンID
	 * @return ボタンが下がったならばtrue
	 */
	virtual bool buttonDown(int id) const{
		return (state_.buttonPressed(id) && (!preState_.buttonPressed(id)));
	}

	/**
	 * ボタンが上がった
	 * @param id ボタンID
	 * @return ボタンが上がったならばtrue
	 */
	virtual bool buttonUp(int id) const{
		return ((!state_.buttonPressed(id)) && preState_.buttonPressed(id));
	}

	//--------------------------------------------------------------------------
	/**
	 * 名前の取得
	 * @return 名前
	 */
	virtual String getName() const{ return device_->getProductName(); }

	/**
	 * アタッチされているか
	 * @return アタッチされていればtrue
	 */
	virtual bool isAttached() const{ return device_->isAttached(); }

	/**
	 * ポーリングが必要か
	 * @return ポーリングが必要ならtrue
	 */
	virtual bool isPolled() const{ return device_->isPolled(); }

	/**
	 * 文字列への変換
	 * @return 文字列
	 */
	virtual String toString() const;

	/**
	 * クリア
	 */
	virtual void clear(){
		state_.clear();
		preState_.clear();
	}

	/**
	 * 協調レベルの設定
	 * @param exclusive 排他モードならtrue
	 * @param foreground フォアグラウンドモードならtrue
	 * @return 成功すればtrue
	 */
	virtual bool setCooperativeLevel(bool exclusive, bool foreground){
		return device_->setCooperativeLevel(exclusive, foreground);
	}

	/**
	 * 排他モードか
	 * @return 排他モードならtrue
	 */
	virtual bool isExclusive() const{ return device_->isExclusive(); }

	/**
	 * フォアグラウンドモードか
	 * @return フォアグラウンドモードならtrue、バックグラウンドモードならfalse
	 */
	virtual bool isForeground() const{ return device_->isForeground(); }

protected:
	//--------------------------------------------------------------------------
	/**
	 * コンストラクタ
	 * @param device デバイス
	 */
	Joystick(JoystickDevice* device);

	/**
	 * デストラクタ
	 */
	virtual ~Joystick();

	/**
	 * 次のステート設定
	 * @param state 次のステート
	 */
	virtual void setNextState(const JoystickState& state);

	/**
	 * ステートの取得
	 * @return ステート
	 */
	virtual const JoystickState& getState(){ return state_; }

	/**
	 * 軸補正
	 * @param axisValue 補正する値
	 * @return 補正された値
	 */
	virtual float correctAxisValue(int axisValue) const{
		return (float)axisValue / (float)maxAxisValue;
	}

	/**
	 * スライダ補正
	 * @param sliderValue 補正する値
	 * @return 補正された値
	 */
	virtual float correctSliderValue(int sliderValue) const{
		return (float)sliderValue / (float)maxAxisValue;
	}

private:
	//--------------------------------------------------------------------------
	// デバイス
	JoystickDevice* device_;
	// ステート
	JoystickState state_;
	// 前回のステート
	JoystickState preState_;

};

//------------------------------------------------------------------------------
} // End of namespace Lamp
#endif // End of JOYSTICK_H_
//------------------------------------------------------------------------------
