package Game;

import DreamBubbleMidlet;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.LayerManager;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;

public abstract class Game extends GameCanvas implements Runnable {

	protected DreamBubbleMidlet dreamBubbleMidlet;

	protected Graphics g;
	protected Image loadingImage;
	protected Image pauseImage;
	protected Image cursorImage;
	protected Image jackStateImage;
	protected Image johnStateImage;
	protected Image numberImage;

	protected Sprite cursor;
	protected Sprite number;
	protected LayerManager cursorManager;
	protected LayerManager numberManager;

	protected Player musicPlayer;

	protected Hashtable bombTable;
	protected Map map;
	protected LayerManager gameLayerManager;
	protected Role player;

	protected int screenWidth;
	protected int screenHeight;
	protected int delay = 50;
	protected int[][] bornPlace;
	protected int chooseIndex;
	protected int stageIndex = 0;
	protected int gameClock;
	protected int loadPercent;

	protected boolean isPause;
	protected boolean isEnd;
	protected boolean isPlaying;
	protected boolean isLoading;

	protected Thread mainThread;

	public Game(DreamBubbleMidlet dreamBubbleMidlet) {
		super(false);
		this.setFullScreenMode(true);
		this.dreamBubbleMidlet = dreamBubbleMidlet;

		this.screenWidth = this.getWidth();
		this.screenHeight = this.getHeight();

		try {
			this.loadingImage = Image.createImage("/Game/Loading.png");
			this.pauseImage = Image.createImage("/Game/Pause.png");
			this.cursorImage = Image.createImage("/Game/Cursor.png");
			this.jackStateImage = Image.createImage("/State/JackState.png");
			this.johnStateImage = Image.createImage("/State/JohnState.png");
			this.numberImage = Image.createImage("/State/Number.png");
		} catch (IOException e) {
			e.printStackTrace();
		}

		this.g = this.getGraphics();
	}

	public void loadStage(int stage) {
		this.isEnd = false;
		this.isPause = false;
		this.isPlaying = false;
		this.gameLayerManager = new LayerManager();
		this.cursorManager = new LayerManager();
		this.numberManager = new LayerManager();
		this.bombTable = new Hashtable();
		this.cursor = new Sprite(this.cursorImage, 32, 32);
		this.number = new Sprite(this.numberImage, 12, 10);
		this.loadPercent = 20;
		sleep();

		loadMap(stage);
		this.loadPercent = 40;
		sleep();

		loadPlayer();
		this.loadPercent = 60;
		sleep();

		this.gameLayerManager.append(map.getBombLayer());
		this.gameLayerManager.append(map.getBuildLayer());
		this.gameLayerManager.append(map.getToolLayer());
		this.gameLayerManager.append(map.getFloorLayer());
		this.gameLayerManager.setViewWindow(0, -5, screenWidth,
				Global.MAP_HEIGHT + 5);
		this.cursorManager.append(cursor);
		this.numberManager.append(number);
		this.loadPercent = 80;
		sleep();

		this.loadPercent = 100;
		sleep();
		isPlaying = true;
	}

	public void run() {
		while (!isEnd) {
			long beginTime = System.currentTimeMillis();
			this.drawScreen();
			long endTime = System.currentTimeMillis();
			if (endTime - beginTime < this.delay) {
				try {
					Thread.sleep(this.delay - (endTime - beginTime));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public void loadMap(int stage) {
		releaseMusic();
		try {
			this.musicPlayer = Manager.createPlayer(getClass()
					.getResourceAsStream("/Sound/" + (stage + 1) + ".mid"),
					"audio/midi");
			this.musicPlayer.prefetch();
			this.musicPlayer.setLoopCount(-1);
			this.musicPlayer.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
		switch (stage) {
		case 0:
			this.map = new Map(Global.MAP_BLOCK);
			this.bornPlace = Global.MAP_BLOCK_BORNPLACE;

			break;
		case 1:
			this.map = new Map(Global.MAP_FACTORY);
			this.bornPlace = Global.MAP_FACTORY_BORNPLACE;
			break;
		case 2:
			this.map = new Map(Global.MAP_FOREST);
			this.bornPlace = Global.MAP_FOREST_BORNPLACE;
			break;
		case 3:
			this.map = new Map(Global.MAP_PIRATE);
			this.bornPlace = Global.MAP_PIRATE_BORNPLACE;
			break;
		case 4:
			this.map = new Map(Global.MAP_FAUBOURG);
			this.bornPlace = Global.MAP_FAUBOURG_BORNPLACE;
			break;
		}
	}

	public void releaseMusic() {
		if (this.musicPlayer != null) {
			try {
				this.musicPlayer.close();
				this.musicPlayer = null;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public abstract void loadPlayer();

	public abstract void playerUpdate();

	public void bombUpdate() {
		Enumeration enu = this.bombTable.keys();
		while (enu.hasMoreElements()) {
			String key = (String) enu.nextElement();
			Bomb bomb = (Bomb) (bombTable.get(key));

			if (bomb.isvisable) {
				bomb.update();
			} else {
				bombTable.remove(key);
				bomb = null;
			}
		}
	}

	public void mapUpdate() {
		this.map.update();
	}

	public void drawScreen() {
		if (gameClock < 10000)
			gameClock++;
		else
			gameClock = 0;
		if (!this.isLoading) {
			if (!isPause) {
				this.operate();
				this.bombUpdate();
				this.playerUpdate();
				this.mapUpdate();
				g.setColor(0x000000);
				g.fillRect(0, 0, getWidth(), getHeight());
				this.drawState();
				gameLayerManager.paint(g, 0, this.screenHeight
						- Global.MAP_HEIGHT - 5);
			} else {
				this.drawPauseFrame();
			}
		} else {
			this.drawLoadingFrame();
		}
		this.flushGraphics();
	}

	public void drawFailScreen() {

	}

	public void drawState() {
		if (this.player.type == Global.JACK) {
			g.drawImage(jackStateImage, 60, 5, Graphics.TOP | Graphics.LEFT);
		}
		if (this.player.type == Global.JOHN) {
			g.drawImage(johnStateImage, 60, 5, Graphics.TOP | Graphics.LEFT);
		}

		this.number.setFrame(this.player.bombNums);
		this.numberManager.paint(g, 101, 15);
		this.number.setFrame(this.player.speed);
		this.numberManager.paint(g, 133, 15);
		this.number.setFrame(this.player.power);
		this.numberManager.paint(g, 165, 15);
	}

	protected void drawPauseFrame() {
		g.setColor(0x000000);
		g.fillRect(0, 0, getWidth(), getHeight());
		this.drawState();
		if (gameClock % 5 == 0)
			this.cursor.setFrame((this.cursor.getFrame() + 1) % 4);
		this.gameLayerManager.paint(g, 0, this.screenHeight - Global.MAP_HEIGHT
				- 5);
		this.cursorManager.paint(g, screenWidth / 2 - pauseImage.getWidth() / 2
				- 32, screenHeight / 2 - pauseImage.getHeight() / 2
				+ this.chooseIndex * 33 + 24);
		g.drawImage(pauseImage, screenWidth / 2, screenHeight / 2,
				Graphics.HCENTER | Graphics.VCENTER);
	}

	protected void drawLoadingFrame() {
		g.setColor(66, 70, 246);
		g.fillRect(0, 0, screenWidth, screenHeight);

		g.drawImage(loadingImage, screenWidth / 2, 2 * screenHeight / 5,
				Graphics.HCENTER | Graphics.VCENTER);

		g.setColor(0, 255, 0);
		g.fillRect((screenWidth - 120) / 2, 2 * screenHeight / 3,
				(this.loadPercent * 120) / 100, 10);

		g.setColor(255, 0, 0);
		g.drawRect((screenWidth - 120) / 2, 2 * screenHeight / 3, 120, 10);
	}

	public void showMe() {
		if (this.dreamBubbleMidlet.menu.musicPlayer != null) {
			this.dreamBubbleMidlet.menu.musicPlayer.close();
			this.dreamBubbleMidlet.menu.musicPlayer = null;
		}
		new Loading(this.stageIndex);
		if (this.mainThread == null) {
			mainThread = new Thread(this);
			mainThread.start();
		}
		this.dreamBubbleMidlet.show(this);
	}

	public void operate() {
		int keyStates = getKeyStates();
		if ((keyStates & DOWN_PRESSED) != 0) {
			this.player.walk(Global.SOUTH);
		} else {
			if ((keyStates & UP_PRESSED) != 0) {
				this.player.walk(Global.NORTH);
			} else {
				if ((keyStates & RIGHT_PRESSED) != 0) {
					this.player.walk(Global.EAST);
				} else {
					if ((keyStates & LEFT_PRESSED) != 0) {
						this.player.walk(Global.WEST);
					}
				}
			}
		}
	}

	protected void keyPressed(int key) {
		if (!this.isPlaying)
			return;
		if (!this.isPause && key == -7) {// ÓÒ¼ü
			this.chooseIndex = 0;
			this.pauseGame();
			return;
		}
		if (key == 35) {// #¼ü
			this.nextStage();
			return;
		}
		if (key == 42) {// *¼ü
			this.preStage();
			return;
		}
		if (this.isPause) {
			switch (key) {
			case -1:
			case -3:
				if (this.chooseIndex == 0)
					this.chooseIndex = 2;
				else
					this.chooseIndex = (this.chooseIndex - 1) % 3;
				break;
			case -2:
			case -4:
				this.chooseIndex = (this.chooseIndex + 1) % 3;
				break;
			case -5:// È·ÈÏ¼ü
			case -6:// ×óÈí¼ü
				switch (chooseIndex) {
				case 0:
					this.continueGame();
					break;
				case 1:
					this.restart();
					break;
				case 2:
					this.endGame();
					break;
				}
				break;
			default:
				break;
			}
		} else {
			switch (key) {
			case 53:
			case -5:// È·ÈÏ¼ü
				this.player.setBomb(this.player.getRow(), this.player.getCol());
				break;
			}
		}
	}

	public void restart() {
		new Loading(this.stageIndex);
	}

	public void continueGame() {
		this.isPause = false;
		this.player.play();
	}

	public void pauseGame() {
		this.isPause = true;
		try {
			this.musicPlayer.stop();
		} catch (MediaException e) {
			e.printStackTrace();
		}
		this.player.stop();
	}

	public void endGame() {
		this.isEnd = true;
		this.mainThread = null;
		this.musicPlayer.close();
		System.gc();
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.dreamBubbleMidlet.menu.showMe();
	}

	public void nextStage() {
		if (this.stageIndex < 4) {
			this.stageIndex++;
		}
		new Loading(this.stageIndex);
	}

	public void preStage() {
		if (this.stageIndex > 0) {
			this.stageIndex--;
		}
		new Loading(this.stageIndex);
	}

	class Loading implements Runnable {
		private Thread innerThread;
		private int stageIndex;

		public Loading(int stageIndex) {
			this.stageIndex = stageIndex;
			innerThread = new Thread(this);
			innerThread.start();
		}

		public void run() {
			isLoading = true;
			loadPercent = 0;
			System.gc();
			loadStage(stageIndex);
			isLoading = false;
		}
	}

	public void sleep() {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
