import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;

/**
 * Example class to show how to handle timing when human
 * and computer alternate moves.
 * <p>
 * When threading is not done correctly, the computer
 * may do all its pauses first, then move instantly.
 * This happens if the pause() is on the same Thread
 * that is supposed to do the GUI update.
 * </p><p>
 * The solution is to do the computer's move in a
 * separate Thread. More precisely, the logic of the
 * computer's move should be done in the model, but
 * the <i>display</i> of the move should be done in a
 * separate Thread in the GUI. 
 * </p><p>
 * In this example program (which is as short as I could
 * reasonably make it), there is no "model" class--the
 * computer makes a random move after each human's move.
 * 
 * @author Dave Matuszek
 * @version Dec 1, 2006
 */
public class AlternateMoves extends JFrame
    implements ActionListener {
    
    static Random rand = new Random();
    static final int ARRAY_SIZE = 8;
    
    MyButton buttons[][] = new MyButton[ARRAY_SIZE][ARRAY_SIZE];
    
    boolean firstButtonClick = true;
    int firstRow;
    int firstColumn;

    public static void main(String[] args) {
        new AlternateMoves().run();
    }
    
    /** Just creates the GUI. Nothing interesting here. */
    void run() {
        setLayout(new GridLayout(ARRAY_SIZE, ARRAY_SIZE));
        for (int i = 0; i < ARRAY_SIZE; i++) {
            for (int j = 0; j < ARRAY_SIZE; j++) {
                buttons[i][j] = new MyButton(i, j);
                add(buttons[i][j]);
                buttons[i][j].setBackground(Color.WHITE);
                buttons[i][j].addActionListener(this);
            }
        }
        pack();
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    /** Handles user's move, then tells computer to move. */
    public void actionPerformed(ActionEvent event) {
        MyButton button = (MyButton)event.getSource();

        if (firstButtonClick) {
            // Show where human is moving from
            firstRow = button.row;
            firstColumn = button.column;
            button.setBackground(Color.RED);
            firstButtonClick = false;
        }
        else {
            // Complete human's move
            setButtonColor(firstRow, firstColumn, Color.WHITE);
            firstButtonClick = true;
            // Then do computer's turn
            doComputersMove();
        }
    }

    private void setButtonColor(int row, int column, Color color) {
        buttons[row][column].setBackground(color);
    }

    private void doComputersMove() {
        // Here's the secret trick!
        // Do the computers move in a separate thread.
        new ComputerThread().start();
    }

    /**
     * Implements a JButton that knows where it is in the array.
     */
    class MyButton extends JButton {
        int row, column;
        
        MyButton(int row, int column) {
            super( row + "  " + column); // Puts label on button
            this.row = row;
            this.column = column;
        }
    }
    
    /**
     * Thread to perform computer's move with appropriate pauses.
     */
    class ComputerThread extends Thread {
        
        public void run() {
            pause(); // So computer doesn't move instantly after human
            int fromRow = rand.nextInt(ARRAY_SIZE);
            int fromColumn = rand.nextInt(ARRAY_SIZE);
            setButtonColor(fromRow, fromColumn, Color.GREEN);
            
            pause();
            int toRow = rand.nextInt(ARRAY_SIZE);
            int toColumn = rand.nextInt(ARRAY_SIZE);
            setButtonColor(toRow, toColumn, Color.BLUE);
            
            pause();
            setButtonColor(fromRow, fromColumn, Color.WHITE);
            setButtonColor(toRow, toColumn, Color.WHITE);
        }
        
        private void pause() {
            try { Thread.sleep(1000); }
            catch (InterruptedException e) { }
        }
    }
}
