package Game;

import Card.*;
import Deck.Deck;
import Discard.Discard;
import Player.Player;

import java.util.ArrayList;

public class Game {
    private Player players[];
    private Deck deck;
    private Discard discard;
    private NumberCard wild;
    private int turn;
    private int order;
    private int numOfPlayers;

    /**
     * Constructor of the Game.Game object
     * @param numOfPlayers number of players participated in the game
     */
    public Game(int numOfPlayers) {
        this.numOfPlayers = numOfPlayers;
        init();
        dealInitialCardsToPlayers();
        assignCardToDiscard();
    }

    /**
     * Test Constructor for game
     * @param numOfPlayers number of players
     * @param seed seed for random deck
     */
    public Game(int numOfPlayers, int seed) {
        this.numOfPlayers = numOfPlayers;
        init();
        deck = new Deck(seed);
        dealInitialCardsToPlayers();
        assignCardToDiscard();
    }

    /**
     * Get all the players
     * @return array of players
     */
    public Player[] getPlayers() {
        return players;
    }

    /**
     * Setter for players variable. Used for testing only.
     * @param players Players to be set in the game
     */
    public void setPlayers(Player[] players) {
        this.players = players;
    }

    /**
     * Getter for wild variable. Used for testing only.
     * @return
     */
    public Card getWild() {
        return wild;
    }

    /**
     * Getter for deck variable. Used for testing only.
     * @return
     */
    public Deck getDeck() {
        return deck;
    }

    /**
     * Getter for discard variable. Used for testing only.
     * @return
     */
    public Discard getDiscard() {
        return discard;
    }

    /**
     * Getter for turn variable. Used for testing only.
     * @return
     */
    public int getTurn() {
        return turn;
    }

    /**
     * Initialization function for Game object.
     */
    public void init() {
        players = new Player[numOfPlayers];
        for (int i = 0; i < numOfPlayers; ++i) {
            players[i] = new Player();
        }
        deck = new Deck();
        discard = new Discard();
        wild = new NumberCard(-1, 5);
        turn = 0;
        order = 1;
    }

    /**
     * Return the index indicating the next player.
     * @return index of next player
     */
    public int nextTurn() {
        return (((turn + order) % numOfPlayers) + numOfPlayers) % numOfPlayers;
    }

    /**
     * Check if any player has won.
     * @return -1 if no one wins; index of player if one wins
     */
    public int checkPlayer() {
        for (int i = 0; i < numOfPlayers; ++i) {
            if (players[i].getNumOfCards() == 0) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Handle the player's action. Will be refactored in the next assignment.
     * @param player player to be handled
     * @param card card on the discard pile
     * @param idx index of the card player will play if possible
     */
    public Card handlePlayer(Player player, Card card, int idx) {
        ArrayList<Card> cards = player.returnIfContainsLegal(card);
        if (cards.size() > 0) {
            player.playCard(cards.get(idx));
            return cards.get(idx);
        } else {
            player.drawOneCard(deck.drawTop());
            cards = player.returnIfContainsLegal(card);
            if (cards.size() > 0) {
                player.playCard(cards.get(idx));
                return cards.get(idx);
            }
        }
        return null;
    }

    /**
     * Handle skip card.
     */
    public void handleSkip() {
        turn = nextTurn();
    }

    /**
     * Handle reverse card.
     */
    public void handleReverse() {
        order = -order;
    }

    /**
     * Handle draw two card.
     */
    public void handleDrawTwo() {
        Player unlucky = players[nextTurn()];
        for (int i = 0; i < 2; ++i) {
            dealCardToPlayer(unlucky);
        }
    }

    /**
     * Handle wild card.
     * @param color color to be set for next turn
     */
    public void handleWild(int color) {
        wild.setColor(color);
    }

    /**
     * Handle wild draw four card.
     * @param color color to be set for next turn
     */
    public void handleWildDrawFour(int color) {
        Player unlucky = players[nextTurn()];
        for (int i = 0; i < 4; ++i) {
            dealCardToPlayer(unlucky);
        }
        wild.setColor(color);
    }

    /**
     * Deal card on top of deck to one player.
     * @param player player to be dealt card to
     */
    private void dealCardToPlayer(Player player) {
        player.drawOneCard(deck.drawTop());
    }

    /**
     * Deal initial cards to all players.
     */
    private void dealInitialCardsToPlayers() {
        for (int i = 0; i < players.length; ++i) {
            Card[] temp = deck.drawMultipleTop(7);
            players[i].drawMultipleCards(temp);
        }
    }

    /**
     * Assign one card to discard. Re-assign if card is not number card.
     */
    private void assignCardToDiscard() {
        Card temp;
        do {
            temp = deck.drawTop();
            discard.discardCard(temp);
        } while(temp.getType());
    }


}