module yamalib.input.inputcommand;

private import std.regexp;

private import y4d_input.mouse;
private import y4d_input.mouse;
private import ytl.vector;

/**
	マウス入力によるコマンド判定を抽象化し、
	コマンド変更に柔軟に対応するためのクラス 
*/
class InputCommand {
public:

	/** 左ボタンのダブルクリック */
	static final const uint C_CMD_L_D_CLICK 	= 0x00000001;
	/** 左ボタン押下 */
	static final const uint C_CMD_L_DOWN 		= 0x00000002;
	/** 左ボタン押し続け */
	static final const uint C_CMD_L_DOWN_KEPP 	= 0x00000004;
	/** 左ボタン押し上げ */
	static final const uint C_CMD_L_UP 			= 0x00000008;
	/** 左ボタン押し上げ */
	static final const uint C_CMD_L_NOTHING 	= 0x00000010;
	
	/** 右ボタンのダブルクリック */
	static final const uint C_CMD_R_D_CLICK 	= 0x00000020;
	/** 右ボタン押下 */
	static final const uint C_CMD_R_DOWN 		= 0x00000040;
	/** 右ボタン押し続け */
	static final const uint C_CMD_R_DOWN_KEPP 	= 0x00000080;
	/** 右ボタン押し上げ */
	static final const uint C_CMD_R_UP 			= 0x00000100;
	/** 右ボタン押し上げ */
	static final const uint C_CMD_R_NOTHING 	= 0x00000200;
	
	/** 中ボタンのダブルクリック */
	static final const uint C_CMD_M_D_CLICK 	= 0x00000400;
	/** 中ボタン押下 */
	static final const uint C_CMD_M_DOWN 		= 0x00000800;
	/** 中ボタン押し続け */
	static final const uint C_CMD_M_DOWN_KEPP 	= 0x00001000;
	/** 中ボタン押し上げ */
	static final const uint C_CMD_M_UP 			= 0x00002000;
	/** 中ボタン押し上げ */
	static final const uint C_CMD_M_NOTHING 	= 0x00004000;
	
	/// マウスを設定する
	void setMouse(MouseInput mouse_) {
		m_mouse = mouse_;
	}
	
	/// マウスを取得する
	MouseInput getMouse() {
		return m_mouse;
	}
	
	/// 状態更新
	void update() {
		m_mouse.update();
		vCmdStream[m_bufferPos++] = getCommand();
		
		// 円環バッファリング
		if (m_bufferPos >= LC_CMD_STREAM_BUFFER_SIZE) {
			m_bufferPos = 0;
		}
	}
	
	/// コマンドをセットし、そのコマンドのＩＤとなる番号を返却する
	int addCommand(char[] cmd) {
		vCommands.push_back(cmd);
		return vCommands.size();
	}
	
	/// コマンドをクリアする
	void clearCommand() {
		vCommands.clear();
	}

	/// 登録したコマンド番号に対して、コマンドが実行されたか判定を行う
	bool isInput(int nCmdNo) {
		if ( vCommands.size() >= nCmdNo )  return false;
		
		// 対象コマンド取得
		char[] cmdPattern = vCommands[ nCmdNo ];
		char[] cmdInputString;

		/* 実際に入力されているコマンド文字列 */
		cmdInputString = getInputCmdString();
		
		return cast(bool) (std.regexp.find(cmdInputString, cmdPattern) != -1);
	}

	/// コンストラクタ
	this() {
		vCommands = new vector!(char[]);
	}

private:

	/// 現在の状態からコマンドを取得する
	uint getCommand() {
		uint cmdR = 0x0;
		uint cmdL = 0x0;
		uint cmdM = 0x0;
		
		// ダブルクリック
		if ( m_mouse.isLButtonDClick() ) {
			cmdL |= C_CMD_L_D_CLICK;
		}
		
		// 押している
		if ( m_mouse.isPress(MouseInput.button.left) ) {
			cmdL |= C_CMD_L_DOWN;
		}

		// 押し上げられた
		if ( m_mouse.isLButtonUp() ) {
			cmdL |= C_CMD_L_UP;
		}
		
		// 押し続けられている
		if ( m_mouse.getKeepingTimeL() > 0 ) {
			cmdL |= C_CMD_L_DOWN_KEPP;
		}
		
		// なにもされていないならそのフラグを立てておく
		if ( 0x0 == cmdL ) {
			cmdL = C_CMD_L_NOTHING;
		}
		
		/*----- 右 -----*/
		
		// ダブルクリック
		if ( m_mouse.isRButtonDClick() ) {
			cmdR |= C_CMD_R_D_CLICK;
		}
		
		// 押している
		if ( m_mouse.isPress(MouseInput.button.right) ) {
			cmdR |= C_CMD_R_DOWN;
		}

		// 押し上げられた
		if ( m_mouse.isRButtonUp() ) {
			cmdR |= C_CMD_R_UP;
		}
		
		// 押し続けられている
		if ( m_mouse.getKeepingTimeR() > 0 ) {
			cmdR |= C_CMD_R_DOWN_KEPP;
		}
		
		// なにもされていないならそのフラグを立てておく
		if ( 0x0 == cmdR ) {
			cmdL = C_CMD_R_NOTHING;
		}
		
		/*----- 中 -----*/

		// ダブルクリック
		if ( m_mouse.isMButtonDClick() ) {
			cmdM |= C_CMD_M_D_CLICK;
		}
		
		// 押している
		if ( m_mouse.isPress(MouseInput.button.middle) ) {
			cmdM |= C_CMD_M_DOWN;
		}

		// 押し上げられた
		if ( m_mouse.isMButtonUp() ) {
			cmdM |= C_CMD_M_UP;
		}
		
		// 押し続けられている
		if ( m_mouse.getKeepingTimeM() > 0 ) {
			cmdM |= C_CMD_M_DOWN_KEPP;
		}
		
		// なにもされていないならそのフラグを立てておく
		if ( 0x0 == cmdM ) {
			cmdM = C_CMD_M_NOTHING;
		}
		
		
		return cmdL | cmdM | cmdR;	
	}
	
	/// コマンド列を文字列表現として返却する
	char[] getCmdString(uint[] cmds) {
		char[] buffer;
		
		foreach (uint cmd; cmds) {
			setCmdString(buffer, cmd);
		}
		
		return buffer;
	}

	/// コマンド列を文字列表現として返却する
	char[] getInputCmdString() {
		char[] buffer;
		
		for (int i = m_bufferPos; i < LC_CMD_STREAM_BUFFER_SIZE; ++i) {
			setCmdString(buffer, vCmdStream[i]);
		}
		
		if ( 0 != m_bufferPos ) {
			for (int i = 0; i < m_bufferPos; ++i ) {
				setCmdString(buffer, vCmdStream[i]);
			}
		}
		
		return buffer;
	}
	
	/// コマンドから文字列を生成する
	void setCmdString(inout char[] buffer, uint cmd) {
		if (C_CMD_L_D_CLICK & cmd) {
			buffer ~= "A";
		}			
		if (C_CMD_L_DOWN & cmd) {
			buffer ~= "B";
		}			
		if (C_CMD_L_DOWN_KEPP & cmd) {
			buffer ~= "C";
		}			
		if (C_CMD_L_UP & cmd) {
			buffer ~= "D";
		}			
		if (C_CMD_L_NOTHING & cmd) {
			buffer ~= "E";
		}			
		

		if (C_CMD_R_D_CLICK & cmd) {
			buffer ~= "F";
		}			
		if (C_CMD_R_DOWN & cmd) {
			buffer ~= "G";
		}			
		if (C_CMD_R_DOWN_KEPP & cmd) {
			buffer ~= "H";
		}			
		if (C_CMD_R_UP & cmd) {
			buffer ~= "I";
		}			
		if (C_CMD_R_NOTHING & cmd) {
			buffer ~= "J";
		}			


		if (C_CMD_M_D_CLICK & cmd) {
			buffer ~= "K";
		}			
		if (C_CMD_M_DOWN & cmd) {
			buffer ~= "L";
		}			
		if (C_CMD_M_DOWN_KEPP & cmd) {
			buffer ~= "M";
		}			
		if (C_CMD_M_UP & cmd) {
			buffer ~= "N";
		}			
		if (C_CMD_M_NOTHING & cmd) {
			buffer ~= "O";
		}			
		
		buffer ~= LC_STR_CANMA;
	}


	/* コマンド入力のストリームサイズ */
	static final const int LC_CMD_STREAM_BUFFER_SIZE = 120;
	static final const char[] LC_STR_CANMA = ",";

	uint[LC_CMD_STREAM_BUFFER_SIZE] vCmdStream;
	int m_bufferPos;
	
	MouseInput m_mouse;
	
	vector!(char[]) vCommands;
}