package com.haru.player;


import game.constant.GameConstant;

import java.awt.Point;


import com.haru.component.GameField;
import com.haru.component.Mino;
import com.haru.component.TetraMino;

import static com.haru.input.InputBuffer.*;

/**
 * Playerが頭脳を持つ。
 * ストリームとは切り離す。
 *
 */
public class Player {



	// フィールドの情報
	private GameField field;

	// 現在の操作中のミノの情報
	private BlockState cur;

	private int landingCount;

	public Player(GameField field) {
		this.field = field;
		cur = new BlockState();
		landingCount = 0;
	}


	/**
	 * ブロックの場所の更新
	 * @param input 入力された情報
	 * @param isDown trueの時落下も行う
	 * @return 	// falseなら次のブロックを出現させる
	 */
	public boolean update(int[] input, boolean isDown) {

		int dx = input[0];
		int dy = input[1];
		int d_t = input[2];
		int isDrop = input[3];


		//---------------------------------------------------------------------------//
		// 移動・回転
		//---------------------------------------------------------------------------//
//		move(ret);
		move(dx, dy, d_t);


		//---------------------------------------------------------------------------//
		// ハードドロップ
		//---------------------------------------------------------------------------//
		if (isDrop == TRUE) {
			drop();
			return false;
		}

		//---------------------------------------------------------------------------//
		// 遊び時間だけ待って設置
		//---------------------------------------------------------------------------//
		if (landingCount > 0) {

			// 下が押されたら確定
			if (input[1] == 1) {
				putGround();
				return false;
			}


	// とりあえず-------------------------------

			// 横移動や回転があったなら、遊び時間のリセット
			if (input[0] != 0 || input[2] != 0) {
				landingCount = GameConstant.LAND_MAX;

				// returnしない？
			}
	//-------------------------------------------

			if (--landingCount == 0) {

				putGround();
				return false;
			}

			// 地面から離れた
			if (isPutBlockDown(cur) ) {
				landingCount = 0;
			}

			return true;
		}


		//---------------------------------------------------------------------------//
		// 落下
		//---------------------------------------------------------------------------//
		if (isDown) {
			// 地面についた
			if (!down() ) {
				landingCount = GameConstant.LAND_MAX;
//				System.out.println("l:" + landingCount + "\tloop:" + loop);
			}
		}

		return true;

	}




	//--------------------------------------------------------------------------//
	// 出現, 移動・重力, 設置
	//--------------------------------------------------------------------------//
	// 出現
	public void setCur(TetraMino b) {
		cur.setDefault(b);
		field.putBlock(cur.getType(), cur);
	}

	// 移動 (入力を反映)
	public boolean move(int dx, int dy, int d_t) {

		// 準備
		if (dx == 0 && dy == 0 && d_t == 0)
			return false;

		// 現在のブロック削除
		field.putBlock(TetraMino.T_EMPTY, cur);

		// ブロックの軸の位置
		Point pos = new Point(cur.getX() + dx, cur.getY() + dy);

		// ブロックが置けない場合
		if ( !isPutBlockSRS(cur.getBlock(), pos, cur.getTurn(), d_t)) {  // posの値は内部で変更される

			// 現在のブロック再配置
			field.putBlock(cur.getType(), cur);

			return false;
		}


		// 現在のブロックの情報の更新
		cur.setPos(pos);
		cur.setTurn( (4 + cur.getTurn() + d_t) % 4 );


		// 現在のブロックの配置
		field.putBlock(cur.getType(), cur);

		return true;
	}

	// 移動 (重力)
	public boolean down() {

		if (isPutBlockDown(cur)) {

			// 下に移動して配置
			field.putBlock(Mino.T_EMPTY, cur);
			cur.setY( cur.getY() + 1 );
			field.putBlock(cur.getType(), cur);
			return true;
		}

		return false;
	}



	//地面に設置
	private void putGround() {

		// 現在のブロック位置固定
		field.putBlock(cur.getType(), cur);

		// ブロックが一列揃ったか確認
		// rm_flagを求める
		boolean flag = false;
		int[] rm_flag = new int[field.getHeight()]; // y行目が消えるか
		int[][] p = cur.getInfo(cur.getTurn());

		if (isFullLine(cur.getY())) {
			flag = true;
			rm_flag[cur.getY()] = 1;
		}
		for (int i = 0; i < 3; i++) {
			if (isFullLine(cur.getY() + p[i][1]) ) {
				flag = true;
				rm_flag[cur.getY() + p[i][1]] = 1;
			}
		}

		// 揃った行があれば消す
		if (flag) {
			removeLine(rm_flag);
		}


		landingCount = 0; // 必要なんだよな
	}

	// 地面まで移動して設置
	private void drop() {
		// 判定のために、仮に現在のブロック削除
		field.putBlock(Mino.T_EMPTY, cur);


		for (int i = 1 ; cur.getY() + i < field.getHeight(); i++ ) {
			if (!isPutBlock(cur.getInfo(cur.getTurn()), cur.getX(), cur.getY() + i)) {


				// (i-1)個下に置く
				cur.setY( cur.getY() + i - 1  );
				putGround();		// いきなり判定に持っていく
				break;
			}
		}

	}



	//--------------------------------------------------------------------------//
	// 関数群(down)
	//--------------------------------------------------------------------------//

	// 下に移動できるか
	// ブロックの情報を変化させるな
	private boolean isPutBlockDown(BlockState cur) {
		// 判定のために、仮に現在のブロック削除
		field.putBlock(Mino.T_EMPTY, cur);

		if ( isPutBlock(cur.getInfo(cur.getTurn()), cur.getX(), cur.getY() + 1)) {
			// 現在のブロックを元に戻す
			field.putBlock(cur.getType(), cur);
			return true;
		}

		// 現在のブロックを元に戻す
		field.putBlock(cur.getType(), cur);
		return false;
	}




	// 確定した後呼ぶ
	private boolean isFullLine(int y) {

		for (int x = 0; x < field.getWidth(); x++) {

			if (field.getType(x, y) == TetraMino.T_EMPTY) {
				return false;
			}
		}
		return true;
	}



	public void removeLine(int[] down) {
		// 揃った行を消し、downの値を一個上にずらす
		for (int i = 1; i < down.length; i++) {
			if (down[i] == 1) {
	//				field.putLine(i, Block.T_EMPTY);
				down[i - 1] = down[i];
				down[i] = 0;
			}
		}

		// 消えた後y行目がずれる数を求める
		for (int i = 1; i < down.length; i++) {
			if (down[i] > 0) {
				for (int j = i - 1; j >= 0; j --) {
					down[j] += 1;
				}
			}
		}

		// downに基づいて下から順にずらす
		for (int i = down.length - 1; i >= 1; i--) {
			if (down[i] > 0) {

				// down[i]個下に置く
				field.putLine(i + down[i], field.getLine(i));
			}
		}
	}







	//--------------------------------------------------------------------------//
	// 関数群 (move)
	//--------------------------------------------------------------------------//
	//----------------------------------------------------------------------------//

	// ブロックが置けるかどうか
	// 置けない場所の位置も重要だったりする
	private boolean isPutBlock(int[][] info , int x, int y) {
//		if (field.getType(x, y) != Block.T_EMPTY)
//			return false;
//		for (int i = 0; i < 3; i++) {
//
//			if ((x + info[i][0] < 0 || x + info[i][0] > GameField.WIDTH)
//			  || (y + info[i][1] < 0 || y + info[i][1] > GameField.HEIGHT))
//				return false;
//
//
//			if (field.getType(x + info[i][0], y + info[i][1]) != Block.T_EMPTY)
//				return false;
//		}
//		return true;

		// debug用

		if (field.getType(x, y) != TetraMino.T_EMPTY && field.getType(x, y) != TetraMino.T_CURRENT)
			return false;
		for (int i = 0; i < 3; i++) {
			if ((x + info[i][0] < 0 || x + info[i][0] > GameField.WIDTH)
			  || (y + info[i][1] < 0 || y + info[i][1] > GameField.HEIGHT))
				return false;

			if (field.getType(x + info[i][0], y + info[i][1]) != TetraMino.T_EMPTY &&
				field.getType(x + info[i][0], y + info[i][1]) != TetraMino.T_CURRENT)
				return false;
		}
		return true;
	}




	// パラメータ curBlock, curPos, curTurn(値を変化させたくないから、引数として取る)
	// pos : curPos + d ← 移動予定位置（書き換え可）
	private boolean isPutBlockSRS(TetraMino block, Point pos, int turn, int dir) {

		int[][] info = block.getInfo((4 + turn + dir)%4);

			// Iブロックの位置補正(infoの辻褄合わせ)
			if (block.getType() == TetraMino.T_I) {

				Point c = correctI(turn, dir);

				pos.x += c.x;
				pos.y += c.y;
			}



		for (int i = 1; i <= 5; i++) {
			Point p = moveDirection(i, block.getType(), turn, dir);

			// 範囲外ならfalseを返す
			if (pos.x  + p.x < 0 || pos.x + p.x >= field.getWidth()
			  || pos.y + p.y < 0 || pos.y + p.y >= field.getHeight()) {
				  return false;
			}


			if (isPutBlock(info, pos.x + p.x, pos.y + p.y)) {

				if (i != 1) {
					System.out.println("turn:" + turn + " dir:" + dir);
					System.out.println("第"+i+"候補: " + p.x + ","+ p.y);
					disp(info);
				}

				// 回転軸補正
				pos.x += p.x;
				pos.y += p.y;

				return true;
			}
		}

		return false;
	}



	// Iブロックは回転のたびに位置の補正が必要
	// 現在のブロックに適応
	public Point correctI(int turn, int dir) {

		Point p = new Point(0, 0);
		if (dir == R_RIGHT) {
			switch (turn) {
			case 0: // →
				p.x = 1;
				break;
			case 1: // ↓
				p.y = 1;
				break;
			case 2: // ←
				p.x = -1;
				break;
			case 3: // ↑
				p.y = -1;
				break;
			}
		} else if (dir == R_LEFT) {
			switch (turn) {
			case 0: // ↓
				p.y = 1;
				break;
			case 1: // ←
				p.x = -1;
				break;
			case 2: // ↑
				p.y = -1;
				break;
			case 3: // →
				p.x = 1;
				break;
			}
		}

		return p;
	}





	private Point moveDirection(int i, int type, int turn, int dir) {

		Point p = new Point(0, 0);

		if (type == TetraMino.T_I) {
			p = moveDirI(i, turn, dir);
		} else {
			p = moveDirOther(i, type, turn, dir);
		}

		return p;
	}

	public Point moveDirOther(int i, int type, int turn, int dir) {

		Point p = new Point(0, 0);
//				第一候補	 2	 		3	 4	 	5
//		初期状態
//		右回転	 基本回転	 左1	 左1上1	 下2	 左1下2
//		左回転	 基本回転	 右1	 右1上1	 下2	 右1下2
//		右90度回転状態
//		右回転	 基本回転	 右1	 右1下1	 上2	 右1上2
//		左回転	 基本回転	 右1	 右1下1	 上2	 右1上2
//		180度回転状態
//		右回転	 基本回転	 右1	 右1上1	 下2	 右1下2
//		左回転	 基本回転	 左1	 左1上1	 下2	 左1下2
//		左90度回転状態
//		右回転	 基本回転	 左1	 左1下1	 上2	 左1上2
//		左回転	 基本回転	 左1	 左1下1	 上2	 左1上2
		switch(i) {
		// 第一候補
		case 1:
			break;


		// 第２候補
		case 2:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = -1;
				} else if (dir == R_LEFT) {
					p.x = 1;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = 1;
				} else if (dir == R_LEFT) {
					p.x = 1;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = 1;
				} else if (dir == R_LEFT) {
					p.x = -1;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = -1;
				} else if (dir == R_LEFT) {
					p.x = -1;
				}
				break;
			}
			break;
		// 第３候補
		case 3:
			switch (turn) {
			case 0:
				if (type == TetraMino.T_T)
					return p;	// パス

				if (dir == R_RIGHT) {
					p.x = -1;
					p.y = -1;
				} else if (dir == R_LEFT) {
					p.x = 1;
					p.y = -1;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = 1;
				} else if (dir == R_LEFT) {
					p.x = 1;
					p.y = 1;
				}
				break;
			case 2:
				if (type == TetraMino.T_T)
					return p;	// パス

				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = -1;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = -1;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = -1;
					p.y = 1;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = 1;
				}
				break;
			}
			break;
		// 第４候補
		case 4:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.y = 2;
				} else if (dir == R_LEFT) {
					p.y = 2;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.y = -2;
				} else if (dir == R_LEFT) {
					p.y = -2;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.y = 2;
				} else if (dir == R_LEFT) {
					p.y = 2;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.y = -2;
				} else if (dir == R_LEFT) {
					p.y = -2;
				}
				break;
			}
			break;
		// 第５候補
		case 5:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = -1;
					p.y = 2;
				} else if (dir == R_LEFT) {
					p.x = 1;
					p.y = 2;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = -2;
				} else if (dir == R_LEFT) {
					p.x = 1;
					p.y = -2;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = 2;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = 2;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = -1;
					p.y = -2;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = -2;
				}
				break;
			}
			break;
		default:
			break;
		}

		return p;
	}

	private Point moveDirI(int i, int turn, int dir) {
//		初期状態
//		右回転	 基本回転	 左2	 右1	 左2下1	 右1上2
//		左回転	 基本回転	 左1	 右2	 左1上2	 右2下1
//		右90度回転状態
//		右回転	 基本回転	 左1	 右2	 左1上2	 右2下1
//		左回転	 基本回転	 右2	 左1	 右2上1	 左1下2
//		180度回転状態
//		右回転	 基本回転	 右1	 左2	 右1下2	 左2上1
//		左回転	 基本回転	 右2	 左1	 右2上1	 左1下2
//		左90度回転状態
//		右回転	 基本回転	 右1	 左2	 右1下2	 左2上1
//		左回転	 基本回転	 左2	 右1	 左2下1	 右1上2

		Point p = new Point(0, 0);

		switch(i) {
		// 第一候補
		case 1:
			break;

		// 第２候補
		case 2:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = -2;
				} else if (dir == R_LEFT) {
					p.x = -1;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = -1;
				} else if (dir == R_LEFT) {
					p.x = 2;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = 1;
				} else if (dir == R_LEFT) {
					p.x = 2;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = 1;
				} else if (dir == R_LEFT) {
					p.x = -2;
				}
				break;
			}
			break;
		// 第３候補
		case 3:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = 1;
				} else if (dir == R_LEFT) {
					p.x = 2;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = 2;
				} else if (dir == R_LEFT) {
					p.x = -1;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = -2;
				} else if (dir == R_LEFT) {
					p.x = -1;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = -2;
				} else if (dir == R_LEFT) {
					p.x = 1;
				}
				break;
			}
			break;
		// 第４候補
		case 4:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = -2;
					p.y = 1;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = -2;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = -1;
					p.y = -2;
				} else if (dir == R_LEFT) {
					p.x = -2;
					p.y = -1;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = 2;
				} else if (dir == R_LEFT) {
					p.x = 2;
					p.y = -1;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = 2;
				} else if (dir == R_LEFT) {
					p.x = -2;
					p.y = 1;
				}
				break;
			}
			break;
		// 第５候補
		case 5:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = -2;
				} else if (dir == R_LEFT) {
					p.x = 2;
					p.y = 1;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = 2;
					p.y = 1;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = 2;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = -2;
					p.y = -1;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = 2;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = -2;
					p.y = -1;
				} else if (dir == R_LEFT) {
					p.x = 1;
					p.y = -2;
				}
				break;
			}
			break;
		default:
			break;
		}

		return p;



	}

	private Point moveDirI_TGM(int i, int turn, int dir) {
//		テトリミノの状態	 回転方向	 第一候補	 第二候補	 第三候補	 第四候補	 第五候補
//		初期状態
//		右回転	 基本回転	 左2	 右1	 左2下1	 右1上2
//		左回転	 基本回転	 左1	 右2	 左1上2	 右2下1
//		右90度回転状態
//		右回転	 基本回転	 左1	 右2	 左1上2	 右2下1
//		左回転	 基本回転	 右2	 左1	 右2上1	 左1下2
//		180度回転状態
//		右回転	 基本回転	 右1	 左2	 右1下2	 左2上1
//		左回転	 基本回転	 右2	 左1	 右2上1	 左1下2
//		左90度回転状態
//		右回転	 基本回転	 右1	 左2	 右1下2	 左2上1
//		左回転	 基本回転	 左2	 右1	 左2下1	 右1上2

		Point p = new Point(0, 0);

		switch(i) {
		// 第一候補
		case 1:
			break;

		// 第２候補
		case 2:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = -2;
				} else if (dir == R_LEFT) {
					p.x = 1;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = -1;
				} else if (dir == R_LEFT) {
					p.x = 2;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = -2;
				} else if (dir == R_LEFT) {
					p.x = 2;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = -2;
				} else if (dir == R_LEFT) {
					p.x = 1;
				}
				break;
			}
			break;
		// 第３候補
		case 3:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = 1;
				} else if (dir == R_LEFT) {
					p.x = -1;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = 2;
				} else if (dir == R_LEFT) {
					p.x = -1;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = 1;
				} else if (dir == R_LEFT) {
					p.x = -1;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = 1;
				} else if (dir == R_LEFT) {
					p.x = -2;
				}
				break;
			}
			break;
		// 第４候補
		case 4:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = -1;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = -2;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = -1;
					p.y = -2;
				} else if (dir == R_LEFT) {
					p.x = -2;
					p.y = -1;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = -2;
					p.y = -1;
				} else if (dir == R_LEFT) {
					p.x = 2;
					p.y = -1;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = -2;
					p.y = -1;
				} else if (dir == R_LEFT) {
					p.x = 1;
					p.y = -2;
				}
				break;
			}
			break;
		// 第５候補
		case 5:
			switch (turn) {
			case 0:
				if (dir == R_RIGHT) {
					p.x = -1;
					p.y = 1;
				} else if (dir == R_LEFT) {
					p.x = 2;
					p.y = 1;
				}
				break;
			case 1:
				if (dir == R_RIGHT) {
					p.x = 2;
					p.y = 1;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = 2;
				}
				break;
			case 2:
				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = 1;
				} else if (dir == R_LEFT) {
					p.x = -1;
					p.y = 2;
				}
				break;
			case 3:
				if (dir == R_RIGHT) {
					p.x = 1;
					p.y = 2;
				} else if (dir == R_LEFT) {
					p.x = -2;
					p.y = 1;
				}
				break;
			}
			break;
		default:
			break;
		}

		return p;



	}



	//--------------------------------------------------------------------------//

	// infoの状態確認
	public void disp(int[][] info) {
		int[][] masu = new int[5][5];
		// 中心
		masu[2][2] = 1;
		for (int i = 0; i < 3; i++) {
			int x = info[i][0] + 2;
			int y = info[i][1] + 2;
			masu[y][x] = 1;
		}
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				System.out.printf("%2d", masu[i][j]);
			}
			System.out.println();
		}
		System.out.println();
	}
}
