<reponame>lhn500382/LGame
package org.test.cat.puzzle.game;

import java.util.ArrayList;

import loon.LSystem;
import loon.LTexture;
import loon.LTextures;
import loon.Screen;
import loon.action.sprite.effect.RippleEffect;
import loon.canvas.LColor;
import loon.event.GameKey;
import loon.event.GameTouch;
import loon.event.SysKey;
import loon.font.BMFont;
import loon.geom.RectBox;
import loon.opengl.GLEx;
import loon.utils.MathUtils;
import loon.utils.timer.LTimerContext;

public class Puzzle extends Screen {

	private static final int BOARD_TOP = -816;

	private static final int BOARD_LEFT = 12;

	public static final int SPIN_SCALAR = 2;

	public static final int MAX_PIECE = 6;

	public static final int TILE = 0;

	public static final int REMOVE_MARKER = 1;

	public static final int FALL_MARKER = 2;

	public static final int MATCH_HINT = 3;

	public static final int PLAY = 0;

	public static final int SWAP = 1;

	public static final int REMOVE = 2;

	public static final int FALL = 3;

	public static final int SWAP_BACK = 4;

	public static final int HINT = 5;

	private LTexture bottom;

	private LTexture edge;

	private LTexture[] kitis = new LTexture[9];

	private LTexture logo;

	private LTexture matchesLabel;

	private LTexture scoreLabel;

	private LTexture timeLabel;

	private LTexture kitty;

	private BMFont font;

	private BMFont numbers;

	private boolean starting = true;

	private float ang;

	private int time;

	private int score;

	private int matches;

	private int[][][] tiles = new int[8][20][10];

	private int selectedx;

	private int selectedy;

	private boolean selected;

	private int tappedx;

	private int tappedy;

	private int state;

	private float swapx;

	private float swapy;

	private float removeStep;

	private int fallOffset;

	private long start;

	private RectBox hintRect;

	private RectBox quitRect;

	private int gameOverCounter;

	private int movesLeft;

	public Puzzle() {

	}

	private String pad(int value, int len) {
		String v = "" + value;

		while (v.length() < len) {
			v = "0" + v;
		}

		return v;
	}

	public void onLoad() {

		try {
			font = new BMFont("assets/puzzle.fnt", "assets/puzzle_00.tga");
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			numbers = new BMFont("assets/numbers.fnt", "assets/numbers_00.tga");
		} catch (Exception e) {
			e.printStackTrace();
		}
		//优先绘制用户界面
		fristUserDraw();
		RippleEffect effect = new RippleEffect();
		add(effect);

		bottom = LTextures.loadTexture("assets/bottom.png");
		edge = LTextures.loadTexture("assets/edges.png");
		logo = LTextures.loadTexture("assets/logo.png");
		matchesLabel = LTextures.loadTexture("assets/matches.png");
		scoreLabel = LTextures.loadTexture("assets/score.png");
		timeLabel = LTextures.loadTexture("assets/time.png");
		kitty = LTextures.loadTexture("assets/kitty.png");
		for (int i = 1; i < 10; i++) {
			kitis[i - 1] = LTextures.loadTexture("assets/" + i + ".png");
		}
		kitis[2] = kitis[6];
		kitis[4] = kitis[8];

		hintRect = new RectBox(469, 375, 100, 30);
		quitRect = new RectBox(468, 415, 100, 30);

		clearBoard();

		setBackground("assets/background.png");
	}

	ArrayList<Integer> list = new ArrayList<Integer>();

	private ArrayList<Integer> getValidPieces(int x, int y) {
		list.clear();
		for (int i = 0; i < MAX_PIECE; i++) {
			list.add(new Integer(i));
		}
		if (x > 0) {
			list.remove(new Integer(tiles[x - 1][y][TILE]));
		}
		if (x < 7) {
			list.remove(new Integer(tiles[x + 1][y][TILE]));
		}
		if (y > 0) {
			list.remove(new Integer(tiles[x][y - 1][TILE]));
		}
		if (y < 9) {
			list.remove(new Integer(tiles[x][y + 1][TILE]));
		}

		return list;
	}

	private void start() {
		gameOverCounter = 0;

		start = System.currentTimeMillis();
		starting = false;
		matches = 0;
		score = 0;
		time = 0;

		clearBoard();
		fillSpaces();

		state = PLAY;
	}

	private void clearBoard() {
		for (int x = 0; x < 8; x++) {
			for (int y = 0; y < 20; y++) {
				tiles[x][y][TILE] = -1;
			}
		}
	}

	private void drawNumberString(GLEx g, BMFont font, String str, int y) {
		font.drawString(g, str,
				(456 + ((180 - (numbers.stringWidth(str))) / 2)), y + 1);
		font.drawString(g, str,
				(455 + ((180 - (numbers.stringWidth(str))) / 2)), y);
	}

	public void render(GLEx g) {
	}

	private void finishRemove() {
		for (int x = 0; x < 8; x++) {
			for (int y = 0; y < 20; y++) {
				if (tiles[x][y][REMOVE_MARKER] == 1) {
					tiles[x][y][REMOVE_MARKER] = 0;
					tiles[x][y][TILE] = -1;
				}
			}
		}
	}

	private void fillSpaces() {
		for (int x = 0; x < 8; x++) {
			for (int y = 0; y < 20; y++) {
				ArrayList<Integer> pieces = getValidPieces(x, y);
				if (tiles[x][y][TILE] == -1) {
					tiles[x][y][TILE] = ((Integer) pieces.get((int) (Math
							.random() * pieces.size()))).intValue();
				}
			}
		}
	}

	private int findFalls() {
		int foundFalls = 0;

		for (int y = 19; y > 0; y--) {
			for (int x = 0; x < 8; x++) {
				tiles[x][y][FALL_MARKER] = 0;

				if ((tiles[x][y][TILE] == -1) && (tiles[x][y - 1][TILE] != -1)) {
					tiles[x][y][TILE] = tiles[x][y - 1][TILE];
					tiles[x][y - 1][TILE] = -1;
					tiles[x][y][FALL_MARKER] = 1;
					foundFalls++;
					fallOffset = 64;
				}
			}
		}

		return foundFalls;
	}

	private int findMoves() {
		int total = 0;

		for (int y = 13; y < 20; y++) {
			for (int x = 0; x < 8; x++) {
				if (x < 7) {
					swap(x, y, x + 1, y);
					int matches = findMatches(false);
					swap(x, y, x + 1, y);

					if (matches != 0) {
						total++;
					}
				}
				if (y < 19) {
					swap(x, y, x, y + 1);
					int matches = findMatches(false);
					swap(x, y, x, y + 1);

					if (matches != 0) {
						total++;
					}
				}
			}
		}

		return total;
	}

	private void hint() {
		if (state == PLAY) {
			int total = findMoves();
			if (total == 0) {
			} else {
				int move = (int) (Math.random() * total);

				for (int y = 13; y < 20; y++) {
					for (int x = 0; x < 8; x++) {
						if (x < 7) {
							swap(x, y, x + 1, y);
							int matches = findMatches(false);
							swap(x, y, x + 1, y);

							if (matches != 0) {
								move--;
								if (move <= 1) {
									tiles[x][y][MATCH_HINT] = 360 * SPIN_SCALAR;
									tiles[x + 1][y][MATCH_HINT] = 360 * SPIN_SCALAR;
									state = HINT;
									return;
								}
							}
						}
						if (y < 19) {
							swap(x, y, x, y + 1);
							int matches = findMatches(false);
							swap(x, y, x, y + 1);

							if (matches != 0) {
								move--;
								if (move <= 1) {
									tiles[x][y][MATCH_HINT] = 360 * SPIN_SCALAR;
									tiles[x][y + 1][MATCH_HINT] = 360 * SPIN_SCALAR;
									state = HINT;
									return;
								}
							}
						}
					}
				}
			}
		}
	}

	private void swap(int x1, int y1, int x2, int y2) {
		int t1 = tiles[x1][y1][TILE];
		tiles[x1][y1][TILE] = tiles[x2][y2][TILE];
		tiles[x2][y2][TILE] = t1;
	}

	private int findMatches() {
		return findMatches(true);
	}

	private int findMatches(boolean updateGameState) {
		int foundMatches = 0;

		for (int y = 19; y > 12; y--) {
			for (int x = 0; x < 8; x++) {
				int ours = tiles[x][y][TILE];
				if (ours != -1) {
					if ((x > 0) && (x < 7)) {
						if ((tiles[x - 1][y][TILE] == ours)
								&& (tiles[x + 1][y][TILE] == ours)) {
							int bonus = 100;
							boolean newMatch = true;

							if (tiles[x - 1][y][REMOVE_MARKER] == 1) {
								newMatch = false;
								bonus *= 2;
							}
							if (tiles[x + 1][y][REMOVE_MARKER] == 1) {
								newMatch = false;
								bonus *= 2;
							}

							foundMatches++;

							if (updateGameState) {
								tiles[x - 1][y][REMOVE_MARKER] = 1;
								tiles[x][y][REMOVE_MARKER] = 1;
								tiles[x + 1][y][REMOVE_MARKER] = 1;

								score += bonus;
								if (newMatch) {
									matches++;
								}
							}
						}
					}

					if ((y > 13) && (y < 19)) {
						if ((tiles[x][y - 1][TILE] == ours)
								&& (tiles[x][y + 1][TILE] == ours)) {
							int bonus = 100;
							boolean newMatch = true;

							if (tiles[x][y - 1][REMOVE_MARKER] == 1) {
								newMatch = false;
								bonus *= 2;
							}
							if (tiles[x][y + 1][REMOVE_MARKER] == 1) {
								newMatch = false;
								bonus *= 2;
							}

							foundMatches++;

							if (updateGameState) {
								tiles[x][y - 1][REMOVE_MARKER] = 1;
								tiles[x][y][REMOVE_MARKER] = 1;
								tiles[x][y + 1][REMOVE_MARKER] = 1;

								score += bonus;
								if (newMatch) {
									matches++;
								}
							}
						}
					}
				}
			}
		}

		return foundMatches;
	}

	private void swap() {
		int xd = tappedx - selectedx;
		int yd = tappedy - selectedy;

		swapx = xd * 54;
		swapy = yd * 64;

		selected = false;

		state = SWAP;
	}

	public void mousePressed(int button, int x, int y) {
		if (starting) {
			start();
		} else {
			if (quitRect.contains(x, y)) {
				starting = true;
				return;
			}
			if (hintRect.contains(x, y)) {
				hint();
				return;
			}
			if (state == PLAY) {
				if (button != 0) {
					selected = false;
					return;
				}

				x -= 11;
				y -= -831;

				x /= 54;
				y /= 64;

				if ((x >= 0) && (x < 8) && (y >= 2) && (y < 20)) {
					if (!selected) {
						selected = true;
						selectedx = x;
						selectedy = y;
					} else {
						int xd = Math.abs(x - selectedx);
						int yd = Math.abs(y - selectedy);

						if ((xd == 0) && (yd == 0)) {
							selected = false;
						} else if (xd + yd == 1) {
							tappedx = x;
							tappedy = y;
							selected = false;
							swap();
						}
					}
				} else {
					selected = false;
				}
			}
		}
	}

	public void draw(GLEx g) {

		if (!isOnLoadComplete()) {
			return;
		}
		g.saveTx();
		g.scale(0.8f, 1);
		bottom.draw(2, 450);
		edge.draw(9, 0, edge.getWidth(), 450);
		g.restoreTx();

		matchesLabel.draw(460, 10);
		scoreLabel.draw(480, 130);
		timeLabel.draw(490, 250);
		kitty.draw(535, 340);

		font.drawString(g, "HELP", 470, 386, LColor.black);
		font.drawString(g, "QUIT", 469, 426, LColor.black);

		font.drawString(g, "HELP", 469, 385, LColor.white);
		font.drawString(g, "QUIT", 468, 425, LColor.white);

		if (numbers != null) {
			String str = pad(matches, 4);
			drawNumberString(g, numbers, str, 50);
			str = pad(score, 7);
			drawNumberString(g, numbers, str, 170);
			str = pad(time / 60, 2) + ":" + pad(time % 60, 2);
			drawNumberString(g, numbers, str, 290);
		}

		for (int x = 0; x < 8; x++) {
			for (int y = 0; y < 20; y++) {
				int tile = tiles[x][y][TILE];

				float yofs = 0;
				float xofs = 0;

				if (tile != -1) {
					if (selected) {
						if ((selectedx == x) && (selectedy == y)) {
							yofs = (MathUtils.cos(ang) * 4);
							g.setColor(1f, 1f, 0f, 0.5f);
							g.fillOval(BOARD_LEFT + (x * 54),
									(BOARD_TOP + (y * 64)), 54, 44);
							g.setColor(LColor.black);
							g.drawOval(BOARD_LEFT + (x * 54),
									(BOARD_TOP + (y * 64)), 54, 44);
							g.resetColor();
						}
					}
					if ((state == SWAP) || (state == SWAP_BACK)) {
						int xbase = swapx < 0 ? -54 : 54;
						int ybase = swapy < 0 ? -64 : 64;

						if ((selectedx == x) && (selectedy == y)) {
							if (swapx != 0) {
								xofs = xbase - swapx;
							}
							if (swapy != 0) {
								yofs = ybase - swapy;
							}
						}
						if ((tappedx == x) && (tappedy == y)) {
							if (swapx != 0) {
								xofs = -(xbase - swapx);
							}
							if (swapy != 0) {
								yofs = -(ybase - swapy);
							}
						}
					}
					float size = 1;
					if (state == REMOVE) {
						if (tiles[x][y][REMOVE_MARKER] == 1) {
							size = removeStep;
							xofs = (54 - (54 * size)) / 2;
							yofs = (64 - (64 * size)) / 2;
						}
					}
					if (state == FALL) {
						if (tiles[x][y][FALL_MARKER] == 1) {
							yofs = -fallOffset;
						}
					}
					if ((state == HINT) && (tiles[x][y][MATCH_HINT] > 0)) {
						int xp = 11 + (x * 54);
						int yp = -831 + (y * 64);
						kitis[tile].draw(xp, yp, tiles[x][y][MATCH_HINT]
								/ SPIN_SCALAR);
					} else {
						kitis[tile].draw((11 + (x * 54) + xofs), (-831
								+ (y * 64) + yofs), (54 * size), (64 * size));
					}
				}
			}
		}

		if (starting) {

			g.resetColor();
			g.setColor(0, 0, 0, 0.7f);
			g.fillRect(50, 50, 540, 380);
			g.setColor(0, 0, 0);
			g.drawRect(50, 50, 540, 380);
			g.drawRect(48, 48, 544, 384);
			g.resetColor();
			g.drawRect(49, 49, 542, 382);
			logo.draw(180, 60);

			if (gameOverCounter > 0) {
				g.drawString("Well Done!", 280, 300);
			} else {
				String mes = "这是一款拼图游戏示例";
				g.drawString(mes,
						(getWidth() - g.getFont().stringWidth(mes)) / 2, 180);
				g.drawString("Click to Start", 255,
						(385 + (MathUtils.cos(ang) * 8)));
			}
		}

	}

	public void alter(LTimerContext timer) {

		if (!isOnLoadComplete()) {
			return;
		}
		long delta = timer.timeSinceLastUpdate;

		time = (int) ((System.currentTimeMillis() - start) / 1000);
		if (starting) {
			time = 0;
		}
		ang += delta * 0.01f;

		if (state == HINT) {
			boolean found = false;

			for (int x = 0; x < 8; x++) {
				for (int y = 0; y < 20; y++) {
					if (tiles[x][y][MATCH_HINT] > 0) {
						tiles[x][y][MATCH_HINT] -= delta;
						found = true;
					}
				}
			}

			if (!found) {
				state = PLAY;
			}
		}
		if (state == REMOVE) {
			removeStep -= delta * 0.005f;
			if (removeStep < 0) {
				removeStep = 0;
				finishRemove();
				int falls = findFalls();
				if (falls == 0) {
					fillSpaces();
					int matches = findMatches();
					if (matches == 0) {
						state = PLAY;
					} else {
						state = REMOVE;
						removeStep = 1;
					}
				} else {
					fallOffset = 64;
					state = FALL;
				}
			}
		}
		if (state == FALL) {
			fallOffset -= delta * 0.5f;
			if (fallOffset <= 0) {
				fallOffset = 0;
				int falls = findFalls();
				if (falls == 0) {
					fillSpaces();
					int matches = findMatches();
					if (matches == 0) {
						state = PLAY;
					} else {
						state = REMOVE;
						removeStep = 1;
					}
				} else {
					fallOffset = 64;
					state = FALL;
				}
			}
		}
		if ((state == SWAP) || (state == SWAP_BACK)) {
			float swapSpeed = 0.17f;

			if (swapx > 0) {
				swapx -= delta * swapSpeed;
				swapx = Math.max(0, swapx);
			}
			if (swapy > 0) {
				swapy -= delta * swapSpeed;
				swapy = Math.max(0, swapy);
			}
			if (swapx < 0) {
				swapx += delta * swapSpeed;
				swapx = Math.min(0, swapx);
			}
			if (swapy < 0) {
				swapy += delta * swapSpeed;
				swapy = Math.min(0, swapy);
			}

			if ((swapx == 0) && (swapy == 0)) {
				int temp = tiles[tappedx][tappedy][TILE];
				tiles[tappedx][tappedy][TILE] = tiles[selectedx][selectedy][TILE];
				tiles[selectedx][selectedy][TILE] = temp;

				if (state == SWAP_BACK) {
					state = PLAY;
				} else {
					int matches = findMatches();
					if (matches == 0) {
						swap();
						state = SWAP_BACK;
					} else {
						removeStep = 1;
						state = REMOVE;
					}
				}
			}
		}

		if (gameOverCounter > 0) {
			gameOverCounter -= delta;
			if (gameOverCounter <= 0) {
				clearBoard();
			}
		}

		if ((!starting) && (state == PLAY)) {
			movesLeft = findMoves();

			if (movesLeft == 0) {
				starting = true;
				gameOverCounter = 2000;
			}
		}

	}

	public void touchDown(GameTouch e) {
		mousePressed(e.getButton(), e.x(), e.y());
	}

	public void touchUp(GameTouch e) {

	}

	public void touchMove(GameTouch e) {

	}

	public void touchDrag(GameTouch e) {

	}

	public void onKeyDown(GameKey e) {
		if (e.getKeyCode() == SysKey.ESCAPE) {
			if (starting) {
				LSystem.exit();
			} else {
				starting = true;
			}
		}
	}

	@Override
	public void resize(int width, int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void resume() {
		// TODO Auto-generated method stub

	}

	@Override
	public void pause() {
		// TODO Auto-generated method stub

	}

	@Override
	public void close() {
		// TODO Auto-generated method stub

	}

}
