import java.applet.*;
import java.awt.event.*;
import java.awt.*;
import java.net.*;
import java.text.*;

public class GemSwapper extends Applet implements Runnable, MouseMotionListener, MouseListener {
	final Color BG_COLOR = new Color(0x001111);
	final Color SCORE_BG = new Color(0x001122);
	final int THINK_TIME = 500;
	final int GAME_TIME = 10000;
	final DecimalFormat SCORE_FORMAT = new DecimalFormat("00000");
	
	Font noMoveFont, scoreTitleFont, scoreFont;	
	Graphics2D offScreenGraphics;
	Image offScreenImage;
	Dimension screen;
	Thread gameThread;
	Game game;
	MoveBlinker blinker;
	Thread blinkerThread;
	int thinkCounter, time, bestScore;
	FPS fpsLimiter;
	
	public void init() {
		screen = getSize();
		offScreenImage = createImage(screen.width,screen.height);
		offScreenGraphics = (Graphics2D)offScreenImage.getGraphics();
		offScreenGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		addMouseMotionListener(this);
		addMouseListener(this);
		thinkCounter = time = bestScore = 0;
		try {
			URL fontURL = GemSwapper.class.getResource("/resources/fonts/SF_Espresso_Shack_Bold_Italic.ttf");
			Font font = Font.createFont(Font.TRUETYPE_FONT, fontURL.openStream());
			noMoveFont = font.deriveFont(48f);
			scoreTitleFont = font.deriveFont(24f);
			scoreFont = font.deriveFont(18f);
		} catch (Exception e) {}
		game = new Game(8, 6);
		gameThread = new Thread(this);
		fpsLimiter = new FPS(24);
		gameThread.start();
	}
	
	public void paint(Graphics g) {
		long ST = System.currentTimeMillis();
		offScreenGraphics.setColor(BG_COLOR);
		offScreenGraphics.fillRect(0, 0, screen.width, screen.height);
		offScreenGraphics.setColor(SCORE_BG);
		offScreenGraphics.fillRect(488, 4, 158, screen.height-8);
		game.drawBoard(offScreenGraphics);
		if (blinker != null && blinker.blink) game.drawHighlight(game.getMove(), offScreenGraphics);
		offScreenGraphics.setColor(Color.WHITE);
		offScreenGraphics.setFont(noMoveFont);
		if (time >= GAME_TIME)
			offScreenGraphics.drawString("GAME OVER", 100, 250);
		else if (game.state == Game.RESTING && game.getMove() < 0)
			offScreenGraphics.drawString("NO MORE MOVES", 10, 250);
		offScreenGraphics.setFont(scoreTitleFont);
		offScreenGraphics.drawString("SCORE:", 510, 40);
		offScreenGraphics.drawString("BEST:", 510, 110);
		offScreenGraphics.drawString("TIME:", 510, 190);
		offScreenGraphics.drawString("HELP:", 510, 350);
		offScreenGraphics.setFont(scoreFont);
		offScreenGraphics.drawString(SCORE_FORMAT.format(game.score), 520, 70);
		offScreenGraphics.drawString(SCORE_FORMAT.format(bestScore), 520, 140);
		offScreenGraphics.fillArc(515, 210, 100, 100, 90, 360-(int)(360*((double)time/GAME_TIME)));
		offScreenGraphics.fillArc(515, 370, 100, 100, 90, 360-(int)(360*((double)thinkCounter/THINK_TIME)));
		g.drawImage(offScreenImage,0,0,null);
		//showStatus(""+(System.currentTimeMillis() - ST));
	}
	
	public void update(Graphics g) {
		paint(g);
	}
	
	public void run() {
		for(;;) {
			time++;
			if (game.state != Game.COLLAPSING)thinkCounter = Math.min(thinkCounter+1, THINK_TIME);
			if (time >= GAME_TIME) {
				repaint();
				pause(5000);
				thinkCounter = time = 0;
				bestScore = Math.max(bestScore, game.score);
				game = new Game(8, 6);
			}
			if (game.state == Game.COLLAPSING && !game.isAnimating()) {
				thinkCounter = 0;
				pause(200);
				game.removeGems();
			} else if (game.state == Game.RESTING && !game.isAnimating() && game.getMove() < 0) {
				game.score = Math.max(0, game.score - 50);
				repaint();
				pause(3000);
				game.randomBoard();
			}
			if (thinkCounter >= THINK_TIME) {
				if (blinker == null) {
					game.score = (int) (game.score*0.9);
					blinker = new MoveBlinker();
					blinkerThread = new Thread(blinker);
					blinkerThread.start();
				}
				if (!blinkerThread.isAlive()) thinkCounter = 0;
			} else blinker = null;
			repaint();
			showStatus("Load: " + (int)(fpsLimiter.delay()*1000)/10.0 +"%");
			//fpsLimiter.delay();
		}
	}
	
	void pause(int ms) {
		try {
			Thread.sleep(ms);
		} catch (InterruptedException ie) {}
	}
	
	public void destroy() {
		gameThread = null;
	}
	
	public void mouseMoved(MouseEvent evt) {} 
	public void mouseDragged(MouseEvent evt) {
		if (game.state == Game.HOLDING) game.slideGem(evt.getX(), evt.getY());
	}
	public void mouseClicked(MouseEvent evt) {}
	public void mouseEntered(MouseEvent evt) {}
	public void mouseExited(MouseEvent evt) {}
	public void mousePressed(MouseEvent evt) {
		if (game.state == Game.RESTING && !game.isAnimating() && game.getMove() >= 0) game.holdGem(evt.getX(), evt.getY());
		else if (game.state == Game.HOLDING) game.dropGem(evt.getX(), evt.getY());
	}
	public void mouseReleased(MouseEvent evt) {}
	
	class MoveBlinker implements Runnable {
		boolean blink;
		
		MoveBlinker() {
			blink = false;
		}
		
		public void run() {
			for (int i = 0; i < 6; i++) {
				blink = !blink;
				try {
					Thread.sleep(250);
				} catch (InterruptedException ie) {}
			}
		}
	}
} 
