import os
import sys
import random
import math
import time
import PySimpleGUI as sg


#-------------------------------------------------------------------------------#
#  Works on Python 3                                                            #
#  Uno card game using a GUI interface provided by PySimpleGUI                  #
#  Based on this excellent text based version:                                  #
#  http://code.activestate.com/recipes/580811-uno-text-based/                   #
#  Contains all of the graphics inside the source file as base64 images         #
#  Cards were obtained from Wikipedia                                           #
#        https://en.wikipedia.org/wiki/Uno_(card_game)                          #
#  Up to 4 players... any number can be computer controlled                     #
#  Still needs some work but close enough for fun                               #
#-------------------------------------------------------------------------------#

yellow_color = '#FFAA00'
blue_color = '#5555FF'
red_color = '#FF5555'
green_color = '#55AA55'



wildblue = b'[encoded data, 9308 characters]'
wildgreen = b'[encoded data, 9284 characters]'
wildred = b'[encoded data, 9192 characters]'
wildyellow = b'[encoded data, 9256 characters]'


plusfour = b'[encoded data, 10132 characters]'
plusfourblue = b'[encoded data, 10600 characters]'
plusfourgreen = b'[encoded data, 10196 characters]'
plusfourred = b'[encoded data, 10192 characters]'
plusfouryellow = b'[encoded data, 10116 characters]'


bluebutton = b'iVBORw0KGgoAAAANSUhEUgAAAGMAAAAnCAMAAAAsLAbdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAAE5a71VV/kZq1ICN7pal7QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJIhjsUAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjVkR1hSAAAAzElEQVRYR+2X0Q6EIAwEgaP//8lYoLVgcomJ3T4Y59EYJrtGKKldIAdkKWVzEP1ycaDumsVBVV7xoC4Wc3gaOlXWNQe5lLSRNYo4vENMJMp0YBQqGQ6UopRRV3eQPACQ1YGLMdtiBzAGw22xAxljBEngGD1IjANbVS8roWNwkM9xm89xnxhHxD/4mv0qYm8POaMiztrW/Oc35ZwZgG2dsw+urWWGg4y7zFSoA/JNRGEO977sBmKOiDsOQ06a/PeuNpBb4zNkLeXq8Ke1A1yYxppnbkkqAAAAAElFTkSuQmCC'
greenbutton = b'iVBORw0KGgoAAAANSUhEUgAAAGMAAAAnCAMAAAAsLAbdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAAAGmCwCqAAyjKU/AWXHLfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFeD53sAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjVkR1hSAAAAzElEQVRYR+2X0Q6EIAwEgaP//8lYoLVgcomJ3T4Y59EYJrtGKKldIAdkKWVzEP1ycaDumsVBVV7xoC4Wc3gaOlXWNQe5lLSRNYo4vENMJMp0YBQqGQ6UopRRV3eQPACQ1YGLMdtiBzAGw22xAxljBEngGD1IjANbVS8roWNwkM9xm89xnxhHxD/4mv0qYm8POaMiztrW/Oc35ZwZgG2dsw+urWWGg4y7zFSoA/JNRGEO977sBmKOiDsOQ06a/PeuNpBb4zNkLeXq8Ke1A1yYxppnbkkqAAAAAElFTkSuQmCC'
redbutton = b'iVBORw0KGgoAAAANSUhEUgAAAGMAAAAnCAMAAAAsLAbdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAALhqYuVaWP5VVeSNi+KlogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPXdpGwAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjVkR1hSAAAAzElEQVRYR+2X0Q6EIAwEEej/fzIWaC2YXGJy3T4Y59EYJrtGKKndIAdkKWVzEB25OFB3zeKgKq94UBeLOTwNnSrrmoNcStrIGkUc3iEmEmU6MAqVDAdKUcqoqztIHgDI6sDFmG2xAxiD4bbYgYwxgiRwjB4kxoGtqpeV0DE4yOd4zOd4Towj4h98zX4VsbeHnFERZ21r/vObcs0MwLau2QfX1jLDQcZdZirUAfkmojCHe192AzFHxB2HISdN/nlXG8it8T9kLeXu8Ke1E2qb0ylAVTgZAAAAAElFTkSuQmCC'


blank = b'iVBORw0KGgoAAAANSUhEUgAAANUAAAFACAMAAAAcQsn1AAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAszD0iAAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAADsEAAA7BAbiRa+0AAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuNWRHWFIAAAFbSURBVHhe7c8BDQAwEMSg+Td908GnOODtolaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5WjlaOVo5Wjour7QMC20VI3QCuyAAAAABJRU5ErkJggg=='

plusfour = b'[encoded data, 10132 characters]'

wild = b'[encoded data, 9472 characters]'


b0 = b'[encoded data, 7600 characters]'
b1 = b'[encoded data, 6056 characters]'
b10 = b'[encoded data, 8876 characters]'
b11 = b'[encoded data, 6940 characters]'
b12 = b'[encoded data, 8432 characters]'
b2 = b'[encoded data, 7792 characters]'
b3 = b'[encoded data, 8020 characters]'
b4 = b'[encoded data, 7160 characters]'
b5 = b'[encoded data, 7988 characters]'
b6 = b'[encoded data, 8060 characters]'
b7 = b'[encoded data, 6184 characters]'
b8 = b'[encoded data, 8264 characters]'
b9 = b'[encoded data, 8152 characters]'
g0 = b'[encoded data, 7608 characters]'
g1 = b'[encoded data, 5912 characters]'
g10 = b'[encoded data, 9272 characters]'
g11 = b'[encoded data, 7440 characters]'
g12 = b'[encoded data, 8588 characters]'
g2 = b'[encoded data, 9940 characters]'
g3 = b'[encoded data, 8068 characters]'
g4 = b'[encoded data, 6956 characters]'
g5 = b'[encoded data, 7924 characters]'
g6 = b'[encoded data, 8440 characters]'
g7 = b'[encoded data, 6380 characters]'
g8 = b'[encoded data, 8532 characters]'
g9 = b'[encoded data, 8612 characters]'
p4 = b'[encoded data, 6664 characters]'
r0 = b'[encoded data, 8060 characters]'
r1 = b'[encoded data, 6200 characters]'
r10 = b'[encoded data, 9572 characters]'
r11 = b'[encoded data, 7564 characters]'
r12 = b'[encoded data, 8828 characters]'
back = b'[encoded data, 9472 characters]'
r2 = b'[encoded data, 7992 characters]'
r3 = b'[encoded data, 8288 characters]'
r4 = b'[encoded data, 7348 characters]'
r5 = b'[encoded data, 8372 characters]'
r6 = b'[encoded data, 8908 characters]'
r7 = b'[encoded data, 6852 characters]'
r8 = b'[encoded data, 9100 characters]'
r9 = b'[encoded data, 8868 characters]'
y0 = b'[encoded data, 8344 characters]'
y1 = b'[encoded data, 6628 characters]'
y10 = b'[encoded data, 9936 characters]'
y11 = b'[encoded data, 7820 characters]'
y12 = b'[encoded data, 9116 characters]'
y2 = b'[encoded data, 8492 characters]'
y3 = b'[encoded data, 8724 characters]'
y4 = b'[encoded data, 7752 characters]'
y5 = b'[encoded data, 8596 characters]'
y6 = b'[encoded data, 9080 characters]'
y7 = b'[encoded data, 7112 characters]'
y8 = b'[encoded data, 9240 characters]'
y9 = b'[encoded data, 9072 characters]'



Update = lambda elem, value: window.Element(elem).Update(value)

class BadInputError(Exception):
    pass


class Player():

    def __init__(self, name):
        self.id = None
        self.name = name
        self.type = 'Human'
        self.hand = Hand()
        self.legalCards = []
        self.wildCards = []
        self.valueChangeCards = []
        self.zeroCards = []
        self.canSkip = False
        self.canReverse = False
        self.canDrawTwo = False
        self.canDrawFour = False
        self.canValueChange = False
        self.drew = False
        self.scrollMax = 0
        self.points = 0
        self.forceDraw = 0

    def addCard(self, card):
        self.drew = True
        if self.forceDraw > 0:
            self.forceDraw -= 1
            self.drew = False
        self.hand.addCard(card)

    def beginTurn(self):
        self.drew = False

    def didDraw(self):
        return self.drew

    def getLegalCards(self, color, value, zeroChange=False):
        self.canSkip = False
        self.canReverse = False
        self.canDrawTwo = False
        self.canDrawFour = False
        self.canValueChange = False
        self.canZeroChange = False
        self.legalCards = []
        self.wildCards = []
        self.valueChangeCards = []
        self.zeroCards = []
        plusFours = []
        for card in self.hand:
            if card.isWild():
                if card.getValue() == '+4':
                    plusFours.append(card)
                else:
                    self.wildCards.append(card)
            elif zeroChange and card.isZero():
                self.canZero = True
                self.zeroCards.append(card)
            elif card.getColor() == color or card.getValue() == value:
                if card.getColor() != color:
                    self.canValueChange = True
                    self.valueChangeCards.append(card)
                if card.getValue() == "+2":
                    self.canDrawTwo = True
                elif card.getValue() == 'R':
                    self.canReverse = True
                elif card.getValue() == 'X':
                    self.canSkip = True
                self.legalCards.append(card)
        if len(self.legalCards) == 0 and len(plusFours) > 0:
            self.canDrawFour = True
            self.wildCards += plusFours

    def getValidCards(self):
        return self.legalCards

    def getAllValidCards(self):
        return self.legalCards + self.wildCards + self.zeroCards

    def hasLegalCard(self):
        return len(self.legalCards) > 0

    def addPoints(self, amount):
        if (self.points + amount) <= 999999999999999999999:
            self.points += amount

    def removeCard(self, index):
        return self.hand.removeCard(index)

    def assignID(self, identity):
        self.id = identity

    def getName(self):
        return self.name

    def getID(self):
        return self.id

    def getPoints(self):
        return self.points

    def getType(self):
        return self.type

    def getCardNum(self):
        return len(self.hand)

    def getHand(self, scrollNum=0, hide=False):
        return self.hand.show(scrollNum, hide)

    def getForceDraws(self):
        return self.forceDraw

    def addForceDraw(self, num):
        self.forceDraw += num

    def decreaseForceDraw(self):
        self.forceDraw -= 1

    def removeForceDraw(self):
        self.forceDraw = 0

    def checkCard(self, index):
        return self.hand.getCard(int(index))

    def discardHand(self):
        self.hand.discard()

    def __str__(self):
        return self.name

    def __repr__(self):
        return '({},{})'.format(self.name, self.points)


class Hand():
    ''''deck' (Deck) : Card's Color (rgby)
       'numberOfCards' (int) : Card's Value (0-9, R, X, W, +2, +4)'''

    def __init__(self, deck=None, numberOfCards=0):
        self.hand = []
        if deck != None:
            self.draw(deck, numberOfCards)

    def __iter__(self):
        return iter(self.hand)

    def __len__(self):
        return len(self.hand)

    def __getitem__(self, item):
        try:
            return self.hand[item]
        except:
            return ''

    def addCard(self, card):
        self.hand.append(card)

    def removeCard(self, index):
        index = int(index)
        if (0 <= index < len(self)):
            return self.hand.pop(index)

    def discard(self):
        self.hand = []

    def show(self, scrollNum=0, hide=False):
        return ''

    def getCard(self, index):
        return self.hand[index]

    def indexCard(self, card):
        return self.hand.index(card)


class GameSettings():
    playerIdentities = ('play1', 'play2', 'play3', 'play4')
    computerNames = ('Watson', 'SkyNet', 'Hal', 'Metal Gear')

    def __init__(self):
        self.playerStaging = []  # Where Player Objs Are Stored Before Game Starts
        self.players = {}  # ID : Player Obj
        self.numPlayers = 0
        self.useColor = True
        self.displayEffects = True
        self.hideComputerHands = True
        self.zeroChange = False
        self.computerSimulation = False
        self.mainMenuError = ''
        self.computerSpeed = 'normal'

    def canAddPlayer(self):
        return (self.numPlayers < 4)

    def canRemovePlayer(self):
        return (self.numPlayers > 0)

    def canBegin(self):
        return (self.numPlayers > 1)

    def addPlayer(self, player):
        self.playerStaging.append(player)
        self.numPlayers += 1

    def removePlayer(self, number):
        number -= 1
        del self.playerStaging[number]
        self.numPlayers -= 1

    def clearStaging(self):
        self.numPlayers = 0
        self.playerStaging = []

    def finalizePlayers(self):
        self.players.clear()
        identity = 0
        for player in self.playerStaging:
            playerID = GameSettings.playerIdentities[identity]
            player.assignID(playerID)
            self.players[playerID] = player
            identity += 1

    def getPlayerNum(self):
        return self.numPlayers

    def getComputerName(self):
        complete = False
        index = self.numPlayers
        while not complete:
            name = self.computerNames[index]
            complete = True
            for player in self.playerStaging:
                if player.getName() == name:
                    index += 1
                    if index >= len(self.computerNames):
                        index = 0
                        complete = False

        return self.computerNames[index]

    def getRandomIdentity(self):
        '''For Getting a Random Player for First Turn.'''
        return random.choice(self.players.keys())


    def changeComputerSpeed(self):
        return



class Deck():
    ''''shuffle' (bool) : shuffle deck.'''

    colors = ('red', 'yellow', 'green', 'blue')
    values = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'X', 'R', '+2')

    def __init__(self, populate):
        '''Initializes proper deck of 108 Uno Cards.'''
        self.deck = []
        if populate:
            self.populate(True)

    def __getitem__(self, index):
        return self.deck[index]

    def populate(self, shuffle=True):
        for color in self.colors:
            for value in self.values:
                self.deck.append(Card(color, value))
                if value != '0':
                    self.deck.append(Card(color, value))
        for i in range(4):
            self.deck.append(Card('wild', '+4'))
            self.deck.append(Card('wild', 'W'))
        if shuffle:
            self.shuffle()

    def __iter__(self):
        return iter(self.deck)

    def __len__(self):
        return len(self.deck)

    def draw(self):
        return self.deck.pop()

    def place(self, card):
        return self.deck.append(card)

    def insert(self, card):
        self.deck.insert(0, card)

    def shuffle(self):
        random.shuffle(self.deck)


class ComputerPlayer(Player):

    def __init__(self, name):
        super().__init__(name)
        self.type = 'Computer'
        self.begun = False
        self.colorsInHand = {'red': 0, 'blue': 0, 'green': 0, 'yellow': 0, 'wild': 0}
        self.colorsOutHand = {}
        self.currentColor = ""

    def addCard(self, card):
        Player.addCard(self, card)
        color = card.getColor()
        self.colorsInHand[color] += 1

    def indexCard(self, cardColor, cardValue):
        for card in self.hand:
            if card.getValue() == cardValue:
                if cardValue in ('+4', 'W'):
                    return self.hand.indexCard(card)
                else:
                    if card.getColor() == cardColor:
                        return self.hand.indexCard(card)
        raise ValueError("Card Cannot Be Found")

    def think(self, match):
        card = None
        self.currentColor = match.currentColor
        currentValue = match.currentValue
        zeroChangeRule = match.zeroChange
        twoPlayers = False
        previousTurnID = match.getNextTurn(True)
        nextTurnID = match.getNextTurn(False)
        previousPlayer = match.getPlayer(previousTurnID)
        # nextPlayer = match.getPlayer(nextTurnID)
        if previousTurnID == nextTurnID:
            twoPlayers = True
            if self.canSkip == False and self.canReverse == True:
                self.canSkip = True
            self.canReverse = False

        self.getLegalCards(self.currentColor, currentValue, zeroChangeRule)

        ### DRAW CASE ###

        if len(self.legalCards) == 0 and len(self.wildCards) == 0:
            return "d"

        else:

            ### NO LEGAL CARD, USE WILD CARD ###

            if len(self.legalCards) == 0:

                if zeroChangeRule and self.canZeroChange:
                    bestZeroColor = self.getBestColor(self.zeroCards)
                    card = self.getCardByColor(self.zeroCards, bestZeroColor)

                else:

                    if self.canDrawFour:
                        card = self.getCardByValue(self.wildCards, "+4")
                        # print(card)

                    else:
                        card = random.choice(self.wildCards)

            else:

                ### HAS LEGAL CARD ###

                if twoPlayers and self.canSkip:  # Always play a skip card in a two player game
                    # print("Shed Skip Strategy")
                    card = self.getCardByValue(self.legalCards, "R", "X")

                if self.canReverse and previousPlayer.didDraw():
                    # print("Reverse Strategy")
                    reverseCards = self.getAllCardsByValue(self.legalCards, "R")
                    for reverseCard in reverseCards:
                        if reverseCard.getColor() == self.currentColor:
                            card = reverseCard

                if self.canValueChange:
                    # Computer Can Value Change, However, Should it?
                    # Computer Checks to See if Value Change Color is Better Than Current
                    currentColorNum = self.colorsInHand[self.currentColor]
                    bestValueChangeColor = self.getBestColor(self.valueChangeCards)
                    if self.colorsInHand[bestValueChangeColor] > currentColorNum or len(self.valueChangeCards) == len(
                            self.legalCards):
                        card = self.getCardByColor(self.valueChangeCards, bestValueChangeColor)

                if card == None:
                    # print("Random Strategy")
                    card = random.choice(list(set(self.legalCards) - set(self.valueChangeCards)))

        color = card.getColor()
        self.colorsInHand[color] -= 1
        return str(self.indexCard(card.getColor(), card.getValue()))

    def getWildColor(self):
        maxKey = max(self.colorsInHand, key=self.colorsInHand.get)
        if maxKey == 'wild':
            return random.choice(('r', 'g', 'b', 'y'))
        else:
            return maxKey

    def getCardByValue(self, cardList, *values):
        for card in cardList:
            if card.getValue() in values:
                return card

    def getAllCardsByValue(self, cardList, *values):
        cards = []
        for card in cardList:
            if card.getValue() in values:
                cards.append(card)
        return cards

    def getCardByColor(self, cardList, *colors):
        for card in cardList:
            if card.getColor() in colors:
                return card

    def getBestColor(self, cardList):
        bestColor = None
        bestColorNum = 0
        for card in cardList:
            color = card.getColor()
            if self.colorsInHand[color] > bestColorNum:
                bestColor = color
                bestColorNum = self.colorsInHand[color]
        return bestColor


class Card():
    '''
    'suit' (string) : Card's Color (rgby)
    'rank' (string) : Card's Value (0-9, R, X, W, +2, +4)
    '''
    # 11 = reverse, 12 = +2
    # 0-9, X, R, W, +2, +4
    # '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'X', 'R', '+2', '+4', 'W'
    green_dict = {'0':g0, '1':g1, '2':g2, '3':g3, '4':g4, '5':g5, '6':g6, '7':g7, '8':g8, '9':g9, 'X':g10, 'R':g11, '+2':g12, '+4':plusfourgreen, 'W':wildgreen}
    red_dict = {'0':r0, '1':r1, '2':r2, '3':r3, '4':r4, '5':r5, '6':r6, '7':r7, '8':r8, '9':r9, 'X':r10, 'R':r11, '+2':r12, '+4':plusfourred, 'W':wildred}
    blue_dict = {'0':b0, '1':b1, '2':b2, '3':b3, '4':b4, '5':b5, '6':b6, '7':b7, '8':b8, '9':b9, 'X':b10, 'R':b11, '+2':b12, '+4':plusfourblue, 'W':wildblue}
    yellow_dict = {'0':y0, '1':y1, '2':y2, '3':y3, '4':y4, '5':y5, '6':y6, '7':y7, '8':y8, '9':y9, 'X':y10, 'R':y11, '+2':y12, '+4':plusfouryellow, 'W':wildyellow}
    green_cards = [g0, g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, g11, g12 ]
    red_cards = [r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12 ]
    blue_cards = [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12 ]
    yellow_cards = [y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12 ]
    wild_card = wild


    colors = {
        'red': '\033[91m',
        'green': '\033[92m',
        'yellow': '\033[93m',
        'blue': '\033[94m',
        'purple': '\033[95m',
        'cyan': '\033[96m',
        'white': '\033[97m',
        'wild': '',
        'dwild': '',
        'dred': '\033[31m',
        'dgreen': '\033[32m',
        'dyellow': '\033[33m',
        'dblue': '\033[34m',
        'dpurple': '\033[35m',
        'dcyan': '\033[36m',
        'dwhite': '\033[37m',
    }

    idMap = {
        'red': 'R', 'blue': 'B', 'green': 'G', 'yellow': 'Y', 'wild': 'W',
        '0': '0', '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9',
        '+2': '+', 'R': 'R', 'W': 'W', '+4': '$', 'X': 'X'
    }


    def __init__(self, color, value):
        '''Initializes Uno Card w/ Color and Value.'''
        self.wild = False  # Is wild card?
        self.zero = False
        self.cardID = '{}{}'.format(self.idMap[color], self.idMap[value])
        self.setColor(color)
        self.setValue(value)
        self.setPoints(value)

    #############################################

    ### -\/-  Retrieve Card Information  -\/- ###

    def __repr__(self):
        return "{},{}".format(self.color, self.value)

    def getBigNum(self, reverse, reverseSeed=0):
        '''Returns list of strings to draw card's value on the pile.'''
        return ''

    def getColor(self):
        '''Returns card's color.'''
        return self.color

    def getColorCode(self):
        '''Returns card's color code.'''
        return self.colorCode

    def getValue(self):
        '''Returns card's value.'''
        return self.value

    def getPoints(self):
        '''Returns card's point value.'''
        return self.points

    def getRow(self, rowNum, hide=False):
        return

    #############################################

    ### -\/-  Set Card Information  -\/- ###

    def setColor(self, color):
        '''Sets Card's color and escape code.'''
        if color == 'blue':
            self.color = 'blue'
            self.colorCode = self.colors['blue']
            self.colorCodeDark = self.colors['dblue']
        elif color == 'red':
            self.color = 'red'
            self.colorCode = self.colors['red']
            self.colorCodeDark = self.colors['dred']
        elif color == 'yellow':
            self.color = 'yellow'
            self.colorCode = self.colors['yellow']
            self.colorCodeDark = self.colors['dyellow']
        elif color == 'green':
            self.color = 'green'
            self.colorCode = self.colors['green']
            self.colorCodeDark = self.colors['dgreen']
        elif color == 'wild':  # No color modification
            self.wild = True
            self.color = 'wild'
            self.colorCodeDark = self.colors['dwild']
            self.colorCode = self.colors['wild']

    def setValue(self, value):
        if value in ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'X', 'R', '+2', '+4', 'W'):
            self.value = value
            self.displaySpace = ' '
            if len(value) == 2:
                self.displaySpace = ''
            if value == '0':
                self.zero = True

    def setPoints(self, value):
        if value in ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'):
            self.points = int(value)
        elif value in ("W", "+4"):
            self.points = 50
        else:
            self.points = 20

    #############################################

    ### -\/-  Wild Card Methods  -\/- ###

    def changeColor(self, color):
        '''Changes Card's Color, Intended for Wild Cards.'''
        self.setColor(color)

    def isWild(self):
        '''Returns if card is a wild card.'''
        return self.wild

    def isZero(self):
        return self.zero


class Match():
    elementsInit = {
        ### Names (final) ###
        'P1Name': '           ', 'P2Name': '           ', 'P3Name': '           ', 'P4Name': '           ',
        ### Card Values ###
        'P1Cards': '           ', 'P2Cards': '           ', 'P3Cards': '           ', 'P4Cards': '           ',
        ### Turn Colors / Hand###
        'P1Turn': '', 'P2Turn': '', 'P3Turn': '', 'P4Turn': '',
        'HName': '\t\t', 'HVisual': '', 'Hand': '',
        ### Deck ###
        'DNum': '', 'Deck': ['', '', '', '', '', '', '', '', ''],
        'PostDNum': '',
        ### Pile ###
        'uHeader': '\t\t\t\t', 'uMiddle': '   ', 'uLower': '   ',
        'oHeader': '\t\t\t',
        'oMiddle': ['\t\t\t', '\t\t\t', '\t\t\t', '\t\t\t', '\t\t\t', '\t\t\t', '\t\t\t', '\t\t\t'],
        ### Messages ###
        'Console': '', 'Error': ''
    }

    speeds = {'slow': 2, 'normal': 1, 'fast': 0}

    window = None

    def __init__(self, gs):
        ### Decks ###
        self.deck = Deck(True)
        self.pile = Deck(False)

        ### Player Information ###
        self.players = gs.players
        self.turnList = []
        self.handTitles = {'play1': '', 'play2': '', 'play3': '', 'play4': ''}

        ### Carry Information ###
        self.displayEffects = gs.displayEffects
        self.hideComputerHands = gs.hideComputerHands
        self.zeroChange = gs.zeroChange
        self.computerSpeed = self.speeds[gs.computerSpeed]
        self.simulation = gs.computerSimulation

        ### Data ###
        self.handPosition = 0  # For hand displays
        self.drawAmount = 0  # Used for force draws
        self.passes = 0  # Keep track of consecutive passes for emergency color change
        self.passMax = 0  # Max passes before color change
        self.turn = ''  # Current turn
        self.event = ''  # Wild, Reverse, Skip, etc
        self.wildColorChange = ''  # Specifies color to change wild card to
        self.currentColor = ''  # Current color
        self.currentValue = ''  # Current value
        self.winnerID = ''  # ID of Player who Won
        self.reverse = False  # Is turn order reversed
        self.turnComplete = False  # Is turn complete
        self.matchComplete = False  # Is the Game over?
        self.matchAbort = False  # Did the match conclude without a winner?
        self.forcedWild = False  # Force change wild

        self.max_cards_displayed = 0
        self.previous_card = None
        ### Initialize Names / Cards / Deck (Assuming New Game) ###
        self.elements = dict(self.elementsInit)

        keyStringName = 'P{}Name'
        keyStringCards = 'P{}Cards'

        for i in self.players:
            self.elements[keyStringName.format(i[-1])] = self.players[i].getName() + (
                        ' ' * (11 - len(self.players[i].getName())))
            self.elements[keyStringCards.format(i[-1])] = '  ' + (
                        ' ' * (3 - len(str(self.players[i].getCardNum())))) + str(
                self.players[i].getCardNum()) + ' Cards'

        self.elements['DNum'] = len(self.deck)

        if len(str(len(self.deck))) < 2:
            self.elements['PostDNum'] = '\t'

        j = 8
        for i in range(int(math.ceil(len(self.deck) / 12))):
            self.elements['Deck'][j] = '='
            j -= 1

        for key in GameSettings.playerIdentities:
            try:
                self.buildHandString(key)
                self.turnList += [key]
            except KeyError:
                pass

        self.passMax = len(self.turnList)
        # ----------------------------------------- Create the GUI window -----------------------------------------
        sg.SetOptions(background_color='black', element_background_color='black', text_color='white', border_width=0)
        Card = lambda image_data, key: sg.Button('', image_data=image_data, image_subsample=4, image_size=(60, 80), button_color=('white', 'black'), key=key)
        CardBig = lambda image_data: sg.Button(button_text='', image_data=image_data, image_subsample=1, image_size=(200, 300),
                                               button_color=('white', 'black'), pad=(0, 0))
        CardBig2 = lambda image_data: sg.Image(data=image_data, size=(100, 300), pad=(0, 0), background_color='black')

        Elem = lambda key: Match.window.Element(key)
        T = lambda text, key=None, font='Helvetica 16', **kwargs: sg.T(text, key=key, font=font, **kwargs )

        MyButton = lambda text, size=(6, 1), font='Helvetica 14', **kwargs: sg.Button(text, size=size, font=font, button_color=('white', 'green3'), **kwargs)
        OvalButton = lambda text, image_data, **kwargs: sg.Button(text, image_data=image_data, font='Helvetica 14', button_color=('white', 'black'), **kwargs)

        col_players = [
            [OvalButton('Quit', greenbutton, key='_QUIT_', visible=False)],
            [T('Player', '_P1_', text_color=yellow_color)],
            [T('Cards', '_C1_', text_color=yellow_color)],
            [T(' ' * 15)],
            [T('Player', '_P2_', text_color=red_color)],
            [T('Cards', '_C2_',  text_color=red_color)],
            [T(' ' * 15,'_S3_',  visible=False,)],
            [T('Player', '_P3_', visible=False)],
            [T('Cards', '_C3_', visible=False)],
            [T(' ' * 15, '_S4_', visible=False,)],
            [T('Player', '_P4_', visible=False)],
            [T('Cards', '_C4_', visible=False)],]

        col_cards_left = [
            [OvalButton('Draw', redbutton, key='_DRAW_')],
            [T('  '), Card(back, '_DRAW_')],
            [T('0 Cards Left', font='Helvetica 14', key='_CARDS_LEFT_')],
            [OvalButton('Pass', bluebutton, key='_PASS_')],]

        NUM_COLS = 10           # how many cards shown across bottom in 1 row
        NUM_ROWS = 4
        cards = []
        for j in range(NUM_ROWS):
            cards.append([sg.T(' ' * 5,font=('Any 5'))] + [Card(None, j*NUM_COLS+i) for i in range(NUM_COLS)])

        layout = [
            [sg.Column(col_cards_left),
             sg.Graph((400, 360), (0, 300), (400, 0), key='_GRAPH_', ),
             sg.Column(col_players)],
            [T(' ')],
            [T(' ')],
            [sg.T('', justification='center', size=(55, 2), font='Helvetica 18', key='_MESSAGES_')],
            [sg.T('', justification='center', size=(55, 2), font='Helvetica 18', key='_MESSAGES2_')],
                ]
        layout += cards

        Match.window = sg.Window('Uno',
                                 background_color='black',
                                 use_default_focus=False,
                                 disable_close=True,
                                 ).Layout(layout).Finalize()

    def clearShell(self):
        os.system('cls' if os.name == 'nt' else 'clear')

    def begin(self):
        self.elements['Console'] = 'Beginning Game, Click Draw to Deal.'
        self.drawScreen()
        self.enterBreak()
        self.eventDealCards()
        self.turn = random.choice(self.turnList)
        self.elements['Console'] = 'First turn will be {}. Click Draw'.format(self.players[self.turn].getName())
        self.drawScreen(True)
        self.enterBreak()
        self.placeCard()
        self.elements['P{}Turn'.format(self.turn[-1])] = '\033[93m'
        if self.event == 'wild':
            self.eventWildCard()
        elif self.event == 'reverse':
            self.eventReverse()

    def end(self, gs):
        if not self.matchAbort:
            points = 0
            self.elements['P{}Turn'.format(self.turn[-1])] = ''
            self.elements['Console'] = '{} Wins! Click Draw to Begin Point Tally'.format(
                self.players[self.winnerID].getName())
            self.drawScreen()
            self.enterBreak()

            for identity in self.turnList:
                if identity != self.winnerID:
                    self.turn = identity
                    self.elements['HName'] = self.handTitles[self.turn]
                    self.elements['P{}Turn'.format(self.turn[-1])] = '\033[93m'
                    while self.players[identity].getCardNum() > 0:
                        card = self.players[identity].removeCard(0)
                        points += card.getPoints()
                        self.elements['Console'] = '{} Won {} Points!'.format(self.players[self.winnerID].getName(),
                                                                              points)

                        keyStringCards = 'P{}Cards'
                        self.elements[keyStringCards.format(identity[-1])] = '  ' + (
                                    ' ' * (3 - len(str(self.players[identity].getCardNum())))) + str(
                            self.players[identity].getCardNum()) + ' Cards'
                        self.players[identity].maxScroll = math.ceil((self.players[identity].getCardNum() / 10) - 1)
                        if self.handPosition > self.players[identity].maxScroll:
                            self.handPosition -= 1
                        self.buildHandVisual(identity)

                        if self.displayEffects and not self.simulation:
                            self.drawScreen()
                            time.sleep(.1)
                    self.elements['P{}Turn'.format(self.turn[-1])] = ''

            self.players[self.winnerID].addPoints(points)
            self.elements['Console'] = '{} Won {} Points! Click Draw'.format(self.players[self.winnerID].getName(),
                                                                              points)
            self.drawScreen()
            self.enterBreak()

        gs.clearStaging()
        for identity in self.turnList:
            self.players[identity].discardHand()
            gs.addPlayer(self.players[identity])
        return gs

    def adjustCardAmount(self, playerID):
        keyStringCards = 'P{}Cards'
        self.elements[keyStringCards.format(playerID[-1])] = '  ' + (
                    ' ' * (3 - len(str(self.players[playerID].getCardNum())))) + str(
            self.players[playerID].getCardNum()) + ' Cards'
        self.players[playerID].maxScroll = math.ceil((self.players[playerID].getCardNum() / 10) - 1)
        if self.handPosition > self.players[playerID].maxScroll:
            self.handPosition -= 1
        self.buildHandVisual(playerID)

    def buildHandString(self, playerID):
        playerName = self.players[playerID].getName()
        if len(playerName) < 9:
            self.handTitles[playerID] = "{}'s Hand\t".format(self.players[playerID].getName())
        else:
            self.handTitles[playerID] = "{}'s Hand".format(self.players[playerID].getName())

    def buildHandVisual(self, playerID):
        string = '['
        for i in range(self.players[playerID].maxScroll + 1):
            if i == self.handPosition:
                string += '|'
            else:
                string += '-'
        string += ']'
        self.elements['HVisual'] = string

    def checkInput(self, playerInput):
        if playerInput == '':
            return {'valid': False, 'entry': playerInput}
        if playerInput.isnumeric():
            if int(playerInput) + (10 * self.handPosition) < self.players[self.turn].getCardNum():
                return {'valid': True, 'entry': str(int(playerInput) + (10 * self.handPosition)), 'type': 'card'}
            else:
                self.elements['Error'] = '{} is not a card.'.format(playerInput)
                return {'valid': False, 'entry': playerInput}
        else:
            playerInput = playerInput.lower()[0]
            if playerInput in ['<', '>', 'u', 'd', 'p', 'q', 's']:
                return {'valid': True, 'entry': playerInput}
            else:
                self.elements['Error'] = '{} is not a valid selection.'.format(playerInput)
                return {'valid': False, 'entry': playerInput}

    def checkColorInput(self, playerInput):
        if playerInput == '':
            return {'valid': False, 'entry': playerInput}
        playerInput = str(playerInput).lower()[0]
        if playerInput[0] == 'b':
            return {'valid': True, 'entry': 'blue'}
        elif playerInput[0] == 'r':
            return {'valid': True, 'entry': 'red'}
        elif playerInput[0] == 'g':
            return {'valid': True, 'entry': 'green'}
        elif playerInput[0] == 'y':
            return {'valid': True, 'entry': 'yellow'}
        return {'valid': False, 'entry': playerInput}

    def eventDealCards(self):
        if self.displayEffects and not self.simulation:
            self.elements['Console'] = 'Dealing Cards...'
        for i in ('play1', 'play2', 'play3', 'play4'):
            if i in self.players:
                for j in range(7):
                    self.dealCard(i)

    def eventReverse(self):
        if self.displayEffects and not self.simulation:
            hide = False
            if self.players[self.turn].getType() == "Computer":
                hide = self.hideComputerHands
            self.elements['Console'] = "Reverse Card Played! Reversing Turn Order.".format(
                self.players[self.turn].getName())
            self.drawScreen(hide)
            time.sleep(1)
            for i in range(10):
                cardBigNums = self.pile[0].getBigNum(self.reverse, i)
                self.elements['oMiddle'] = cardBigNums
                self.drawScreen(hide)
                if self.displayEffects and not self.simulation:
                    time.sleep(.1)
        cardBigNums = self.pile[0].getBigNum(self.reverse, 9)
        self.elements['oMiddle'] = cardBigNums
        self.reverse = not self.reverse
        self.event = ''

    def eventSkip(self):
        if self.displayEffects and not self.simulation:
            hide = False
            if self.players[self.turn].getType() == "Computer":
                hide = self.hideComputerHands
            self.elements['Console'] = "Skip Card Placed! Skipping {}'s Turn.".format(self.players[self.turn].getName())
            self.drawScreen(hide)
            time.sleep(1)
            for i in range(2):
                self.elements['P{}Turn'.format(self.turn[-1])] = '\033[91m'
                self.drawScreen(hide)
                time.sleep(.3)
                self.elements['P{}Turn'.format(self.turn[-1])] = ''
                self.drawScreen(hide)
                time.sleep(.3)
        self.turnComplete = True
        self.event = ''

    def eventWildCard(self):
        hide = False
        if not self.forcedWild:
            if self.players[self.turn].getType() == 'Human':
                self.elements['Console'] = 'Wild Card! Specifiy a Color'
                self.elements['Error'] = 'Specifiy A Color'
                self.drawScreen()
                while True:
                    layout=[[sg.T('Wild Card! Specify a Color')],
                            [sg.Combo(('Blue', 'Red', 'Green', 'Yellow'))],
                            [sg.OK()]]
                    window = sg.Window('Wild Card').Layout(layout)

                    event, values = window.Read()
                    try:
                        playerInput = values[0].lower()[0]
                    except:
                        playerInput = ' '
                    # playerInput = str(input("Color Change: "))
                    checked = self.checkColorInput(playerInput)
                    if checked['valid']:
                        window.Close()
                        break
            else:
                hide = self.hideComputerHands
                checked = self.checkColorInput(self.players[self.turn].getWildColor())
            self.wildColorChange = checked['entry']
        else:
            self.wildColorChange = self.checkColorInput(random.choice(('r', 'b', 'g', 'y')))['entry']
            self.forcedWild = False
        self.currentColor = self.wildColorChange
        self.elements['Error'] = ""
        if self.displayEffects and not self.simulation:
            self.elements['Console'] = 'Wild Card! Changing Color.'
            seed = 1
            for i in range(10):
                if seed > 4:
                    seed = 1
                self.drawScreen(hide, wildSeed=seed)
                time.sleep(.1)
                seed += 1
        self.pile[0].changeColor(self.wildColorChange)
        self.wildColorChange = ''
        cardBigNums = self.pile[0].getBigNum(self.reverse)
        self.elements['oHeader'] = '{}\u2666\u2666\u2666=========\u2666\u2666\u2666\033[0m\t'.format(
            self.pile[0].getColorCode())
        self.elements['oMiddle'] = cardBigNums
        self.event = ''

    def eventDraw(self):
        self.players[self.turn].addForceDraw(self.drawAmount)
        self.drawAmount = 0
        self.event = ''

    def dealCard(self, playerID):

        card = self.deck.draw()
        self.players[playerID].addCard(card)

        ### Adjust Hand Visual ###
        self.players[playerID].maxScroll = math.ceil((self.players[playerID].getCardNum() / 10) - 1)
        self.handPosition = self.players[playerID].maxScroll
        self.buildHandVisual(playerID)

        ### Adjust Player Tile ###
        keyStringCards = 'P{}Cards'
        self.elements[keyStringCards.format(playerID[-1])] = '  ' + (
                    ' ' * (3 - len(str(self.players[playerID].getCardNum())))) + str(
            self.players[playerID].getCardNum()) + ' Cards'

        ### Adjust Deck ###
        self.elements['DNum'] = len(self.deck)
        if len(str(len(self.deck))) < 2:
            self.elements['PostDNum'] = '\t'
        j = 8
        self.elements['Deck'] = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
        for i in range(math.ceil(len(self.deck) / 12)):
            self.elements['Deck'][j] = '='
            j -= 1

    def placeCard(self, card=None):
        if card == None:
            ### Used At Beginning For First Card ###
            card = self.deck.draw()
            self.elements['DNum'] = len(self.deck)

        cardColor = card.getColorCode()
        cardBigNums = card.getBigNum(self.reverse)

        self.currentColor = card.getColor()
        self.currentValue = card.getValue()

        self.pile.insert(card)
        self.elements['oHeader'] = '{}\u2666\u2666\u2666=========\u2666\u2666\u2666\033[0m\t'.format(cardColor)
        self.elements['oMiddle'] = cardBigNums

        if len(self.pile) > 1:
            previousCard = self.pile[1]
            previousCardColor = previousCard.getColorCode()
            self.elements['uHeader'] = '{}      \u2666\u2666\u2666=========\u2666\u2666\u2666\033[0m\t\t'.format(
                previousCardColor)
            self.elements['uMiddle'] = '{}| |\033[0m'.format(previousCardColor)
            self.elements['uLower'] = '{}\u2666\u2666\u2666\033[0m'.format(previousCardColor)

        if self.currentColor == 'wild':
            self.event = 'wild'

        if self.currentValue == 'X':
            self.event = 'skip'
        elif self.currentValue == 'R':
            if len(self.players) > 2:
                self.event = 'reverse'
            else:
                self.event = 'skip'
        elif self.currentValue == '+4':
            self.drawAmount = 4
        elif self.currentValue == '+2':
            self.drawAmount = 2
        self.passes = 0

    def extractCard(self, playerID, index):
        card = self.players[playerID].removeCard(index)
        if self.players[playerID].getCardNum() == 0:
            self.matchComplete = True
            self.winnerID = self.turn
        self.adjustCardAmount(playerID)
        return card

    def enterBreak(self):
        if not self.simulation:
            while True:
                if Match.window is None:
                    break
                event, values = Match.window.Read()
                if event == '_DRAW_':
                    break
                if event == '_QUIT_':
                    if sg.PopupYesNo('Do you really want to quit?') == 'Yes':
                        self.matchAbort = True
                        self.matchComplete = True

    def nextTurn(self):
        self.turnComplete = False
        self.handPosition = 0
        turnType = self.players[self.turn].getType()
        self.players[self.turn].beginTurn()
        ### Prepare Hand Visuals ###

        self.elements['HName'] = self.handTitles[self.turn]
        self.buildHandVisual(self.turn)

        if self.event == 'skip':
            self.eventSkip()
        elif self.drawAmount > 0:
            self.eventDraw()
        #--------------  Main GUI event loop ---------------
        while not self.turnComplete:
            if turnType == 'Human':
                self.players[self.turn].getLegalCards(self.currentColor, self.currentValue, self.zeroChange)
                if len(self.deck) > 0:
                    self.elements['Console'] = 'Select a card or Draw'
                else:
                    self.players[self.turn].removeForceDraw()
                    self.elements['Console'] = 'Select a card, Draw, or Pass'
                if self.players[self.turn].getForceDraws() > 0:
                    self.elements['Error'] = 'Draw Card Played! Draw {} cards.'.format(
                        self.players[self.turn].getForceDraws())
                self.drawScreen()
                ## GUI INPUT
                event, values = Match.window.Read()

                playerInput = str(event)
                if event == '_DRAW_':
                    playerInput = 'd'
                elif event == '_PASS_':
                    playerInput = 's'
                elif event == '_QUIT_':
                    if sg.PopupYesNo('Do you really want to quit?') == 'Yes':
                        self.matchAbort = True
                        self.matchComplete = True
                        break

                if playerInput == '<':
                    self.handPosition -= 1
                    if self.handPosition == -1:
                        self.handPosition = self.players[self.turn].maxScroll
                    self.buildHandVisual(self.turn)
                elif playerInput == '>':
                    self.handPosition += 1
                    if self.handPosition > self.players[self.turn].maxScroll:
                        self.handPosition = 0
                    self.buildHandVisual(self.turn)
                elif playerInput == 'd':
                    if len(self.deck) > 0:
                        self.elements['Error'] = ''
                        self.dealCard(self.turn)
                    else:
                        self.elements['Error'] = "Cannot Draw. Deck is Empty"
                elif playerInput == 'p':
                    pauseOutput = self.pauseScreen()
                    if pauseOutput == 'quit':
                        self.matchComplete = True
                        self.turnComplete = True
                        self.winnerID = 'play1'
                        self.matchAbort = True
                elif playerInput == 's':
                    if len(self.deck) > 0:
                        self.elements['Error'] = "Cannot pass until Deck is empty."
                    elif len(self.players[self.turn].getAllValidCards()) > 0:
                        self.elements['Error'] = "Cannot pass while having playable cards."
                    else:
                        self.turnComplete = True
                        self.passes += 1
                        if self.passes == self.passMax:
                            self.forcedWild = True
                            self.event = 'wild'
                            self.passes = 0
                elif playerInput.isnumeric():
                    if self.players[self.turn].getForceDraws() == 0:
                        cardCheck = self.players[self.turn].checkCard(playerInput)
                        if cardCheck in self.players[self.turn].getAllValidCards():
                            card = self.extractCard(self.turn, playerInput)
                            self.placeCard(card)
                            self.elements['Error'] = ""
                            self.turnComplete = True
                        else:
                            self.elements['Error'] = "Card Doesn't Match The Color {} or Value {}!".format(
                                self.currentColor, self.currentValue)
                    else:
                        pass

            elif turnType == 'Computer':
                event, values = Match.window.Read(timeout=0)
                if event == '_QUIT_':
                    if sg.PopupYesNo('Do you really want to quit?') == 'Yes':
                        self.matchAbort = True
                        self.matchComplete = True
                        break

                self.elements['Console'] = '{}\'s Turn'.format(self.players[self.turn].getName())
                self.drawScreen(self.hideComputerHands)
                if not self.simulation:
                    time.sleep(self.computerSpeed)
                # str(input())
                while (True):
                    if self.displayEffects and not self.simulation:
                        time.sleep(.2)
                    if self.players[self.turn].getForceDraws() > 0 and len(self.deck) > 0:
                        cardIndex = 'd'
                    else:
                        cardIndex = self.players[self.turn].think(self)
                    if cardIndex.isnumeric():
                        card = self.extractCard(self.turn, int(cardIndex))
                        if card.getColor() != self.currentColor:
                            self.resetDrawBool()
                        self.placeCard(card)
                        self.turnComplete = True
                        break
                    else:
                        if cardIndex == 'd':
                            if len(self.deck) > 0:
                                self.dealCard(self.turn)
                                self.drawScreen(self.hideComputerHands)
                            else:
                                self.turnComplete = True
                                self.players[self.turn].removeForceDraw()
                                self.passes += 1
                                if self.passes == self.passMax:
                                    self.forcedWild = True
                                    self.event = 'wild'
                                    self.passes = 0
                                break

            ### DECODE INPUT ###
        if self.event == 'reverse':
            self.eventReverse()
        elif self.event == 'wild':
            self.eventWildCard()

        # Clear Current Turn
        self.elements['P{}Turn'.format(self.turn[-1])] = ''
        # Prepare Next Turn
        self.turn = self.getNextTurn()
        self.elements['P{}Turn'.format(self.turn[-1])] = '\033[93m'

    # --------------------------------------- drawScreen ---------------------------------------
    def drawScreen(self, hide=False, wildSeed=0):
        if Match.window is None:
            return
        def get_card_graphic(color, value):
            dict = Card.red_dict
            if color == 'red':
                dict = Card.red_dict
            if color == 'blue':
                dict = Card.blue_dict
            if color == 'green':
                dict = Card.green_dict
            if color == 'yellow':
                dict = Card.yellow_dict
            card_graphic = dict[value] if value != '' else Card.wild_card
            return card_graphic

        Update = lambda key, value, **kwargs: Match.window.Element(key).Update(value, **kwargs)
        elem = lambda key: self.elements[key]
        if self.simulation:
            return

        currentTurn = self.turn
        if currentTurn == '':
            currentTurn = self.turnList[-1]
            hide = True

        Update('_MESSAGES_', elem('Console'))
        Update('_MESSAGES2_', elem('Error'))
        Update('_P1_', elem('P1Name'))
        Update('_C1_', elem('P1Cards'))
        Update('_P2_', elem('P2Name'))
        Update('_C2_', elem('P2Cards'))
        if elem('P3Name') != '           ':
            Update('_P3_', elem('P3Name'), visible=True)
            Update('_C3_', elem('P3Cards'), visible=True)
            Update('_S3_', None, visible=True)
        if elem('P4Name') != '           ':
            Update('_P4_', elem('P4Name'), visible=True)
            Update('_C4_', elem('P4Cards'), visible=True)
            Update('_S4_',None, visible=True)
        Update('_CARDS_LEFT_', '{} Cards Left'.format(elem('DNum')))
        # hand = self.players[currentTurn].hand
        hand = self.players[currentTurn].hand.hand
        i=0
        for i, card in enumerate(hand):
            if card.value == 'W':
                card_graphic = wild
            elif card.value == '+4':
                card_graphic = plusfour
            else:
                card_graphic = get_card_graphic(card.color, card.value)
            if hide:
                card_graphic = back

            Update(i, '', image_data=card_graphic, image_subsample=4, image_size=(60, 80), visible=True)
        else:
            for x in range(i+1, self.max_cards_displayed+1):
                Update(x, '', image_data=blank, image_subsample=4, image_size=(60, 80), visible=True)
            self.max_cards_displayed = max(i, self.max_cards_displayed)

        card_graphic = get_card_graphic(self.currentColor, self.currentValue)

        if self.previous_card is not None:
            Match.window.Element('_GRAPH_').DrawImage(data=self.previous_card, location=(0,0))
        Match.window.Element('_GRAPH_').DrawImage(data=card_graphic, location=(100,40))

        self.previous_card = card_graphic

        Match.window.Refresh()

        return ''

    def pauseScreen(self):
        return

    def isComplete(self):
        return self.matchComplete

    def next(self):
        self.turn = self.getNextTurn()

    def getNextTurn(self, forceReverse=False):
        if forceReverse:
            reverse = not self.reverse
        else:
            reverse = self.reverse
        currentIndex = self.turnList.index(self.turn)
        if not reverse:
            if (currentIndex + 1) == len(self.turnList):
                return self.turnList[0]
            else:
                return self.turnList[currentIndex + 1]
        else:
            if currentIndex == 0:
                return self.turnList[len(self.turnList) - 1]
            else:
                return self.turnList[currentIndex - 1]

    def getPlayer(self, playerID):
        return self.players[playerID]

    def resetDrawBool(self):
        for identity in self.players:
            self.players[identity].drew = False

def clearShell():
    os.system('cls' if os.name == 'nt' else 'clear')

def mainMenu():
    gs = GameSettings()

    PLAYER_TYPE_HUMAN = 'HUMAN'
    PLAYER_TYPE_COMPUTER = 'COMPUTER'
    computer_names = ('Watson', 'SkyNet', 'Hal', 'Metal Gear')
    layout = [
        [sg.Button('1 - No Player', size=(20,5), key=0, pad=(1,1)),
         sg.Button('2 - No Player', size=(20, 5), key=1, pad=(1,1), disabled=True)],
        [sg.Button('3 - No Player', size=(20, 5), key=2, pad=(1,1), disabled=True),
         sg.Button('4 - No Player', size=(20, 5), key=3, pad=(1,1), disabled=True)],
        [sg.Text('Enter player 1 (none) name', key='_TXT_'), sg.Input(key='_IN_', do_not_clear=True, size=(22,1)), sg.Button('Set name')],
        [sg.Button('Begin'), sg.Button('Settings')],]

    Elem = lambda key: window.Element(key)

    button_states = [0] * 4
    player_names = [''] * 4
    player_types = [None] * 4

    window = sg.Window('Uno Setup', border_depth=0).Layout(layout)

    current_player = 0
    while True:             # Event Loop
        event, values = window.Read()
        # print(event, values)
        if event is None or event == 'Exit':
            break
        if event == 'Begin':
            window.Hide()
            gs.finalizePlayers()
            # ---------------------- START THE MATCH ----------------------
            gs = playMatch(gs)
            for i, id in enumerate(gs.players):
                player = gs.players[id]
                window.Element(i).Update('{} - {}\n{} points'.format(i+1, player.name, player.points))
            window.UnHide()
            window.Refresh()
        elif event == 'Settings':
            layout2 = [[sg.Checkbox('Hide computer hands', key='_HIDE_', default=True)],
                       [sg.T('Computer speed')],
                       [sg.Radio('Fast', 1, key='_FAST_')],
                       [sg.Radio('Normal', 1, key='_NORMAL_', default=True)],
                       [sg.Radio('Slow', 1, key='_SLOW_')],
                       [sg.OK(), sg.Cancel()]
                       ]
            win2 = sg.Window('Settings').Layout(layout2)
            ev, vals = win2.Read()
            win2.Close()
            if ev == 'OK':
                if vals['_NORMAL_']:
                    gs.computerspeed = 'normal'
                if vals['_FAST_']:
                   gs.computerspeed = 'slow'
                if vals['_SLOW_']:
                   gs.computerspeed = 'slow'
                gs.hideComputerHands = vals['_HIDE_']
        elif event == 'Set name':
            if values['_IN_'] in player_names:
                sg.Popup('Duplicate - Cannot set to same as other players name')
            else:
                Elem(current_player).Update(str(current_player+1) + ' - '+values['_IN_'])
                player_names[current_player] = values['_IN_']
                Elem(current_player + (current_player < 3)).Update(disabled=False)
                if player_types[current_player] == PLAYER_TYPE_HUMAN:
                    addPlayer(gs, values['_IN_'])
                else:
                    addComputer(gs, values['_IN_'])
        elif 0 <= event <= 3:
            current_player = event
            state = button_states[event] = button_states[event]+1 if button_states[event] < 2 else 0
            Elem('_TXT_').Update('Enter Player %s (%s) Name'%(str(current_player+1), ('human', 'computer', 'none')[state-1]))
            if state == 1:
                Elem('_IN_').SetFocus()
                Elem('_IN_').Update('Player name', select=True)
                Elem(event).Update(str(event+1) +' - Player')
                player_types[event] = PLAYER_TYPE_HUMAN
            elif state == 2:
                Elem('_IN_').SetFocus()
                Elem('_IN_').Update(computer_names[event], select=True)
                Elem(event).Update(str(event+1) + ' - ' + computer_names[event])
                player_types[event] = PLAYER_TYPE_COMPUTER
            elif state == 0:
                Elem(event).Update(str(event+1) +' - No Player')
                Elem('_IN_').SetFocus()
                Elem('_IN_').Update('', select=True)
                for i in range(event):
                    pass
                player_types[event] = None
                player_names[event] = ''
                for i in range(event+1,4):
                    Elem(i).Update(text='%s - No player'%i, disabled=True)
                    player_names[i] = ''
                    try:
                        gs.removePlayer(i)
                    except:
                        pass
    window.Close()
    return player_names


def playMatch(gs):
    m = Match(gs)
    m.begin()
    Match.window.Element('_QUIT_').Update(visible=True)
    while (not m.isComplete()):
        m.nextTurn()
    gs = m.end(gs)
    Match.window.Close()
    Match.window = None
    return gs


def addPlayer(gs, name):
    p = Player(name)
    gs.addPlayer(p)
    gs.mainMenuError = ""

    return gs


def addComputer(gs, name):
    c = ComputerPlayer(name)
    gs.addPlayer(c)

    return gs



if __name__ == "__main__":
    mainMenu()

