
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Dimension;
import java.util.Random;
import java.awt.event.MouseEvent;

public class SlidePuzzle extends Applet {

  int columns = 0;
  int rows    = 0;
  int[] puzzleData;
  
  protected final int parseSize(String sizeStr)
  {
    int v;
    v = Integer.parseInt(sizeStr);
    if (v <= 1)
      v = 2;
    else if (v > 10)
      v = 10;
    return v;
  }

  public void init() {
    int x = 4;
    int y = 4;
    String param;
    param = getParameter("size");

    if (param != null) {
      x = parseSize(param);
      y = x;
    }
    param = getParameter("columns");
    if (param != null)
      x = parseSize(param);
    param = getParameter("rows");
    if (param != null)
      y = parseSize(param);
    initPuzzle(x, y);
    // "subscribe" to mouseclicks
    enableEvents(MouseEvent.MOUSE_CLICKED);
  }

  // Initialize puzzle to given matrix dimensions
  public void initPuzzle(int x, int y)
  {
    int size = x*y;
    int i;
    Random rnd = new Random();

    columns = x;
    rows = y;
    // Allocatie out data storage (array of int)
    puzzleData = new int[size];
    // put the numbers into the cells
    for (i=0; i < size-1; i++)
      puzzleData[i] = i+1;
    // The '0' cell is the "blank" cell
    puzzleData[size-1] = 0;
    // Shuffle the tiles.
    for (i=0; i < 2*size; i++)
      exchange(Math.abs(rnd.nextInt()) % size, Math.abs(rnd.nextInt()) % size);
  }

  // Paints the applet
  public void paint(Graphics g) {
    Dimension b = getSize();
    int i;
    int size = columns * rows;
    int w = b.width / columns;
    int h = b.height / rows;
    for (int y=0; y<rows; y++) {
      for (int x=0; x<columns; x++) {
	i = puzzleData[x + (columns * y)];
	if (i > 0) {
	  g.setColor(Color.blue);
	  g.fillRect(x * w, y * h, w - 2, h - 2);
	  g.setColor(Color.white);
	  g.drawString("" + i, x*w+1, (y+1)*h - 8);
	}
      }
    }
  }

  // Swap two cells at given coordinates (offset into puzzleData array)
  public void exchange(int i, int j)
    throws ArrayIndexOutOfBoundsException
  {
    int m = puzzleData[i];
    puzzleData[i] = puzzleData[j];
    puzzleData[j] = m;
  }

  // Returns cell value at x,y
  public int dataAt(int x, int y) {
    return puzzleData[x + (y*rows)];
  }

  // returns index into puzzleData array for given coordinates
  public int indexOf(int x, int y) {
    return (x + (y*rows));
  }

  // Handles mouse events
  protected void processMouseEvent(MouseEvent e) {
    if (e.getID() == MouseEvent.MOUSE_CLICKED) {
      hasClickedAt(e.getX(), e.getY());
    }
  }
  
  // The real mous click handler.
  protected void hasClickedAt(int x, int y) {
    Dimension b = getSize();
    int i, t;
    int size = columns * rows;
    int atX = columns * x / b.width;
    int atY = rows * y / b.height;

    // Where did we click
    i = atX + (columns * atY);
    t = -1;
    // Find the blank neighbor
    if (puzzleData[i] != 0) {
      if ((atX > 0) && (dataAt(atX-1, atY) == 0)) t = indexOf(atX-1, atY);
      else if ((atY > 0) && (dataAt(atX, atY-1) == 0)) t = indexOf(atX, atY-1);
      else if ((atX < columns-1) && (dataAt(atX+1, atY) == 0)) t = indexOf(atX+1, atY);
      else if ((atY < rows-1) && (dataAt(atX, atY+1) == 0)) t = indexOf(atX, atY+1);
      
      // If we found a blank neigbor, move the clicked tile over there.
      if (t >= 0) {
	exchange(i,t);
	repaint();
      }
    }
  }

  public String getAppletInfo()
  {
    return "SlidePuzzle - (c)1998 by Mike Looijmans (my first applet)";
  }

  public String[][] getParameterInfo()
  {
    String[][] pinfo = {
	{"size",      "int", 	"sides of the puzzle"},
	{"columns",    "int", 	"x-dimension (overrides sides)"},
	{"rows",       "int",	"y-dimension (overrides sides)"}
    };
    return pinfo;    
  }
}

