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

class Board extends Panel implements Observer {
    private Piece[][] board;
    Vector pieces = new Vector();
    private int rows;
    private int columns;
    private int defaultSpeed = 5;
    private Dimension cellSize;
    private Image offscreenImage = null;
    private Graphics offscreenGraphics = null;
    private int oldHeight;
    private int oldWidth;
    
    /**
     * Creates a playing board with the given number of
     * rows and columns.
     */
    public Board(int rows, int columns) {
        this.rows = rows;
        this.columns = columns;
        board = new Piece[rows][columns];
        for(int i = 0; i < rows; i++)
            for(int j = 0; j< columns; j++)
                board[i][j] = null;
       
    }
    
    /** Returns the number of rows in this Board. */
    public int getRows() { return rows; }
    
    /** Returns the number of columns in this Board. */
    public int getColumns() { return columns; }
    
    /**
     * Returns the piece at the given row and column in
     * this Board, or null if the given location is empty.
     */
    public Piece getPiece(int row, int column) {
        return board[row][column];
    }
    
    /**
     * Places the given piece at the given location in this
     * board. The piece itself is also adjusted to know its
     * own row and column numbers.
     */
    public void place(Piece piece, int row, int column) {
        board[row][column] = piece;
        if (piece.getRow() < 0) piece.place(row, column);
        pieces.add(piece);
    }
    
    /**
     * Removes the piece at the given row and column on this
     * Board. The piece itself is also adjusted to know that
     * it is no longer at this position.
     */
    public void remove(int row, int column) {
        if (!legalPosition(row, column)) return;
        Piece oldPiece = getPiece(row, column);
        board[row][column] = null;
        if (oldPiece != null && oldPiece.getRow() >= 0) {
            oldPiece.remove();
            pieces.remove(oldPiece);
        }
    }
    
    /**
     * Sets the default speed of movement for pieces on this
     * board. This value may be overridden if the piece specifies
     * its own speed.
     */
    public void setSpeed(int speed) {
        if (speed > 0) defaultSpeed = speed;
    }
    
    /**
     * Returns the default speed of pieces on this board.
     */
    public int getSpeed() {
        return defaultSpeed;
    }
    
    /**
     * Returns the current width, in pixels, of a single cell
     * on this Board. The value will change if this Board is
     * resized.
     */
    public int getCellWidth() {
        return getWidth() / columns;
    }
    
    /**
     * Returns the current height, in pixels, of a single cell
     * on this Board. The value will change if this Board is
     * resized.
     */
    public int getCellHeight() {
        return getHeight() / rows;
    }
    
    /**
     * Paints the board itself, not including the pieces.
     */
    public void paintBackground(Graphics g) {
        int height = getHeight();
        int width = getWidth();
        int x, y;
        
        setBackground(Color.WHITE);
        Color oldColor = g.getColor();
        g.setColor(Color.LIGHT_GRAY);

        // Paint vertical lines
        for (int i = 0; i <= columns; i++) {
            x = getX(i);
            g.drawLine(x, 0, x, height);
        }
        // Paint horizontal lines
        for (int i = 0; i <= rows; i++) {
            y = getY(i);
            g.drawLine(0, y, width, y);
        }
        g.setColor(oldColor);
    }
    
    /**
     * Repaints this Board and everything on it.
     */
    public void update(Graphics g) {
        paint(g);
    }
    
    /**
     * Repaints this Board and everything on it.
     */
    public void paint(Graphics g) {
        // Set up the offscreen graphics (used to avoid flicker)
        if (offscreenImage == null ||
            oldHeight != getHeight() ||
            oldWidth != getWidth()) {
                offscreenImage = createImage(getWidth(), getHeight());
                offscreenGraphics = offscreenImage.getGraphics();
                oldHeight = getHeight();
                oldWidth = getWidth();
        }
        // Clear the offscreen graphics
        offscreenGraphics.setColor(Color.WHITE);
        offscreenGraphics.fillRect(0, 0, getWidth(), getHeight());
        // Paint the board
        paintBackground(offscreenGraphics);
        // Paint pieces
        for (int i = 0; i < pieces.size(); i++) {
            Piece piece = (Piece)pieces.elementAt(i);
            piece.paint(offscreenGraphics);
        }
        // Copy the offscreen onto the screen
        g.drawImage(offscreenImage, 0, 0, null);
    }
    
    /**
     * Returns the X coordinate of the left side of cells
     * in the given column of this Board.
     */
    public int getX(int columnNumber) {
        return (columnNumber * (getWidth() - 1)) / columns;
    }
    
    /**
     * Returns the Y coordinate of the top side of cells
     * in the given column of this Board.
     */
    public int getY(int rowNumber) {
        return (rowNumber * (getHeight() - 1)) / rows;
    }
    
    /**
     * Determines whether the given row and column denote
     * a legal position on this Board.
     */
    public boolean legalPosition(int row, int column) {
        if (row < 0 || row >= rows) return false;
        if (column < 0 || column >= columns) return false;
        return true;
    }
    
    /**
     * Whenever a Piece is modified, this method redraws
     * this Board.
     */
    public void update(Observable piece, Object nothing) {
        repaint();
    }
}
