package cpsc2150.extendedConnectX.controllers;

import cpsc2150.extendedConnectX.models.*;
import cpsc2150.extendedConnectX.views.*;

/**
 * The controller class will handle communication between our View and our Model ({@link IGameBoard})
 * <p>
 * This is where you will write code
 * <p>
 * You will need to include your {@link BoardPosition} class, {@link IGameBoard} interface
 * and both of the {@link IGameBoard} implementations from Project 4.
 * If your code was correct you will not need to make any changes to your {@link IGameBoard} implementation class.
 *
 * @version 2.0
 */
public class ConnectXController {

    /**
     * <p>
     * The current game that is being played
     * </p>
     */
    private IGameBoard curGame;

    /**
     * <p>
     * The screen that provides our view
     * </p>
     */
    private ConnectXView screen;

    /**
     * <p>
     * Constant for the maximum number of players.
     * </p>
     */
    public static final int MAX_PLAYERS = 10;
    
    /**
     * <p>
     * The number of players for this game. Note that our player tokens are hard coded.
     * </p>
     */
    private int numPlayers;

    /**
     * <p>
     * An array of pre-defined player tokens for use in ConnectXApp, with ten
     * tokens available.
     * </p>
     */
    private char[] tokens = {'X', 'O', 'M', 'H', 'G', 'K', 'D', 'S', 'T', 'Y'};

    /**
     * <p>
     * Variable to represent the current player, intended to be incremented from
     * 1 to numPlayers.
     * </p>
     */
    private int currentPlayer;

    /**
     * <p>
     * Variable to keep track of whether the game should be played again, based on
     * a previous win or tie.
     * </p>
     */
    private boolean playAgain;

    /**
     * <p>
     * This creates a controller for running the Extended ConnectX game
     * </p>
     * 
     * @param model
     *      The board implementation
     * @param view
     *      The screen that is shown
     * @param np
     *      The number of players for this game.
     * 
     * @post [ the controller will respond to actions on the view using the model. ]
     */
    public ConnectXController(IGameBoard model, ConnectXView view, int np) {

        this.curGame = model;
        this.screen = view;
        this.numPlayers = np;

        // Set the currentPlayer to 0
        currentPlayer = 0;

        // Set playAgain to false
        playAgain = false;

    }

    /**
     * <p>
     * This processes a button click from the view.
     * </p>
     * 
     * @param col 
     *      The column of the activated button
     * 
     * @post [ will allow the player to place a token in the column if it is not full, otherwise it will display an error
     * and allow them to pick again. Will check for a win as well. If a player wins it will allow for them to play another
     * game hitting any button ]
     */
    public void processButtonClick(int col) {

        // If playAgain is true, indicating that the last button press resulted in a win
        // or a tie, start a new game and return early
        if (playAgain) {

            // Call helper function newGame()
            newGame();

            return;

        }

        // Check that the column the player is trying to play a token in is free
        if (curGame.checkIfFree(col)) {

            // Create a variable to store the row where a token can be dropped
            // in the column selected by the user
            int row = 0;

            // Loop through the number of rows on the board
            for (int i = 0; i < curGame.getNumRows(); i++) {

                // Create a BoardPosition object with the current row and the column
                // selected by the user
                BoardPosition pos = new BoardPosition(i, col);

                // If the space at the current row and column is free, update the row tracker
                // and stop looping
                if (curGame.whatsAtPos(pos) == ' ') {

                    row = i;

                    break;

                }

            }

            // Change the text on the button for the space where the token will be played to the
            // current player's token
            screen.setMarker(row, col, tokens[currentPlayer % numPlayers]);

            // Place the current player's token in the column selected by the user
            curGame.placeToken(tokens[currentPlayer % numPlayers], col);

            // If the token being placed has resulted in a win, print win message and
            // update playAgain status variable
            if (curGame.checkForWin(col)) {

                screen.setMessage("Player " + tokens[currentPlayer % numPlayers] + " wins! Press any button to play again.");

                playAgain = true;

                // If the token being placed has resulted in a tie, print tie message and
                // update playAgain status variable
            } else if (curGame.checkTie()) {

                screen.setMessage("Tie game! Press any button to play again.");

                playAgain = true;

                // If the token being placed has not resulted in a win or a tie
            } else {

                // Advance to the next player
                currentPlayer++;

                // Prompt the next player
                screen.setMessage("It is " + tokens[currentPlayer % numPlayers] + "'s turn.");

            }

            // If the column is not free, display an error message
        } else {

            screen.setMessage("Column " + col + " is full; pick another column. It is " + tokens[currentPlayer % numPlayers] + "'s turn.");

        }

    }

    /**
     * <p>
     * This method will start a new game by returning to the setup screen and controller
     * </p>
     * 
     * @post [ a new game gets started ]
     */
    private void newGame() {

        // Close the current screen
        screen.dispose();
        
        // Start back at the setup menu
        SetupView screen = new SetupView();
        SetupController controller = new SetupController(screen);
        screen.registerObserver(controller);

    }

}