import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JOptionPane;

public class BackFromTheKlondikeApplet extends Applet implements ActionListener {

	private int[][] numSteps   = null;
	private int     gridWidth  = 23; 
	private int     gridHeight = 23;
	private Point   currentPos = null;
	private Point	prevPos    = null;
	private	Font	normalFont = new Font("Arial", Font.BOLD, 10);
	private Font	activeFont = new Font("Verdana", Font.PLAIN, 10);

	public static int SQUARE_OUTSIDE_BOARD = 0;
	public static int INVALID_SQUARE       = -1;

	/*private static final int dx[] = { 0, 1, 1, 1, 0, -1, -1, -1 };
	private static final int dy[] = { -1, -1, 0, 1, 1, 1, 0, -1 };*/

	/*private class UntriedDirections
	{
		public final static int NONE      = -1;
		public final static int NORTH     = 0;
		public final static int NORTHEAST = 1;
		public final static int EAST      = 2;
		public final static int SOUTHEAST = 3;
		public final static int SOUTH     = 4;
		public final static int SOUTHWEST = 5;
		public final static int WEST      = 6;
		public final static int NORTHWEST = 7;

		private boolean[] direction = { false, false, false, false,
				false, false, false, false };

		public int pickRandomUntriedDirection()
		{
			int r = (int)(Math.random() * 8);
			for(int i = 0; i < 8; i++)
			{
				int index = (r + i) % 8;
				if(!direction[index])
				{
					direction[index] = true;
					return index;
				}
			}
			return UntriedDirections.NONE;
		}

		public int getXChange(int direction)
		{
			switch(direction)
			{
			case UntriedDirections.NORTH:
			case UntriedDirections.SOUTH:
				return 0;
			case UntriedDirections.NORTHEAST:
			case UntriedDirections.EAST:
			case UntriedDirections.SOUTHEAST:
				return 1;
			default:
				return -1;
			}
		}

		public int getYChange(int direction)
		{
			switch(direction)
			{
			case UntriedDirections.NORTH:
			case UntriedDirections.NORTHEAST:
			case UntriedDirections.NORTHWEST:
				return -1;
			case UntriedDirections.SOUTH:
			case UntriedDirections.SOUTHEAST:
			case UntriedDirections.SOUTHWEST:
				return 1;
			default:
				return 0;
			}
		}
	}*/

	private void createButtons()
	{
		Panel p = new Panel();
		p.setLayout(new GridLayout(3, 3));
		Button b = new Button("Go Northwest"); 
		p.add(b);
		b.addActionListener(this);
		b = new Button("Go North");
		p.add(b);
		b.addActionListener(this);
		b = new Button("Go Northeast");
		p.add(b);
		b.addActionListener(this);
		b = new Button("Go West");
		p.add(b);
		b.addActionListener(this);
		b = new Button("Undo");
		p.add(b);
		b.addActionListener(this);
		b = new Button("Go East");
		p.add(b);
		b.addActionListener(this);
		b = new Button("Go Southwest");
		p.add(b);
		b.addActionListener(this);
		b = new Button("Go South");
		p.add(b);
		b.addActionListener(this);
		b = new Button("Go Southeast");
		p.add(b);
		b.addActionListener(this);
		add(p);
	}

	public void init()
	{
		this.loadOriginalBoard();
		this.createButtons();
		//this.randomiseBoard(8);
	}

	public void loadOriginalBoard()
	{
		this.currentPos = new Point(11, 11);
		this.prevPos    = new Point(11, 11);

	    int tempNumSteps[][] = {
	    		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
				{0,0,0,0,0,0,0,0,0,0,4,2,2,0,0,0,0,0,0,0,0,0,0},
				{0,0,0,0,0,0,0,7,4,4,3,7,5,4,4,7,0,0,0,0,0,0,0},
				{0,0,0,0,0,3,1,2,2,1,5,1,2,3,1,3,1,2,0,0,0,0,0},
				{0,0,0,0,4,7,4,1,6,6,2,5,2,7,6,2,6,5,6,0,0,0,0},
				{0,0,0,1,9,2,7,8,7,5,3,1,6,5,7,6,7,4,5,2,0,0,0},
				{0,0,0,4,4,9,8,5,2,1,2,1,1,1,8,1,3,7,6,3,0,0,0},
				{0,0,5,5,9,8,4,5,5,1,2,3,2,9,3,5,4,8,4,1,7,0,0},
				{0,0,4,1,6,3,2,3,2,1,3,1,4,3,4,3,8,7,6,2,4,0,0},
				{0,0,4,1,7,5,9,1,4,9,2,5,4,4,3,9,1,5,7,3,4,0,0},
				{0,4,8,1,5,6,2,1,2,1,4,3,6,4,4,2,2,6,2,3,5,3,0},
				{0,7,3,4,5,7,7,3,2,4,2,3,3,5,1,3,1,1,5,3,7,3,0},
				{0,7,3,5,5,3,1,1,5,3,5,2,4,2,3,2,2,3,2,2,3,4,0},
				{0,0,4,1,8,9,1,3,4,4,3,4,1,9,1,1,1,5,2,1,4,0,0},
				{0,0,6,7,7,1,8,3,3,4,5,2,2,4,2,5,2,7,6,3,4,0,0},
				{0,0,3,1,6,8,2,4,2,3,1,3,1,1,3,7,2,8,3,2,7,0,0},
				{0,0,0,3,6,7,2,2,8,1,1,7,2,9,2,5,8,7,4,1,0,0,0},
				{0,0,0,5,8,5,7,8,1,9,3,7,6,5,3,8,9,2,7,1,0,0,0},
				{0,0,0,0,5,8,6,6,7,8,5,5,5,7,6,9,4,9,4,0,0,0,0},
				{0,0,0,0,0,5,3,1,7,2,5,4,1,4,2,5,1,3,0,0,0,0,0},
				{0,0,0,0,0,0,0,3,3,7,3,2,8,8,4,4,0,0,0,0,0,0,0},
				{0,0,0,0,0,0,0,0,0,0,7,7,8,0,0,0,0,0,0,0,0,0,0},
				{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		};
		this.numSteps = tempNumSteps;

		this.gridWidth  = this.numSteps.length;
		this.gridHeight = this.numSteps[0].length;
	}

	/*public void randomiseBoard(int numSteps)
	{
		final Point centreSquare = new Point((int)(this.gridWidth / 2),
				(int)(this.gridHeight / 2));
		this.currentPos = centreSquare;
		this.prevPos    = centreSquare;

		this.numSteps = new int[this.gridWidth][this.gridHeight];

		UntriedDirections untriedDirections[][] =
				new UntriedDirections[this.gridWidth][this.gridHeight];

		this.numSteps[centreSquare.x][centreSquare.y] = 2;
		// Pick a random starting location one square off the board
		Point currentPos = new Point((int)(Math.random() * this.gridWidth),
									 (int)(Math.random() * this.gridHeight));

		untriedDirections[currentPos.x][currentPos.y] = new UntriedDirections();

		// Pick a random direction
		int direction = untriedDirections[currentPos.x][currentPos.y].pickRandomUntriedDirection();

		// Travel in this direction until the edge of the board is reached
		int tempX = currentPos.x,
			tempY = currentPos.y;

		while(tempX >= 0 && tempX < this.gridWidth && tempY >= 0 && tempY < this.gridHeight)
		{
			tempX += untriedDirections[currentPos.x][currentPos.y].getXChange(direction);
			tempY += untriedDirections[currentPos.x][currentPos.y].getYChange(direction);
			this.numSteps[currentPos.x][currentPos.y]++;
		}

		// Pick a random direction in which it is possible to travel
		// at least one square without leaving the board and without
		// ending on either a square that has already been assigned
		// a number or is the centre square

		// Pick a direction in which there is at least one square
		// that meets the following criteria:
		// 1. The square has not already been given a number of steps
		// 2. If the required number of squares have not yet been
		//      added then there is at least one empty square on the
		//      same row or column as the centre square 

	}*/

	public void paint(Graphics g)
	{
		for(int i = 0; i < this.gridWidth; i++)
		{
			for(int j = 0; j < this.gridHeight; j++)
			{
				if(this.isWinningSquare(i, j))
				{
					g.setColor(Color.GREEN);
					g.fillRect(i * 20 + 5, j * 20 + 80, 19, 19);
					continue;
				}
				else if(numSteps[i][j] == BackFromTheKlondikeApplet.SQUARE_OUTSIDE_BOARD)
				{
					continue;
				}

				if(i == this.currentPos.x && j == this.currentPos.y)
				{
					g.setColor(Color.WHITE);
					g.fillRect(i * 20 + 5, j * 20 + 80, 19, 19);
					g.setColor(Color.RED);
					g.fillOval(i * 20 + 5, j * 20 + 80, 19, 19);
					g.setColor(Color.BLACK);
					g.setFont(this.activeFont);
				}
				else
				{
					boolean isOddSquare = ((i + j) % 2) == 1;
					g.setColor(isOddSquare ? Color.BLACK : Color.WHITE);
					g.fillRect(i * 20 + 5, j * 20 + 80, 19, 19);
					g.setFont(this.normalFont);
					g.setColor(!isOddSquare ? Color.BLACK : Color.WHITE);
				}

				g.drawString("" + numSteps[i][j], i * 20 + 12, j * 20 + 93);
			}
		}
	}

	/**
	 * Returns the number of steps to be taken from
	 * a given square
	 * @param x x co-ordinate of the square to examine
	 * @param y y co-ordinate of the square to examine
	 * @return the number of steps to be taken from this
	 * square, or INVALID_SQUARE if the (x, y) is not a
	 * valid array index
	 */
	public int getNumSteps(int x, int y)
	{
		try
		{
			return numSteps[x][y];
		}
		catch(ArrayIndexOutOfBoundsException e)
		{
			return BackFromTheKlondikeApplet.INVALID_SQUARE;
		}
	}

	/**
	 * Determines whether a given square is exactly one
	 * square outside the border.
	 * @param x x co-ordinate of the square to examine
	 * @param y y co-ordinate of the square to examine
	 * @return true if the square (x, y) is not inside
	 * the board but is adjacent to a square that is;
	 * otherwise false 
	 */
	public boolean isWinningSquare(int x, int y)
	{
		if(this.getNumSteps(x, y) != BackFromTheKlondikeApplet.SQUARE_OUTSIDE_BOARD)
		{
			return false;
		}
		// TODO: Determine whether the square is adjacent to
		// a square inside the board
		if(this.getNumSteps(x - 1, y - 1) >= 1 ||
				this.getNumSteps(x - 1, y) >= 1 ||
				this.getNumSteps(x - 1, y + 1) >= 1 ||
				this.getNumSteps(x, y - 1) >= 1 ||
				this.getNumSteps(x, y + 1) >= 1 ||
				this.getNumSteps(x + 1, y - 1) >= 1 ||
				this.getNumSteps(x + 1, y) >= 1 ||
				this.getNumSteps(x + 1, y + 1) >= 1)
		{
			return true;
		}
		return false;
	}

	public void actionPerformed(ActionEvent e)
	{
		String buttonLabel = e.getActionCommand();
		if(buttonLabel.equals("Undo"))
		{
			this.currentPos.x = this.prevPos.x;
			this.currentPos.y = this.prevPos.y;
			this.repaint();
			return;
		}

		int    numSteps    = this.numSteps[this.currentPos.x][this.currentPos.y];
		Point  newPos      = new Point(this.currentPos.x, this.currentPos.y);

		if(buttonLabel.equals
				("Go North") ||
				buttonLabel.equals("Go Northwest") ||
				buttonLabel.equals("Go Northeast"))
		{
			newPos.y -= numSteps;
		}
		else if(buttonLabel.equals("Go South") ||
				buttonLabel.equals("Go Southwest") ||
				buttonLabel.equals("Go Southeast"))
		{
			newPos.y += numSteps;
		}
		if(buttonLabel.equals("Go West") ||
				buttonLabel.equals("Go Northwest") ||
				buttonLabel.equals("Go Southwest"))
		{
			newPos.x -= numSteps;
		}
		else if(buttonLabel.equals("Go East") ||
				buttonLabel.equals("Go Northeast") ||
				buttonLabel.equals("Go Southeast"))
		{
			newPos.x += numSteps;
		}

		int newNumSteps = this.getNumSteps(newPos.x, newPos.y); 
		if(newNumSteps != BackFromTheKlondikeApplet.INVALID_SQUARE)
		{
			this.prevPos    = this.currentPos;
			this.currentPos = newPos;
			this.repaint();

			if(this.isWinningSquare(newPos.x, newPos.y))
			{
				JOptionPane.showMessageDialog(null, "Well done");
			}
		}		
	}
}
