import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Hako extends java.applet.Applet implements ActionListener, Runnable {
	BoardCanvas bc;
	BoardList bl;

	Button b1, b2, b3;

	TextArea ta;

	Thread th;

	public void init() {
		setLayout(new GridLayout(1, 2));

		bc = new BoardCanvas();
		bc.setBoard(newBoard());
		add(bc);

		Panel p1= new Panel(new GridLayout(1, 2));

		Panel p2= new Panel();

		p2.add(new Label("Daughter"));
		p2.add(new Label("in the Box"));

		b1 = new Button("Init");
		b1.addActionListener(this);
		p2.add(b1);
		b2 = new Button("Solve");
		b2.addActionListener(this);
		p2.add(b2);
		b3 = new Button("Next");
		b3.addActionListener(this);
		p2.add(b3);

		p1.add(p2);

		ta = new TextArea();
		ta.setEditable(false);
		p1.add(ta);

		add(p1);
	}

	public Board newBoard() {
		Board b = new Board(4, 5);

		b.add(new Koma(Color.red, 2, 2, 1, 0));
		b.add(new Koma(Color.blue, 1, 2, 0, 0));
		b.add(new Koma(Color.green, 1, 2, 3, 0));
		b.add(new Koma(Color.magenta, 1, 2, 0, 3));
		b.add(new Koma(Color.yellow, 1, 2, 3, 3));
		b.add(new Koma(Color.cyan, 1, 1, 0, 2));
		b.add(new Koma(Color.orange, 1, 1, 1, 2));
		b.add(new Koma(Color.pink, 1, 1, 2, 2));
		b.add(new Koma(Color.gray, 1, 1, 3, 2));
		b.add(new Koma(Color.white, 2, 1, 1, 3));

		return b;
	}

	public void actionPerformed(ActionEvent ae) {
		if (ae.getActionCommand().equals("Init")) {
			bc.setBoard(newBoard());
		} else if (ae.getActionCommand().equals("Solve")) {
			b2.setLabel("Stop");
			ta.append("Solve started.\n");
			th = new Thread(this);
			th.start();
		} else if (ae.getActionCommand().equals("Stop")) {
			th.stop();
			b2.setLabel("Solve");
			ta.append("Solve stopped.\n");
		} else if (ae.getActionCommand().equals("Next")) {
			if (bl == null) return;
			else {
				bc.setBoard(bl.b);
				bl = bl.child;
			}
		}
	}

	public void run() {
		bl = (bc.getBoard()).solve(ta);
		b2.setLabel("Solve");
		ta.append("Solve finished.\n");
	}
}

class BoardCanvas extends Canvas implements MouseListener {
	Board board;
	int pressed_x, pressed_y;

	BoardCanvas() {
		addMouseListener(this);
	}

	void setBoard(Board b) {
		board = b;
		repaint();
	}

	Board getBoard() {
		return board;
	}

	public void mouseClicked(MouseEvent me) {
	}
	public void mouseEntered(MouseEvent me) {
	}
	public void mouseExited(MouseEvent me) {
	}
	public void mousePressed(MouseEvent me) {
		pressed_x = me.getX();
		pressed_y = me.getY();
	}
	public void mouseReleased(MouseEvent me) {
		Koma k1 = board.getSelectedKoma(pressed_x, pressed_y, this);
		if (k1 == null) return;
		int off_x = me.getX() - pressed_x;
		int off_y = me.getY() - pressed_y;
		if (Math.abs(off_x) > Math.abs(off_y)) {
			off_y = 0;
			if (off_x > 0) off_x = 1;
			else off_x = -1;
		} else {
			off_x = 0;
			if (off_y > 0) off_y = 1;
			else off_y = -1;
		}
		
		board.remove(k1);
		Koma k2 = new Koma(k1);
		k2.move(off_x, off_y);
		if (board.canAdd(k2)) {
			board.add(k2);
			repaint();
		} else {
			board.add(k1);
		}
	}

	public void paint(Graphics g) {
		board.drawBoard(this);
	}
}

class Koma {
	Color color;
	int size_x;
	int size_y;
	int x, y;

	Koma(Color a, int b, int c, int d, int e) {
		color = a;
		size_x = b;
		size_y = c;
		x = d;
		y = e;
	}

	Koma(Koma koma) {
		color = koma.color;
		size_x = koma.size_x;
		size_y = koma.size_y;
		x = koma.x;
		y = koma.y;
	}

	void move(int off_x, int off_y) {
		x += off_x;
		y += off_y;
	}

	boolean isOverlap(Koma koma) {
		if (x + size_x <= koma.x) return false;
		if (koma.x + koma.size_x <= x) return false;
		if (y + size_y <= koma.y) return false;
		if (koma.y + koma.size_y <= y) return false;
		return true;
	}
}

class BoardList {
	Board b;
	BoardList next;
	BoardList left;
	BoardList right;
	BoardList parent;
	BoardList child;
	int count;
	String id;

	BoardList(Board board) {
		b = board;
		id = board.makeID();
	}
}

class Board {
	Vector b;
	int size_x, size_y;

	Board(int x, int y) {
		b = new Vector();
		size_x = x;
		size_y = y;
	}

	Board(Board board) {
		b = new Vector();
		size_x = board.size_x;
		size_y = board.size_y;

		Enumeration e = (board.b).elements();
		while (e.hasMoreElements()) {
			b.addElement((Koma)e.nextElement());
		}
	}

	void add(Koma k) {
		b.addElement(k);
	}

	void remove(Koma k) {
		b.removeElement(k);
	}

	boolean canAdd(Koma k) {
		if (k.x < 0) return false;
		if (k.x  + k.size_x > size_x) return false;
		if (k.y < 0) return false;
		if (k.y  + k.size_y > size_y) return false;
		Enumeration e = b.elements();
		while (e.hasMoreElements()) {
			if (k.isOverlap((Koma)e.nextElement())) return false;
		}
		return true;
	}

	void drawBoard(Component c) {
		Graphics g = c.getGraphics();
		Dimension d = c.getSize();

		int size = Math.min(d.width / size_x, d.height / size_y);
		int off_x = (d.width - size * size_x) / 2;
		int off_y = (d.height - size * size_y) / 2;

		g.setColor(Color.black);
		g.fillRect(off_x, off_y, size_x * size, size_y * size);

		Enumeration e = b.elements();
		while (e.hasMoreElements()) {
			Koma k = (Koma)e.nextElement();
			g.setColor(k.color);
			g.fillRect(k.x * size + off_x, k.y * size + off_y, k.size_x * size, k.size_y * size);
		}
	}

	String makeID() {
		int i, j;

		StringBuffer r = new StringBuffer();
		r.setLength(size_x * size_y * 2);
		for (i=0; i<size_x * size_y * 2; i++) {
			r.setCharAt(i, ' ');
		}
		
		Enumeration e = b.elements();
		while (e.hasMoreElements()) {
			Koma k = (Koma)e.nextElement();
			for (i=0; i<k.size_x; i++) {
				for (j=0; j<k.size_y; j++) {
						int tmp = (k.y+j) * size_x + (k.x+i);
						r.setCharAt(tmp * 2    , (char)('0' + k.size_x));
						r.setCharAt(tmp * 2 + 1, (char)('0' + k.size_y));
				}
			}
		}

		return r.toString();
	}

	public String toString() {
		int i, j, n;

		StringBuffer r = new StringBuffer();
		r.setLength((size_x + 1) * size_y);
		for (i=0; i<(size_x + 1) * size_y; i++) {
			r.setCharAt(i, (i % (size_x + 1) == size_x) ? '\n' : ' ');
		}
		
		n = 0;
		Enumeration e = b.elements();
		while (e.hasMoreElements()) {
			Koma k = (Koma)e.nextElement();
			for (i=0; i<k.size_x; i++) {
				for (j=0; j<k.size_y; j++) {
						int tmp = (k.y+j) * (size_x + 1) + (k.x+i);
						r.setCharAt(tmp, (char)('A' + n));
				}
			}
			n++;
		}

		return r.toString();
	}

	BoardList solve(TextArea ta) {
		BoardList list = new BoardList(this);
		list.next = null;
		list.left = null;
		list.right = null;
		list.parent = null;
		list.count = 0;
		BoardList cur = list;
		BoardList last = list;
		Board board;

		int [] table_x = {0, 1, 0, -1};
		int [] table_y = {1, 0, -1, 0};

		int count = 0;
		int count2 = 1;

		while (true) {
			Enumeration e = ((cur.b).b).elements();
			while (e.hasMoreElements()) {
				Koma k1 = (Koma)e.nextElement();
				board = new Board(cur.b);
				board.remove(k1);
				for (int i=0; i<4; i++){
					Koma k2 = new Koma(k1);
					k2.x = k1.x + table_x[i];
					k2.y = k1.y + table_y[i];
					if (board.canAdd(k2)) {
						Board board2 = new Board(board);
						board2.add(k2);
						BoardList nb = new BoardList(board2);
						nb.count = cur.count + 1;
						nb.parent = cur;
						int tmp;
						BoardList p = list;
						while (true) {
							tmp = (p.id).compareTo(nb.id);
							if (tmp == 0) break;
							else if (tmp > 0) {
								if (p.right == null) {
									p.right = nb;
									break;
								}
								p = p.right;
							} else {
								if (p.left == null) {
									p.left = nb;
									break;
								}
								p = p.left;
							}
						}
						if (tmp != 0) {
							nb.left = null;
							nb.right = null;

							last.next = nb;
							nb.next = null;
							last = nb;
							count2++;
							if (count != nb.count) {
								count = nb.count;
								if (ta != null) {
									ta.append(count + ": " + count2 + "\n");
								}
							}
							if (board2.isGoal()) {
								last.child = null;
								while (last.parent != null) {
									(last.parent).child = last;
									last = last.parent;
								}
								return list;
							}
						}
					}
				}
			}

			if (cur.next == null) break;
			cur = cur.next;
		}

		return null;
	}

	boolean isGoal() {
		Enumeration e = b.elements();
		while (e.hasMoreElements()) {
			Koma k = (Koma)e.nextElement();
			if (k.size_x == 2 && k.size_y == 2 && k.x == 1 && k.y == 3) return true;
		}
		return false;
	}

	Koma getSelectedKoma(int x, int y, Component c) {
		Dimension d = c.getSize();
		int size = Math.min(d.width / size_x, d.height / size_y);
		int off_x = (d.width - size * size_x) / 2;
		int off_y = (d.height - size * size_y) / 2;

		Enumeration e = b.elements();
		while (e.hasMoreElements()) {
			Koma k = (Koma)e.nextElement();
			int tmp_x = x - off_x - k.x * size;
			int tmp_y = y - off_y - k.y * size;
			if ((0 <= tmp_x && tmp_x < k.size_x * size)
			 && (0 <= tmp_y && tmp_y < k.size_y * size)
			   ) {
				return k;
			}
		}
		return null;
	}
}
