/*
    Owen Sullivan
 */

package cpsc2150.extendedConnectX.models;

/**
 * Class that represents an entire game board for the ExtendedConnectX game.
 * <p>This class includes methods to place tokens, check token locations,
 * and determine whether or not the game has been won or is over.</p>
 *
 * @author Owen Sullivan
 * @version 1.0
 *
 * @invariant
 *      (board[][] = ' ' OR board[][] = 'X' OR board[][] = 'O') AND
 *      [ there are no gaps between non-space tokens ] AND
 *      numRows = MAX_ROWS AND
 *      numColumns = MAX_COLUMNS AND
 *      numToWin = NUM_TO_WIN
 *
 * @correspondence
 *      number_of_rows = numRows AND
 *      number_of_columns = numColumns AND
 *      num_to_win = numToWin AND
 *      self = board[0...numRows-1][0...numColumns-1]
 */
public class GameBoard extends AbsGameBoard implements IGameBoard {

    private int numRows = MAX_ROWS;
    private int numColumns = MAX_COLUMNS;
    private int numToWin = NUM_TO_WIN;
    private char[][] board;

    /**
     * Constructor for GameBoard that takes no parameters and
     * initializes all indexes of board to ' '.
     *
     * @post
     *      board[][] = ' ' AND
     *      [ size of board[] ] = numRows AND
     *      [ size of board[][] ] = numColumns
     */
    public GameBoard() {

        board = new char[numRows][numColumns];

        // Loop through number of rows
        for (int i = 0; i < numRows; i++) {

            // Loop through number of columns
            for (int j = 0; j < numColumns; j++) {

                // Set the value at the current row and column to a space character
                board[i][j] = ' ';

            }

        }

    }

    public void placeToken(char p, int c) {

        // Loop through rows (starting from the bottom)
        for (int i = 0; i < numRows; i++) {

            // If the current value at current row and column indexes is a space (not a token)
            if (board[i][c] == ' ') {

                // Set the value to the parameter player token p
                board[i][c] = p;
                // Return early
                return;

            }

        }

    }

    public boolean checkHorizWin(BoardPosition pos, char p) {

        // Create a counter for the number of matches (including initial)
        int numMatches = 1;
        // Create a bool that holds the status of whether matches are unbroken in sequence
        boolean unbroken = true;

        // Create a new BoardPosition object starting on the right side of the same row
        BoardPosition newPos = new BoardPosition(pos.getRow(), numColumns - 1);

        // Loop while the column is valid and we haven't found enough matches yet
        while (newPos.getColumn() >= 0 && numMatches < numToWin) {

            // If the position contains the player token and the line is unbroken
            if (whatsAtPos(newPos) == p && unbroken) {

                numMatches++;

                // If the position does not contain the player token, the line is broken
            } else {

                unbroken = false;

            }

            // If the line is broken, set the number of matches to zero and assume
            // the next line will be unbroken
            if (!unbroken) {

                numMatches = 0;
                unbroken = true;

            }

            // If we've found enough matches, return true
            if (numMatches == numToWin) {

                return true;

            }

            // Set the new position one left of the current column
            newPos = new BoardPosition(pos.getRow(), newPos.getColumn() - 1);

        }

        return false;

    }

    public boolean checkVertWin(BoardPosition pos, char p) {

        // Create a counter for the number of matches (including initial)
        int numMatches = 1;
        // Create a bool that holds the status of whether matches are unbroken in sequence
        boolean unbroken = true;

        // Create a new BoardPosition object starting on the top of the same column
        BoardPosition newPos = new BoardPosition(numRows - 1, pos.getColumn());

        // Loop while the row is valid and we haven't found enough matches yet
        while (newPos.getRow() >= 0 && numMatches < numToWin) {

            // If the position contains the player token and the line is unbroken
            if (whatsAtPos(newPos) == p && unbroken) {

                numMatches++;

                // If the position does not contain the player token, the line is broken
            } else {

                unbroken = false;

            }

            // If the line is broken, set the number of matches to zero and assume
            // the next line will be unbroken
            if (!unbroken) {

                numMatches = 0;
                unbroken = true;

            }

            // If we've found enough matches, return true
            if (numMatches == numToWin) {

                return true;

            }

            // Set the new position one left of the current column
            newPos = new BoardPosition(newPos.getRow() - 1, pos.getColumn());

        }

        return false;

    }

    public boolean checkDiagWin(BoardPosition pos, char p) {

        // Create a counter for the number of matches (including initial)
        int numMatches = 1;
        // Create a bool that holds the status of whether matches are unbroken in sequence
        boolean unbroken = true;

        // Create a new BoardPosition object starting on the adjacent bottom left space
        BoardPosition newPos = new BoardPosition(pos.getRow() - 1, pos.getColumn() - 1);
        // Loop while the row and column are valid and we haven't found enough matches yet
        while (newPos.getRow() >= 0 && newPos.getColumn() >= 0 && numMatches < numToWin) {

            // If the position contains the player token and the line is unbroken
            if (whatsAtPos(newPos) == p && unbroken) {

                numMatches++;

                // If the position does not contain the player token, the line is broken
            } else {

                unbroken = false;

            }

            // If the line is broken, set the number of matches to zero and assume
            // the next line will be unbroken
            if (!unbroken) {

                numMatches = 0;
                unbroken = true;

            }

            // If we've found enough matches, return true
            if (numMatches == numToWin) {

                return true;

            }

            // Set the new position one left of and one below the current position
            newPos = new BoardPosition(newPos.getRow() - 1, newPos.getColumn() - 1);

        }

        // If numMatches is 0 (line was broken at last check), reset numMatches (including initial)
        if (numMatches == 0) {

            numMatches = 1;

        }

        // Update newPos to start on the adjacent top right space
        newPos = new BoardPosition(pos.getRow() + 1, pos.getColumn() + 1);
        // Loop while the row and column are valid and we haven't found enough matches yet
        while (newPos.getRow() < numRows && newPos.getColumn() < numColumns && numMatches < numToWin) {

            // If the position contains the player token and the line is unbroken
            if (whatsAtPos(newPos) == p && unbroken) {

                numMatches++;

                // If the position does not contain the player token, the line is broken
            } else {

                unbroken = false;

            }

            // If the line is broken, set the number of matches to zero and assume
            // the next line will be unbroken
            if (!unbroken) {

                numMatches = 0;
                unbroken = true;

            }

            // If we've found enough matches, return true
            if (numMatches == numToWin) {

                return true;

            }

            // Set the new position one right of and one above the current position
            newPos = new BoardPosition(newPos.getRow() + 1, newPos.getColumn() + 1);

        }

        // Reset numMatches (including initial)
        numMatches = 1;

        // Update newPos to start on the adjacent bottom right space
        newPos = new BoardPosition(pos.getRow() - 1, pos.getColumn() + 1);
        // Loop while the row and column are valid and we haven't found enough matches yet
        while (newPos.getRow() >= 0 && newPos.getColumn() < numColumns && numMatches < numToWin) {

            // If the position contains the player token and the line is unbroken
            if (whatsAtPos(newPos) == p && unbroken) {

                numMatches++;

                // If the position does not contain the player token, the line is broken
            } else {

                unbroken = false;

            }

            // If the line is broken, set the number of matches to zero and assume
            // the next line will be unbroken
            if (!unbroken) {

                numMatches = 0;
                unbroken = true;

            }

            // If we've found enough matches, return true
            if (numMatches == numToWin) {

                return true;

            }

            // Set the new position one right of and one below the current position
            newPos = new BoardPosition(newPos.getRow() - 1, newPos.getColumn() + 1);

        }

        // If numMatches is 0 (line was broken at last check), reset numMatches (including initial)
        if (numMatches == 0) {

            numMatches = 1;

        }

        // Update newPos to start on the adjacent top left space
        newPos = new BoardPosition(pos.getRow() + 1, pos.getColumn() - 1);
        // Loop while the row and column are valid and we haven't found enough matches yet
        while (newPos.getRow() < numRows && newPos.getColumn() >= 0 && numMatches < numToWin) {

            // If the position contains the player token and the line is unbroken
            if (whatsAtPos(newPos) == p && unbroken) {

                numMatches++;

                // If the position does not contain the player token, the line is broken
            } else {

                unbroken = false;

            }

            // If the line is broken, set the number of matches to zero and assume
            // the next line will be unbroken
            if (!unbroken) {

                numMatches = 0;
                unbroken = true;

            }

            // If we've found enough matches, return true
            if (numMatches == numToWin) {

                return true;

            }

            // Set the new position one left of and one above the current position
            newPos = new BoardPosition(newPos.getRow() + 1, newPos.getColumn() - 1);

        }

        return false;

    }

    public char whatsAtPos(BoardPosition pos) {

        // Call BoardPosition methods to get row and column, then return
        // the value at the corresponding indexes for row and column in board
        return board[pos.getRow()][pos.getColumn()];

    }

    public int getNumRows() {

        return numRows;

    }

    public int getNumColumns() {

        return numColumns;

    }

    public int getNumToWin() {

        return numToWin;

    }

}
