import java.util.ArrayList;
import java.util.Random;

public class RuleController {
    public static CardParser parser = new CardParser();

    /** IMPORTANT
     * As long as one of the following three is matchable, the play will be considered as legal
     */
    private String currentMatchableColor;
    private String currentMatchableNumber;
    private String currentMatchableSymbol = "none"; // Card played in the first round can only match by either color or number
    private int nextPlayerSkipLevel = 0;
    private int cumulativePenaltyDraw = 0;
    private boolean isClockWise = true;   // if clockwise, next player will be the element in player list

    public RuleController() {
        // initialize first matched color and number
        boolean chosen = false;
        while (!chosen) {
            Random rand = new Random();
            int cardID = rand.nextInt(100) + 1; // generate a number from 1 - 100. these are colored cards
            String desc = parser.parseCardID(cardID);
            String[] result = parser.parseCardDescription(desc);
            String color = result[0];
            String type = result[1];
            String content = result[2];

            if (type.equals("num")) {
                currentMatchableColor = color;
                currentMatchableNumber = content;
                chosen = true;
            }
        }
    }

    /**
     * judge whether a pending player should be skipped
     * @param player the pending player
     * @return bool
     */
    public boolean checkSkipandDraw(Player player) {
        if (nextPlayerSkipLevel == 3) {
            assert cumulativePenaltyDraw == 0;
            nextPlayerSkipLevel = 0; // the next player should no longer be skipped
            return true;

        } else if (nextPlayerSkipLevel != 0) {
            // player will be skipped and forced to draw cards
            // unless they have draw2 / wildDraw4 cards
            assert cumulativePenaltyDraw != 0;
            boolean toSkip = player.findLegalCard().isEmpty();
            if (toSkip) {
                nextPlayerSkipLevel = 0;
                player.drawCards(cumulativePenaltyDraw);
                resetPenaltyDraw();
            }
            return toSkip;
        } else {
            return false;
        }
    }



    /**
     * Given a card played by the user, judge whether this play is valid
     * that is, all cards must be played following the rule
     * @return whether the play is valid
     */
    public boolean isValidPlay(Player player, int cardID, boolean updateIfValid) {
        assert(player != null);
        String cardDescription = parser.parseCardID(cardID);
        String[] result = parser.parseCardDescription(cardDescription);
        String color = result[0];
        String type = result[1];
        String content = result[2];

        boolean valid = false;

        if (nextPlayerSkipLevel == 3) {
            valid = false;

        } else if (nextPlayerSkipLevel == 2) {
            if (content.equals("wildDraw4")) {
                valid = checkDraw4IsLegal(player);
            } else {
                valid = false;
            }

        } else if (nextPlayerSkipLevel == 1) {
            if (content.equals("wildDraw4")) {
                valid = checkDraw4IsLegal(player);
            } else {
                return content.equals("draw2"); // draw 2 is always allowed in this case
            }


        } else {
            // if not skipped, first consider wild card
            if (content.equals("wild")) {
                valid = true;  // can be used unconditionally
            }

            // then other colored cards
            if (checkAttrMatch(currentMatchableColor, color))
                valid = true;
            if (checkAttrMatch(currentMatchableNumber, content) || checkAttrMatch(currentMatchableSymbol, content))
                valid = true;

            // check wildDraw4 at last
            if (content.equals("wildDraw4")) {
                valid = checkDraw4IsLegal(player);
            }
        }

        if (valid && updateIfValid) {
            updateRule(color, type, content);
        }

        return valid;
    }

    private void updateRule(String color, String type, String content) {
        setMatchableColor(color); // wildcard "NA" - this will be updated later by player declaring the color
        if (type.equals("sym")) {
            if (content.equals("skip")) {
                setNextPlayerSkiplevel(3);
                // the states after the skip.. (supposed skipping is done)
                currentMatchableSymbol = content; //??
                currentMatchableNumber = "none"; //??

            } else if (content.equals("draw2")) {
                // next round match by either COL or SYM
                setNextPlayerSkiplevel(1);
                increasePenaltyDraw(2);
                currentMatchableSymbol = content;
                currentMatchableNumber = "none";

            } else if (content.equals("wildDraw4")) {
                // next round match only by COL
                setNextPlayerSkiplevel(2);
                increasePenaltyDraw(4);
                currentMatchableSymbol = "none";
                currentMatchableNumber = "none";

            } else if (content.equals("reverse")) {
                // next round match by either COL or SYM
                changeGameOrder();
                setNextPlayerSkiplevel(0);
                currentMatchableSymbol = content;
                currentMatchableNumber = "none";

            } else if (content.equals("wild")) {
                // next round can only match by COL
                currentMatchableSymbol = "none";
                currentMatchableNumber = "none";
            }

        } else if (type.equals("num")) {
            // next round match by either COL or NUM
            setNextPlayerSkiplevel(0);
            currentMatchableNumber = content;
            currentMatchableSymbol = "none";
        }
    }

    private boolean checkAttrMatch(String legalString, String stringToCheck) {
        return legalString.equals("all") || stringToCheck.equals(legalString);
    }


    /**
     * Check whether a player have currently matchable **color** when attempting to play wild draw 4
     */
    private boolean checkDraw4IsLegal(Player player) {

        ArrayList<Integer> cards = player.getCards();
        for (int i = 0; i < cards.size(); i++) {
            int cardID = cards.get(i);
            String cardDescription = parser.parseCardID(cardID);
            String color = parser.parseCardDescription(cardDescription)[0]; // color of wild cards are NA
            if (color.equals(currentMatchableColor)) {
                return false;
            }
        }
        return true;
    }

    /**
     *  Getter and Setter for Allowed number
     */

    public String getMatchableNumber() {
        return currentMatchableNumber;
    }

    public void setMatchableNumber(String number) {
        currentMatchableNumber = number;
    }

    /**
     *  Getter and Setter for Allowed Symbol
     */

    public String getMatchableSymbol() {
        return currentMatchableSymbol;
    }

    public void setMatchableSymbol(String symbol) {
        currentMatchableSymbol = symbol;
    }


    /**
     *  Getter and Setter for Allowed Color
     */

    public String getMatchableColor() {
        return currentMatchableColor;
    }

    public void setMatchableColor(String color) {
        currentMatchableColor = color;
    }

    /**
     * Getter and setter for nextPlayerIsSkipped
     * level 0: next player won't be skipped
     * level 1: next player can play a either a draw2 card or a wildDraw4 card to avoid being skipped
     * level 2: next player can only play a wildDraw4 card of any color to avoid being skipped
     * level 3: next player will be skipped anyway.
     */

    public int getNextPlayerSkiplevel() { return nextPlayerSkipLevel; }

    public void setNextPlayerSkiplevel(int level) { nextPlayerSkipLevel = level; }

    private String descSkipLevel() {
        if (nextPlayerSkipLevel == 0) {
            return("level 0: player won't be skipped");
        } else if (nextPlayerSkipLevel == 1) {
            return("level 1: player can play a either a draw2 card or a wildDraw4 card to avoid being skipped");
        } else if (nextPlayerSkipLevel == 2) {
            return("level 2: next player can only play a wildDraw4 card of any color to avoid being skipped");
        } else {
            return("level 3: next player will be skipped anyway.");
        }
    }

    /**
     * Getter and setter for cumulativePenaltyDraw
     * @return
     */

    public int getPenaltyDraw() { return cumulativePenaltyDraw; }

    public void resetPenaltyDraw() { cumulativePenaltyDraw = 0; }

    public void increasePenaltyDraw(int num) { cumulativePenaltyDraw += num; }

    /**
     * Getter and setter for isClockwise
     * @return
     */

    public boolean getIsClockwise() { return isClockWise; }

    public void changeGameOrder() { isClockWise = !isClockWise; }


    public void reportCurrentState(Game gameController) {
        System.out.println("============================= Game State Report ========================================");
        System.out.println("Current matchable color : " + getMatchableColor());
        System.out.println("Current matchable number : " + getMatchableNumber());
        System.out.println("Current matchable symbol : " + getMatchableSymbol());
        System.out.println("Game order : " + (getIsClockwise() ? "clockwise" : "counterclockwise"));
        System.out.println("Player skip level is " + descSkipLevel());
        System.out.println("Player will be forced to draw " + cumulativePenaltyDraw + " cards");
        System.out.println("There are " + gameController.gameCardManager.numCardLeft() + " cards in the card pile.");
        System.out.println("There are " + gameController.gameCardManager.numLeftDiscardPile() + " cards in the discard pile.");
    }


}