// Game of Fanorona
// David Eppstein, UC Irvine, 11 Jun 1997
//
// Code for showing current state of the board

import java.awt.*;
import java.awt.image.*;
import java.util.*;

class BoardDisplayRow {
	public int top, bottom, left, right;
	public BoardDisplayRow(int t, int b, int l, int r) { top = t; bottom = b; left = l; right = r; }
	public boolean containsRow(int row) { return row >= top && row <= bottom; }
	public int indexOfCol(int col) { return ((col - left) * 9) / (right + 1 - left); }
	public int leftCol(int sq) { return left + sq*(right+1-left)/9; }
	public int rightCol(int sq) { return leftCol(sq+1)-1; }
}

class BoardDisplayCell {
	BoardDisplay display;
	int row, col;
	Image empty, white, black;
	int top, bottom, left, right;
	static MediaTracker tracker = null;
	static void track() {
		if (tracker != null) {
			try { tracker.waitForID(0); }
			catch (InterruptedException e) { throw new ImageUnavailable(); }
		}
		tracker = null;
	}
	
	public BoardDisplayCell(BoardDisplay b, int r, int c) {
		display = b;
		row = r; col = c;
		top = b.rows[row].top;
		bottom = b.rows[row].bottom;
		left = b.rows[row].leftCol(col);
		right = b.rows[row].rightCol(col);
		ImageFilter f = new CropImageFilter(left, top, right-left+1, bottom-top+1);
		empty = b.createImage(new FilteredImageSource(b.emptyBoard.getSource(), f));
		white = b.createImage(new FilteredImageSource(b.whiteBoard.getSource(), f));
		black = b.createImage(new FilteredImageSource(b.blackBoard.getSource(), f));
		if (tracker == null) tracker = new MediaTracker(b);
		tracker.addImage(empty, 0);
		tracker.addImage(white, 0);
		tracker.addImage(black, 0);
	}

	public void paint(Graphics g) {
		track();
		long m = display.game.getBoard().myPieces;
		long o = display.game.getBoard().opponentPieces;
		long b = Bits.at(row, col);
		Image i;
		if (((m | o) & b) == 0) i = empty;
		else if (((m & b) == 0) ^ ((m & Bits.IS_WHITE) != 0)) i = white;
		else i = black;
		g.drawImage(i, left, top, display);
	}
}

public class BoardDisplay extends Canvas implements Observer
{
	Game game;
	long mouseDown;

	public Image emptyBoard, whiteBoard, blackBoard;
	public BoardDisplayRow rows[] = {
		new BoardDisplayRow(16,46,38,361),
		new BoardDisplayRow(47,77,35,364),
		new BoardDisplayRow(78,110,32,367),
		new BoardDisplayRow(111,145,28,371),
		new BoardDisplayRow(146,180,25,374)
	};
	public BoardDisplayCell cells[][];
	
	public BoardDisplay(java.applet.Applet applet, Game g) {
		game = g;
		game.addObserver(this);
		
		java.net.URL documentBase = applet.getDocumentBase();
		emptyBoard = applet.getImage(documentBase, "Board.jpg");
		whiteBoard = applet.getImage(documentBase, "White.jpg");
		blackBoard = applet.getImage(documentBase, "Black.jpg");

		// make sure we have images before trying to crop them
		MediaTracker tracker = new MediaTracker(this);
		tracker.addImage(emptyBoard,0);
		tracker.addImage(whiteBoard,1);
		tracker.addImage(blackBoard,2);
		try {
			tracker.waitForID(0);
			tracker.waitForID(1);
			tracker.waitForID(2);
		} catch (InterruptedException e) {
			throw new ImageUnavailable();
		}

		cells = new BoardDisplayCell[5][];
		for (int i = 0; i < 5; i++) {
			cells[i] = new BoardDisplayCell[9];
			for (int j = 0; j < 9; j++)
				cells[i][j] = new BoardDisplayCell(this, i, j);
		}
	}
	
	public Dimension minimumSize() {
//		return new Dimension(emptyBoard.getWidth(this), emptyBoard.getHeight(this));
		return new Dimension(400,225);	// otherwise Metrowerks Pro 2 Java skooz up
	}
	public Dimension preferredSize() { return minimumSize(); }
	
	// update screen display when a move is made
	private Board lastUpdate = null;
	public void update(Observable updatedGame, Object updatedObject) {
		if (updatedGame != game) throw new IllegalArgumentException("Board display: updated object is not game");
		if (!(updatedObject instanceof Board)) return;
		if (!isShowing()) return;
		Board b = (Board) updatedObject;
		if (b.previousPosition != lastUpdate || !b.wasPass()) repaint();
		lastUpdate = b;
	}
	
	// no image no flash
	public void update(Graphics g) { paint(g); }

	public void paintPieces(Graphics g, long pieces) {
		for (int i = 0; i < 5; i++)
			for (int j = 0; j < 9; j++)
				if ((pieces & Bits.at(i,j)) != 0)
					cells[i][j].paint(g);
	}
	
	Image offscreenImage = null;
	Graphics offscreenGraphics = null;
	public void paint(Graphics g) {
		if (offscreenImage == null) {
			offscreenImage = createImage(emptyBoard.getWidth(this), emptyBoard.getHeight(this));
			offscreenGraphics = offscreenImage.getGraphics();
		}
		offscreenGraphics.drawImage(emptyBoard, 0, 0, this);
		Board board = game.getBoard();
		paintPieces(offscreenGraphics, board.myPieces | board.opponentPieces);
		g.drawImage(offscreenImage, 0, 0, this);
	}
	
	// put a short message in the status line
	public void showMessage(String s, boolean p) { Fanorona.showMessage(this, s, p); }
	
	// translate mouse clicks into bit representing board cell
	// returns zero if click was off-board
	long translateMouse(int x, int y) {
		for (int i = 0; i < 5; i++)
			if (rows[i].containsRow(y)) {
				int j = rows[i].indexOfCol(x);
				if (j < 0 || j >= 9) return 0;
				return Bits.at(i,j);
			}
		return 0;
	}
	
	// handle mouse events. mouse down just stores info for later mouse up.
	public boolean mouseDown(Event e, int x, int y) {
		if (!game.humanToMove()) {
			mouseDown = 0;
			return true;
		}
		mouseDown = translateMouse(x, y);
		return true;
	}

	// mouse up tries to find a move matching the clicked pieces
	public boolean mouseUp(Event e, int x, int y) {
		if (mouseDown == 0 || !game.humanToMove()) return false;
		long mouseUp = translateMouse(x, y);
		if (mouseUp == 0) return true;
		long bits = mouseDown | mouseUp;
		
		Board board = game.getBoard();
		MoveGenerator mg = new MoveGenerator(board);
		Board newBoard = null;
		if (!mg.hasMoreElements()) {
			showMessage("Game over, no more moves available", false);
			return true;
		}
		while (mg.hasMoreElements()) {
			long changed = mg.nextElement();
			if ((bits & changed) == bits) {
				if (newBoard != null) {
					if (mouseDown == mouseUp)
						showMessage("Ambiguous move; click and drag a piece to a new location", false);
					else showMessage("Ambiguous move; drag your piece onto a piece you wish to eat", false);
					return true;
				}
				newBoard = new Board(board,changed);
			}
		}
		if (newBoard == null) showMessage("Illegal move", false);
		else {
			if (game.mustPass(newBoard)) newBoard = new Board(newBoard,0); // forced pass?
			game.setBoard(newBoard);								// make move (and forced pass)
			if (newBoard.midCapture())
				showMessage("Continue eating with the same piece, or pass", true);
		}
		return true;
	}
}
