import java.awt.Color;

import objectdraw.FramedOval;
import objectdraw.Line;
import objectdraw.Location;
import objectdraw.WindowController;

// Demonstrates different ways of walking a 2D array.

public class TicTacToe extends WindowController {
    // Top, left corner of grid
    private static final int TOP = 40;
    private static final int LEFT = 40;
    
    // Number of rows in the grid
    private static final int NUM_ROWS = 3;
    private static final int NUM_COLS = NUM_ROWS;
    
    // Size of each grid square
    private static final int CELL_SIZE = 40;
    
    // Size of the marks to draw in the grid and offset from square edge
    private static final int MARK_SIZE = CELL_SIZE * 3 / 4;
    private static final int MARK_OFFSET = (CELL_SIZE - MARK_SIZE) / 2;
    
    // Length of the lines making up the grid
    private static final int LINE_LENGTH = CELL_SIZE * NUM_ROWS;
    
    // Denote value of a square to show who has played there.
    private static final int EMPTY = 0;
    private static final int X_MARK = 1;
    private static final int O_MARK = 2;
    
    // The grid contents
    private int[][] marks = new int[NUM_ROWS][NUM_ROWS];
    
    // Whose turn it is next
    private int nextMark = X_MARK;
    
    // Whether the game has been won or not
    private boolean gameOver = false;
    
    // Draw an empty grid
    public void begin() {
        for (int i = 1; i < NUM_ROWS; i++) {
            new Line(LEFT, TOP + i * CELL_SIZE,
                     LEFT + LINE_LENGTH, TOP + i * CELL_SIZE, canvas);
            new Line(LEFT + i * CELL_SIZE, TOP,
                     LEFT + i * CELL_SIZE, TOP + LINE_LENGTH, canvas);
        }
    }
    
    // Draw an X with the given upper left hand corner
    private void drawX(int left, int top) {
        new Line(left, top, left + MARK_SIZE, top + MARK_SIZE, canvas);
        new Line(left + MARK_SIZE, top, left, top + MARK_SIZE, canvas);
    }
    
    // Draw an O with the given upper left hand corner
    private void drawO(int left, int top) {
        new FramedOval(left, top, MARK_SIZE, MARK_SIZE, canvas);
    }
    
    // Place the next piece and check for a win
    public void onMouseClick(Location point) {
        double x = point.getX();
        double y = point.getY();
        
        // Ignore clicks outside the grid
        if (x > LEFT && x < LEFT + LINE_LENGTH && 
            y > TOP && y < TOP + LINE_LENGTH) {
            x = x - LEFT;
            y = y - TOP;
            
            // Figure out which cell in the grid was clicked in
            int col = (int) (x / CELL_SIZE);
            int row = (int) (y / CELL_SIZE);
            
            // Make sure the grid is empty before adding a mark.
            // Also make sure the game is not already over
            if (marks[row][col] == EMPTY && !gameOver) {
                // Add the mark
                marks[row][col] = nextMark;
                int thisMark = nextMark;
                if (nextMark == X_MARK) {
                    drawX(LEFT + col * CELL_SIZE + MARK_OFFSET,
                          TOP + row * CELL_SIZE + MARK_OFFSET);
                    nextMark = O_MARK;
                } else {
                    drawO(LEFT + col * CELL_SIZE + MARK_OFFSET,
                          TOP + row * CELL_SIZE + MARK_OFFSET);
                    nextMark = X_MARK;
                }
                
                // See if the game is over
                checkGameOver(row, col, thisMark);
            }
        }
    }
    
    // The game is over if an entire row, column, or diagonal has the same pieces
    private void checkGameOver(int row, int col, int matchMark) {
        gameOver = checkForRowWin(row, matchMark)
            || checkForColWin(col, matchMark)
            || checkForDiagWin(row, col, matchMark)
            || checkForDiag2Win(row, col, matchMark);
    }
    
    // See if this row has all the same pieces
    private boolean checkForRowWin(int row, int matchMark) {
        for (int j = 0; j < NUM_COLS; j++) {
            if (marks[row][j] != matchMark) {
                return false;
            }
        }
        
        int winLineY = TOP + row * CELL_SIZE + CELL_SIZE / 2;
        new Line(LEFT, winLineY,
                 LEFT + LINE_LENGTH, winLineY, canvas).setColor(Color.red);
        return true;
    }
    
    // See if there is a column with all the same pieces
    private boolean checkForColWin(int col, int matchMark) {
        for (int i = 0; i < NUM_ROWS; i++) {
            if (marks[i][col] != matchMark) {
                return false;
            }
        }
        
        int winLineX = LEFT + col * CELL_SIZE + CELL_SIZE / 2;
        new Line(winLineX, TOP, winLineX, TOP + LINE_LENGTH, canvas).setColor(Color.red);
        return true;
    }
    
    // See if the diagonal from top-left to bottom-right has all the same pieces
    private boolean checkForDiagWin(int row, int col, int matchMark) {
        if (row != col) {
            return false;
        }
        
        for (int i = 0; i < NUM_ROWS; i++) {
            if (marks[i][i] != matchMark) {
                return false;
            }
        }
        new Line(LEFT, TOP,
                 LEFT + LINE_LENGTH, TOP + LINE_LENGTH,
                 canvas).setColor(Color.red);
        return true;
    }
    
    // See if the diagonal from top-right to bottom-left has all the same pieces.
    private boolean checkForDiag2Win(int row, int col, int matchMark) {
        for (int i = 0; i < NUM_ROWS; i++) {
            if (marks[i][NUM_COLS - i - 1] != matchMark) {
                return false;
            }
        }
        new Line(LEFT + LINE_LENGTH, TOP,
                 LEFT, TOP + LINE_LENGTH,
                 canvas).setColor(Color.red);
        return true;
    }
    
}
