/*
 * 
 * 		Minesweeper, by Elliot Walmsley, 16/07/2009
 * 
 */

import java.applet.AudioClip;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;

public class Minesweeper extends JApplet implements Runnable {
	
	private static final int GRIDSTARTY = 40;
	private static final int GRIDPADDING = 20;
	private static final int GRIDWIDTH = 20;
	private static final int GRIDHEIGHT = 20;
	private static final int BOXSIZE = 18;
	private static final int BOXMARGIN = 3;
	private static final int MINENUMBER = (int) (GRIDWIDTH * GRIDWIDTH * 0.15); // 0.15
	private static final boolean DEBUG = false;
	
	private static final Color BACKGROUND = Color.black;
	private static final Color COVERED = Color.yellow;
	private static final Color MAINTEXTCOLOUR = Color.yellow;
	private static Color BOXTEXTCOLOUR = Color.black;
	private static final Color BLANK = new Color(255, 255, 255);
	private static final Color MINENUMBERBOX = new Color(255, 255, 255);
	private static final Color MINEBOX = new Color(100, 255, 50);
	private static final Color FLAGBOX = new Color(200, 255, 0);
	
    
	int[][] mines = new int[GRIDWIDTH][GRIDHEIGHT];
	int[][] topLayer = new int[GRIDWIDTH][GRIDHEIGHT];
	
	/*
	    mines:

		0   	BLANK
		1-8		MINE NUMBER
		9		MINE
	
		topLayer:
	
		0		NOT YET UNCOVERED
		1		UNCOVERED
		2		FLAG
		
	 */
	
    private Thread thread;
    private BufferedImage bimg;

    private boolean showInfo;
    
    MouseInput1 mouse; // Mouse Polling
    KeyboardInput keyboard; // Keyboard Polling
    Random random = new Random(); // Random
    
	private Point box = new Point();
	private boolean showMines;
	private int time;
	private int timeStart;
	private int mode;
	private boolean firstMove;
	
	/*
	 	Mode
		
		0 	Nothing
		1	Start
		2	Win
		3	Hit mine
		
	 */
	
	BufferedImage flag = null;
	BufferedImage mine = null;
	
	Font mainFont = new Font("Courier New", Font.BOLD, 34);
	Font subFont = new Font("Courier New", Font.BOLD, 15);
	Font mineNumberFont = new Font("Courier New", Font.BOLD, 18);
	
	AudioClip flagSpawn, mineExplode, mineSearch, win;
    
    private void setupMines() {

    	int[][] mineNumbers = new int[GRIDWIDTH][GRIDHEIGHT];
    	
        // Add some Mines
        for (int i = 0; i < MINENUMBER; i++) {
        	mines[random.nextInt(GRIDWIDTH)][random.nextInt(GRIDHEIGHT)] = 9;
        }
    	
        // Get Mine Numbers
    	for (int j = 0; j < GRIDHEIGHT; j++) {
    		for (int i = 0; i < GRIDWIDTH; i++) {
				if (mines[i][j] == 9) {
					// top row
					if (j > 0) {
							mineNumbers[i][j-1] += 1;
						if (i > 0) {	
							mineNumbers[i-1][j-1] += 1;
						}
						if (i < (GRIDWIDTH - 1)) {
							mineNumbers[i+1][j-1] += 1;
						}
					}
					// bottom row
					if (j < (GRIDHEIGHT - 1)) {
						mineNumbers[i][j+1] += 1;
						if (i > 0) {	
							mineNumbers[i-1][j+1] += 1;
						}
						if (i < (GRIDWIDTH - 1)) {
							mineNumbers[i+1][j+1] += 1;
						}
					}
					// left
					if (i > 0) {
						mineNumbers[i-1][j] += 1;
					}
					// right
					if (i < (GRIDWIDTH - 1)) {
						mineNumbers[i+1][j] += 1;
					}
				}
    		}
    	}
    	
    	// Add MineNumbers to Mines array
    	for (int j = 0; j < GRIDHEIGHT; j++) {
    		for (int i = 0; i < GRIDWIDTH; i++) {
    			if (mines[i][j] != 9) {
    				mines[i][j] = mineNumbers[i][j];
    			}
    		}
    	}
    	
	}

    public void clearArea(Point p) {
    	ArrayList<Point> stack = new ArrayList<Point>();
    	Point p2, p1;
    	p2 = new Point();
    	p1 = new Point();
    	int deletePoint;
    	int[][] checked = new int[GRIDWIDTH][GRIDHEIGHT];
    	boolean first = true;
    	
    	stack.add(p); // Add chosen box to stack
    	
    	// Get all the boxes onto the stack
    	
    	while (stack.size() > 0) {
    		
    		p1 = new Point();
    		p1.x = stack.get(stack.size() - 1).x;
    		p1.y = stack.get(stack.size() - 1).y;
    		deletePoint = stack.size() - 1;
	    	
	    	// Display the current box
    		topLayer[p1.x][p1.y] = 1;
	    	
	    	// Make sure it doesn't get checked again
	    	checked[p1.x][p1.y] = 1;
	    	
	    	if (DEBUG) {
		    	System.out.println("Checking around x: " + p1.x + " y: " + p1.y);
		    	System.out.println("stack size: " + stack.size());
	    	}
	    	
	    	// Continue to reveal the map if the first box was blank, or a number
	    	if (mines[p.x][p.y] != 9 && first) {
	    		
	    		first = false;
	    		
	    		if (p1.y > 0) {
		    		p2 = new Point();
		    		p2.x = p1.x;
					p2.y = p1.y;
					p2.y = p2.y - 1;
					if (DEBUG) {
						System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
					}
					
					if (checked[p2.x][p2.y] == 0 && mines[p2.x][p2.y] == 0) {
						stack.add((Point) p2);
						checked[p2.x][p2.y] = 1;
						if (DEBUG) {
							System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
						}
					}
					
					// upleft
					if (p1.x > 0) {
						p2 = new Point();
			    		p2.x = p1.x;
						p2.y = p1.y;
						p2.y = p2.y - 1;
						p2.x = p2.x - 1;
						if (DEBUG) {
							System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
						}
						if (checked[p2.x][p2.y] == 0 && mines[p2.x][p2.y] == 0) {
							stack.add((Point) p2);
							checked[p2.x][p2.y] = 1;
							if (DEBUG) {
								System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
							}
						}
					}
					
					// upright
					if (p1.x < (GRIDWIDTH - 1)) {
						p2 = new Point();
			    		p2.x = p1.x;
						p2.y = p1.y;
						p2.y = p2.y - 1;
						p2.x = p2.x + 1;
						if (DEBUG) {
							System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
						}
						if (checked[p2.x][p2.y] == 0 && mines[p2.x][p2.y] == 0) {
							stack.add((Point) p2);
							checked[p2.x][p2.y] = 1;
							if (DEBUG) {
								System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
							}
						}
					}
					
				}
				if (p1.x > 0) {
					p2 = new Point();
					p2.x = p1.x;
					p2.y = p1.y;
					p2.x = p2.x - 1;
					if (DEBUG) {
						System.out.println("Checking left x: " + p2.x + " y: " + p2.y);
					}
					if (checked[p2.x][p2.y] == 0 && mines[p2.x][p2.y] == 0) {
						stack.add((Point) p2);
						checked[p2.x][p2.y] = 1;
						if (DEBUG) {
							System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
						}
					}
				}
				if (p1.y < (GRIDHEIGHT - 1)) {
					p2 = new Point();
					p2.x = p1.x;
					p2.y = p1.y;
					p2.y = p2.y + 1;
					if (DEBUG) {
						System.out.println("Checking below x: " + p2.x + " y: " + p2.y);
					}
					if (checked[p2.x][p2.y] == 0 && mines[p2.x][p2.y] == 0) {
						stack.add((Point) p2);
						checked[p2.x][p2.y] = 1;
						if (DEBUG) {
							System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
						}
					}
					
					// BOTTOMleft
					if (p1.x > 0) {
						p2 = new Point();
			    		p2.x = p1.x;
						p2.y = p1.y;
						p2.y = p2.y + 1;
						p2.x = p2.x - 1;
						if (DEBUG) {
							System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
						}
						if (checked[p2.x][p2.y] == 0 && mines[p2.x][p2.y] == 0) {
							stack.add((Point) p2);
							checked[p2.x][p2.y] = 1;
							if (DEBUG) {
								System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
							}
						}
					}
					
					// BOTTOMright
					if (p1.x < (GRIDWIDTH - 1)) {
						p2 = new Point();
			    		p2.x = p1.x;
						p2.y = p1.y;
						p2.y = p2.y + 1;
						p2.x = p2.x + 1;
						if (DEBUG) {
							System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
						}
						if (checked[p2.x][p2.y] == 0 && mines[p2.x][p2.y] == 0) {
							stack.add((Point) p2);
							checked[p2.x][p2.y] = 1;
							if (DEBUG) {
								System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
							}
						}
					}
					
					
				}
				if (p1.x < (GRIDWIDTH - 1)) {
					p2 = new Point();
					p2.x = p1.x;
					p2.y = p1.y;
					p2.x = p2.x + 1;
					if (DEBUG) {
						System.out.println("Checking right x: " + p2.x + " y: " + p2.y);
					}
					if (checked[p2.x][p2.y] == 0 && mines[p2.x][p2.y] == 0) {
						stack.add((Point) p2);
						checked[p2.x][p2.y] = 1;
						if (DEBUG) {
							System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
						}
					}
				}
				
	    	}
	    	
	    	// Continue to reveal the map if the current box is blank
	    	if (mines[p1.x][p1.y] == 0) { // != 9
	    		
		    	if (p1.y > 0) {
		    		p2 = new Point();
		    		p2.x = p1.x;
					p2.y = p1.y;
					p2.y = p2.y - 1;
					if (DEBUG) {
						System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
					}
					
					if (checked[p2.x][p2.y] == 0) {
						stack.add((Point) p2);
						checked[p2.x][p2.y] = 1;
						if (DEBUG) {
							System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
						}
					}
					
					// upleft
					if (p1.x > 0) {
						p2 = new Point();
			    		p2.x = p1.x;
						p2.y = p1.y;
						p2.y = p2.y - 1;
						p2.x = p2.x - 1;
						if (DEBUG) {
							System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
						}
						if (checked[p2.x][p2.y] == 0) {
							stack.add((Point) p2);
							checked[p2.x][p2.y] = 1;
							if (DEBUG) {
								System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
							}
						}
					}
					
					// upright
					if (p1.x < (GRIDWIDTH - 1)) {
						p2 = new Point();
			    		p2.x = p1.x;
						p2.y = p1.y;
						p2.y = p2.y - 1;
						p2.x = p2.x + 1;
						if (DEBUG) {
							System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
						}
						if (checked[p2.x][p2.y] == 0) {
							stack.add((Point) p2);
							checked[p2.x][p2.y] = 1;
							if (DEBUG) {
								System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
							}
						}
					}
					
				}
				if (p1.x > 0) {
					p2 = new Point();
					p2.x = p1.x;
					p2.y = p1.y;
					p2.x = p2.x - 1;
					if (DEBUG) {
						System.out.println("Checking left x: " + p2.x + " y: " + p2.y);
					}
					if (checked[p2.x][p2.y] == 0) {
						stack.add((Point) p2);
						checked[p2.x][p2.y] = 1;
						if (DEBUG) {
							System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
						}
					}
				}
				if (p1.y < (GRIDHEIGHT - 1)) {
					p2 = new Point();
					p2.x = p1.x;
					p2.y = p1.y;
					p2.y = p2.y + 1;
					if (DEBUG) {
						System.out.println("Checking below x: " + p2.x + " y: " + p2.y);
					}
					if (checked[p2.x][p2.y] == 0) {
						stack.add((Point) p2);
						checked[p2.x][p2.y] = 1;
						if (DEBUG) {
							System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
						}
					}
					
					// BOTTOMleft
					if (p1.x > 0) {
						p2 = new Point();
			    		p2.x = p1.x;
						p2.y = p1.y;
						p2.y = p2.y + 1;
						p2.x = p2.x - 1;
						if (DEBUG) {
							System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
						}
						if (checked[p2.x][p2.y] == 0) {
							stack.add((Point) p2);
							checked[p2.x][p2.y] = 1;
							if (DEBUG) {
								System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
							}
						}
					}
					
					// BOTTOMright
					if (p1.x < (GRIDWIDTH - 1)) {
						p2 = new Point();
			    		p2.x = p1.x;
						p2.y = p1.y;
						p2.y = p2.y + 1;
						p2.x = p2.x + 1;
						if (DEBUG) {
							System.out.println("Checking ontop x: " + p2.x + " y: " + p2.y);
						}
						if (checked[p2.x][p2.y] == 0) {
							stack.add((Point) p2);
							checked[p2.x][p2.y] = 1;
							if (DEBUG) {
								System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
							}
						}
					}
					
					
				}
				if (p1.x < (GRIDWIDTH - 1)) {
					p2 = new Point();
					p2.x = p1.x;
					p2.y = p1.y;
					p2.x = p2.x + 1;
					if (DEBUG) {
						System.out.println("Checking right x: " + p2.x + " y: " + p2.y);
					}
					if (checked[p2.x][p2.y] == 0) {
						stack.add((Point) p2);
						checked[p2.x][p2.y] = 1;
						if (DEBUG) {
							System.out.println("Adding to stack: x: " + p2.x + " y: " + p2.y);
						}
					}
				}
	    	}
	    	
	    	stack.remove(deletePoint);
	    	
	    	for (int i = 0; i < (stack.size()); i++) {
	    		if (DEBUG) {
					System.out.println("> " + i + ": " + stack.get(i));
	    		}
	    	}
	    	
    	}
    	
    }
    
    public Point coordToGrid(Point coord) {
    	Dimension d = getSize();
    	Point gridPoint = new Point();
    	
    	gridPoint.x = (coord.x - GRIDPADDING) / (BOXSIZE + BOXMARGIN);
    	gridPoint.y = (coord.y - GRIDSTARTY) / (BOXSIZE + BOXMARGIN);
    	
    	return gridPoint;
    }

    private void restartGame() {

    	firstMove = true;
		showMines = false;
		time = 0;
		mode = 0;
		
		for (int i = 0; i < GRIDHEIGHT; i++) {
			for (int j = 0; j < GRIDWIDTH; j++) {
				mines[j][i] = 0;
				topLayer[j][i] = 0;
			}
		}
		
		setupMines();
		
	}
    
	public void init() {
		
		// Add key listeners
        keyboard = new KeyboardInput();
        addKeyListener(keyboard);
    		
        // Add mouse listeners
        mouse = new MouseInput1();
        addMouseListener( mouse );
        addMouseMotionListener( mouse );
		
		firstMove = true;
		showMines = false;
		time = 0;
		mode = 0;
		
        setBackground(BACKGROUND);
        
        mineSearch = getAudioClip(getCodeBase(), "bleep.au");
        mineExplode = getAudioClip(getCodeBase(), "bomb2.au");
        flagSpawn = getAudioClip(getCodeBase(), "bomb.au");
        win = getAudioClip(getCodeBase(), "win.au");

        try {
        		URL url = new URL(getCodeBase(), "flag.png");
	        	flag = ImageIO.read(url);
	        	url = new URL(getCodeBase(), "mine.png");
	        	mine = ImageIO.read(url);
            
	        } catch (IOException e) {
        }
        
        setupMines();
        
    }
    
    public void step(int w, int h) {
    	
    	/*
    	 * 
    	 * KEYBOARD/MOUSE INPUT
    	 * 
    	 */
    	
    	keyboard.poll();
    	mouse.poll();
    	
    	if (mouse.buttonDownOnce(1)) {
    		if (mode == 0 | mode == 1) {
	    	      box = coordToGrid(mouse.getPosition());
	    	      if (box.x >= 0 && box.x < GRIDWIDTH && box.y >= 0 && box.y < GRIDHEIGHT) {
	    	    	  if (firstMove) { 
	    	    		  firstMove = false;
	    	    		  mode = 1;
	    	    		  timeStart = (int) (System.currentTimeMillis() / 1000);
	    	    		  if (mines[box.x][box.y] != 9) {
	    	    			  mineSearch.play();
	    	    			  clearArea(box);
	    	    			  checkForWin();
	    	    		  } else {
	    	    			  mineExplode.play();
	    	    			  showMines = true;
	    	    			  mode = 3;
	    	    		  }
	    	    	  } else {
	    	    		  if (topLayer[box.x][box.y] == 0) {
			    	    	  if (mines[box.x][box.y] != 9) {
			    	    		  mineSearch.play();
			    	    		  clearArea(box);
			    	    		  checkForWin();
			    	    	  } else {
			    	    		  mineExplode.play();
			    	    		  showMines = true;
			    	    		  mode = 3;
			    	    	  }
	    	    		  }
	    	    	  }
	    	      }
    		} else {
    			restartGame();
    		}
    	}
    	
    	if (mouse.buttonDownOnce(3)) {
    		if (mode == 1) {
	    		Point loc = new Point();
	    		loc = coordToGrid(mouse.getPosition());
	    		
	    		if (loc.x >= 0 && loc.x < GRIDWIDTH && loc.y >= 0 && loc.y < GRIDHEIGHT) {
	    			if (topLayer[loc.x][loc.y] == 0) {
	    				flagSpawn.play();
	    				topLayer[loc.x][loc.y] = 2;
	    			} else if (topLayer[loc.x][loc.y] == 2) {
	    				flagSpawn.play();
	    				topLayer[loc.x][loc.y] = 0;
	    			} else {
	    				System.out.println("Can't flag here.");
	    			}
	    		}
    		}
    	}
    	
    	if (keyboard.keyDown(KeyEvent.VK_I)) {
    		showInfo = true;
    	} else {
    		showInfo = false;
    	}
        
    }

	private void checkForWin() {
		int count = 0;
		for (int i = 0; i < GRIDHEIGHT; i++) {
			for (int j = 0; j < GRIDWIDTH; j++) {
				// Count the number of correct flags
				if (topLayer[j][i] == 2 & mines[j][i] == 9) { 
					count ++;
				}
				// Count the not-yet uncovered
				if (topLayer[j][i] == 0) {
					count++;
				}
			}
		}
		if (count == MINENUMBER) { // Win
			
			win.play();
			
			mode = 2;
			
			// Fill blanks with mineNumbers and Flags
			for (int i = 0; i < GRIDHEIGHT; i++) {
				for (int j = 0; j < GRIDWIDTH; j++) {
					if (topLayer[j][i] == 0) { 
						topLayer[j][i] = 1;
					}
					if (topLayer[j][i] == 9) {
						topLayer[j][i] = 2;
					}
				}
			}
			
			if (DEBUG) {
				System.out.println("Win.");
			}
			
		} else { // Normal Click
			mineSearch.play();
		}
		
	}

	public void drawDemo(int w, int h, Graphics2D g2) {

    	// draw board
    	g2.setColor(Color.yellow);
    	for (int i = 0; i < GRIDHEIGHT; i++) {
    		for (int j = 0; j < GRIDWIDTH; j++) {
    			
    			if (topLayer[j][i] == 0) { // Covered
    				g2.setColor(COVERED);
    				g2.fillRect(GRIDPADDING + j * BOXSIZE + j * BOXMARGIN, 
     					   GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN, 
     					   BOXSIZE, 
     					   BOXSIZE);
    			}
    			if (topLayer[j][i] == 1) { // Uncovered
    				if (mines[j][i] == 0) { // Blank
	    				g2.setColor(BLANK);
	    				g2.fillRect(GRIDPADDING + j * BOXSIZE + j * BOXMARGIN, 
	     					   GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN, 
	     					   BOXSIZE, 
	     					   BOXSIZE);
    				}
    				
    				if (mines[j][i] != 0 && mines[j][i] != 9) { // Number
	    				g2.setColor(MINENUMBERBOX);
	    				g2.fillRect(GRIDPADDING + j * BOXSIZE + j * BOXMARGIN, 
	     					   GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN, 
	     					   BOXSIZE, 
	     					   BOXSIZE);
	    				
	    				switch (mines[j][i]) {
	    					case 1:
	    						BOXTEXTCOLOUR = new Color(50, 50, 255);
	    					break;
	    					case 2:
		    					BOXTEXTCOLOUR = new Color(200, 200, 50);
	    					break;
	    					case 3:
		    					BOXTEXTCOLOUR = new Color(50, 255, 50);
	    					break;
	    					case 4:
    							BOXTEXTCOLOUR = new Color(100, 100, 255);
	    					break;
	    					case 5:
    							BOXTEXTCOLOUR = new Color(255, 150, 150);
	    					break;
	    					case 6:
    							BOXTEXTCOLOUR = new Color(100, 255, 255);
	    					break;
	    					case 7:
								BOXTEXTCOLOUR = new Color(255, 150, 50);
	    					break;
	    					case 8:
								BOXTEXTCOLOUR = new Color(50, 50, 255);
	    					break;
	    				}
	    				
	    				g2.setColor(BOXTEXTCOLOUR);
	    				g2.setFont(mineNumberFont);
	    				
	    				g2.drawString("" + mines[j][i], 
	    						 	 GRIDPADDING + j * BOXSIZE + j * BOXMARGIN + BOXSIZE/2 - 4, 
	    						 	 GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN + BOXSIZE/2 + 5);
    				}
    				
    				if (mines[j][i] == 9 && mode == 2) { // Mine
    					g2.setColor(MINEBOX);
	    				g2.fillRect(GRIDPADDING + j * BOXSIZE + j * BOXMARGIN, 
	     					   GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN, 
	     					   BOXSIZE, 
	     					   BOXSIZE);
    					g2.drawImage(mine,
    							GRIDPADDING + j * BOXSIZE + j * BOXMARGIN + BOXSIZE/2 - mine.getWidth()/2,
    							GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN + BOXSIZE/2 - mine.getHeight()/2,
    							null);
    				}
    			}
    			
    			if (topLayer[j][i] == 2) { // Flagged
    				g2.setColor(FLAGBOX);
    				g2.fillRect(GRIDPADDING + j * BOXSIZE + j * BOXMARGIN, 
     					   GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN, 
     					   BOXSIZE, 
     					   BOXSIZE);
    				g2.drawImage(flag,
    							GRIDPADDING + j * BOXSIZE + j * BOXMARGIN + BOXSIZE/2 - flag.getWidth()/2,
    							GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN + BOXSIZE/2 - flag.getHeight()/2,
    							null);
    			}
    			
    			if (showMines) {
    				if (mines[j][i] == 9 & topLayer[j][i] != 2) { // Draw Mines
    					g2.setColor(MINEBOX);
    					g2.fillRect(GRIDPADDING + j * BOXSIZE + j * BOXMARGIN, 
 	     					   GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN, 
 	     					   BOXSIZE, 
 	     					   BOXSIZE);
    					g2.drawImage(mine,
    							GRIDPADDING + j * BOXSIZE + j * BOXMARGIN + BOXSIZE/2 - mine.getWidth()/2,
    							GRIDSTARTY + i * BOXSIZE + i * BOXMARGIN + BOXSIZE/2 - mine.getHeight()/2,
    							null);
    				}
    	        }
    		}
    	}
    	
    	int mx = coordToGrid(mouse.getPosition()).x;
		int my = coordToGrid(mouse.getPosition()).y;
		
		g2.setColor(Color.black);
		if (mx >= 0 && mx < GRIDWIDTH && my >= 0 && my < GRIDHEIGHT) {
			g2.drawRect(GRIDPADDING + mx * BOXSIZE + mx * BOXMARGIN, 
					   GRIDSTARTY + my * BOXSIZE + my * BOXMARGIN, 
					   BOXSIZE - 1, 
					   BOXSIZE - 1);
		}
    	
		
        // draw strings
		
		g2.setFont(mainFont);
		g2.setColor(MAINTEXTCOLOUR);
		FontMetrics met = g2.getFontMetrics(mainFont);
		if (mode == 1) {
			time = (int) (System.currentTimeMillis() / 1000);
			g2.drawString("" + (time - timeStart), 15, 30);
		} else if (mode == 0) {
			g2.drawString(":)", 15, 30);
		} else if (mode == 2) {
			g2.drawString(":D", 15, 30);
		} else if (mode == 3) {
			g2.drawString(":(", 15, 30);
		}
		String str = "Minesweeper";
		g2.drawString(str, (w / 2) - (met.stringWidth(str))/2, 30);
		if (DEBUG) {
	    	g2.drawString("x: " + mouse.getPosition().x + " y: " + mouse.getPosition().y , 15, 15);
	        g2.drawString("x: " + box.x + " y: " + box.y, 15, 30);
		}
        g2.drawString("i", w - 45, 30);
        
        g2.setFont(subFont);
        if (showInfo) {
        	g2.setColor(Color.yellow);
        	g2.fillRect(25, 65, 360, 80);
        	g2.setColor(Color.black);
        	g2.drawRect(25, 65, 360, 80);
        	g2.drawString("Minesweeper, written in Java", 40, 80);
        	g2.drawString("by Walmsley, 14/07/09.", 40, 100);
        	g2.drawString("Please visit: www.crustycabbage.com", 40, 120);
        	g2.drawString("for Source Code.", 40, 140);
        	
        }
    }

    public Graphics2D createGraphics2D(int w, int h) {
        Graphics2D g2 = null;
        if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
            bimg = (BufferedImage) createImage(w, h);
        } 
        g2 = bimg.createGraphics();
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, w, h);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        return g2;
    }

    public void paint(Graphics g) {
        Dimension d = getSize();
        step(d.width, d.height);
        Graphics2D g2 = createGraphics2D(d.width, d.height);
        drawDemo(d.width, d.height, g2);
        g2.dispose();
        g.drawImage(bimg, 0, 0, this);
    }

	public void start() {
        thread = new Thread(this);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    public synchronized void stop() {
        thread = null;
    }

    public void run() {
    	
        Thread me = Thread.currentThread();
        while (thread == me) {
            repaint();
            try {
                thread.sleep(5);
            } catch (InterruptedException e) { break; }
        }
        thread = null;
    }
    
}
