import random
import json
import math
import datetime

from shutil import copyfile

from funcs import logThis, replaceMultiple, getName
from . import money, blackjackDraw

# Shuffles the blackjack cards
def blackjackShuffle(decks,channel):
    logThis("Shuffling the blackjack deck")

    with open("resources/games/deckofCards.txt","r") as f:
        deck = f.read()
    
    allDecks = deck.split("\n") * decks
    random.shuffle(allDecks)
    data = "\n".join(allDecks)

    with open("resources/games/blackjackCards/"+channel+".txt","w") as f:
        f.write(data)

    # Creates hilo file
    logThis("creating hilo/"+channel+".txt.")
    data = "0"
    with open("resources/games/hilo/"+channel+".txt","w") as f:
        f.write(data)

    return

# Calculates the value of a blackjack hand
def calcHandValue(hand : list):
    logThis("Calculating hand value")
    values = [0]

    for card in hand:
        cardValue = card[0]
        cardValue = replaceMultiple(cardValue,["0","k","q","j"],"10")
        if cardValue == "a":
            length = len(values)
            for x in range(length):
                values.append(values[x] + 11)
                values[x] += 1
        else:
            for x in range(len(values)):
                values[x] += int(cardValue)
    
    values.sort()

    handValue = values[0]
    for value in values:
        if value <= 21:
            handValue = value

    logThis("Calculated "+str(hand)+" to be "+str(handValue))

    return handValue

# Draws a card from the deck
def drawCard(channel):
    logThis("drawing a card")
    with open("resources/games/blackjackCards/"+channel+".txt","r") as f:
        cards = f.read().split("\n")
    with open("resources/games/hilo/"+channel+".txt", "r") as f:
        data = int(f.read())
    
    drawnCard = cards.pop(0)
    deck = "\n".join(cards)
    
    value = calcHandValue([drawnCard])

    if value <= 6:
        data += 1
    elif value >= 10:
        data -= 1

    with open("resources/games/blackjackCards/"+channel+".txt","w") as f:
        f.write(deck)
    with open("resources/games/hilo/"+channel+".txt", "w") as f:
        f.write(str(data))

    return drawnCard

# Dealer draws a card and checks if they should draw another one
def dealerDraw(channel):
    with open("resources/games/games.json", "r") as f:
        data = json.load(f)
    
    done = False
    dealerHand = data["blackjack games"][channel]["dealer hand"]

    if calcHandValue(dealerHand) < 17:
        data["blackjack games"][channel]["dealer hand"].append(drawCard(channel))
    else:
        done = True
    
    if calcHandValue(dealerHand) > 21:
        data["blackjack games"][channel]["dealer busted"] = True

    with open("resources/games/games.json", "w") as f:
        json.dump(data,f,indent=4)
    
    return done

# Goes to the next round and calculates some stuff
def blackjackContinue(channel):
    logThis("Continuing blackjack game")
    with open("resources/games/games.json", "r") as f:
        data = json.load(f)

    done = False

    data["blackjack games"][channel]["round"] += 1

    with open("resources/games/games.json", "w") as f:
        json.dump(data,f,indent=4)
    
    allStanding = True
    preAllStanding = True
    message = "All players are standing. The dealer now shows his cards and draws."

    if data["blackjack games"][channel]["all standing"]:
        logThis("All are standing")
        with open("resources/games/games.json", "w") as f:
            json.dump(data,f,indent=4)

        done = dealerDraw(channel)
        message = "The dealer draws a card."

        with open("resources/games/games.json", "r") as f:
            data = json.load(f)

    logThis("Testing if all are standing")
    for user in data["blackjack games"][channel]["user hands"]:
        try:
            data["blackjack games"][channel]["user hands"][user], allStanding, preAllStanding = testIfStanding(data["blackjack games"][channel]["user hands"][user],allStanding,preAllStanding,True) 
        except:
            logThis("Error in testing if all are standing (error code 1331)")

    if allStanding:
        data["blackjack games"][channel]["all standing"] = True
        with open("resources/games/games.json", "w") as f:
            json.dump(data,f,indent=4)
    else:
        with open("resources/games/games.json", "w") as f:
            json.dump(data,f,indent=4)

    try:
        blackjackDraw.drawImage(channel)
    except:
        logThis("Error drawing blackjack table (error code 1340)")

    if allStanding:
        if done == False:
            return message, True, done
        else:
            return "The dealer is done drawing cards", True, done
    elif preAllStanding:
        return "", True, done
    else:
        if data["blackjack games"][channel]["round"] == 1:
            firstRoundMessage = ". You can also double down with \"!blackjack double\" or split with \"!blackjack split\""
        else:
            firstRoundMessage = ""
        return "You have 2 minutes to either hit or stand with \"!blackjack hit\" or \"!blackjack stand\""+firstRoundMessage+". It's assumed you're standing if you don't make a choice.", False, done

def testIfStanding(hand,allStanding,preAllStanding,topLevel):
    if hand["hit"] == False:
        hand["standing"] = True

    if hand["standing"] == False:
        allStanding = False

        if calcHandValue(hand["hand"]) >= 21 or hand["doubled"]:
            hand["standing"] = True
        else:
            preAllStanding = False

    hand["hit"] = False

    if topLevel:
        if hand["split"] >= 1:
            hand["other hand"], allstanding, preAllStanding = testIfStanding(hand["other hand"],allStanding,preAllStanding,False)
        if hand["split"] >= 2:
            hand["third hand"], allstanding, preAllStanding = testIfStanding(hand["third hand"],allStanding,preAllStanding,False)
        if hand["split"] >= 3:
            hand["fourth hand"], allstanding, preAllStanding = testIfStanding(hand["fourth hand"],allStanding,preAllStanding,False)
    
    return hand, allStanding, preAllStanding


# When players try to hit
def blackjackHit(channel,user,handNumber = 0):
    with open("resources/games/games.json", "r") as f:
        data = json.load(f)

    if user in data["blackjack games"][channel]["user hands"]:
        
        hand, handNumber = getHandNumber(data["blackjack games"][channel]["user hands"][user],handNumber)
        print(hand)

        if hand != None:                
            if data["blackjack games"][channel]["round"] > 0:
                if hand["hit"] == False:
                    if hand["standing"] == False:
                        hand["hand"].append(drawCard(channel))
                        hand["hit"] = True

                        handValue = calcHandValue(hand["hand"])
                        
                        if handValue > 21:
                            hand["busted"] = True
                        
                        if handNumber == 2:
                            data["blackjack games"][channel]["user hands"][user]["other hand"] = hand
                        elif handNumber == 3:
                            data["blackjack games"][channel]["user hands"][user]["third hand"] = hand
                        elif handNumber == 4:
                            data["blackjack games"][channel]["user hands"][user]["fourth hand"] = hand
                        else:
                            data["blackjack games"][channel]["user hands"][user] = hand

                        with open("resources/games/games.json", "w") as f:
                            json.dump(data,f,indent=4)

                        response = "accept"
                        roundDone = isRoundDone(data["blackjack games"][channel])

                        return response + str(roundDone)[0] + str(data["blackjack games"][channel]["round"]) 
                    else:
                        logThis(user+" is already standing")
                        return "You can't hit when you're standing"
                else:
                    logThis(user+" has already hit this round")
                    return "You've already hit this round"
            else:
                logThis(user+" tried to hit on the 0th round")
                return "You can't hit before you see your cards"
        else:
            logThis(user+" didn't specify a hand")
            return "You need to specify a hand"
    else:
        logThis(user+" tried to hit without being in the game")
        return "You have to enter the game before you can hit"


# When players try to double down
def blackjackDouble(channel,user,handNumber = 0):
    with open("resources/games/games.json", "r") as f:
        data = json.load(f)
    
    if user in data["blackjack games"][channel]["user hands"]:
        hand, handNumber = getHandNumber(data["blackjack games"][channel]["user hands"][user],handNumber)
        
        if hand != None:
            if data["blackjack games"][channel]["round"] > 0:
                if hand["hit"] == False:
                    if hand["standing"] == False:
                        if len(hand["hand"]) == 2:
                            bet = hand["bet"]
                            if money.checkBalance(user) >= bet:
                                money.addMoney(user,-1 * bet)
                                with open("resources/games/games.json", "r") as f:
                                    data = json.load(f)

                                hand["hand"].append(drawCard(channel))
                                hand["hit"] = True
                                hand["doubled"] = True
                                hand["bet"] += bet

                                handValue = calcHandValue(hand["hand"])

                                
                                if handValue > 21:
                                    hand["busted"] = True
                                
                                if handNumber == 2:
                                    data["blackjack games"][channel]["user hands"][user]["other hand"] = hand
                                elif handNumber == 3:
                                    data["blackjack games"][channel]["user hands"][user]["third hand"] = hand
                                elif handNumber == 4:
                                    data["blackjack games"][channel]["user hands"][user]["fourth hand"] = hand
                                else:
                                    data["blackjack games"][channel]["user hands"][user] = hand

                                with open("resources/games/games.json", "w") as f:
                                    json.dump(data,f,indent=4)

                                roundDone = isRoundDone(data["blackjack games"][channel])

                                return "Adding another "+str(bet)+" GwendoBucks to "+getName(user)+"'s bet and drawing another card.",str(roundDone)[0] + str(data["blackjack games"][channel]["round"]) 
                            else:
                                logThis(user+" doesn't have enough GwendoBucks")
                                return "You don't have enough GwendoBucks",""
                        else:
                            logThis(user+" tried to double on round "+str(data["blackjack games"][channel]["round"]))
                            return "You can only double down on the first round",""    
                    else:
                        logThis(user+" is already standing")
                        return "You can't double when you're standing",""
                else:
                    logThis(user+" has already hit this round")
                    return "You've already hit this round",""
            else:
                logThis(user+" tried to double on the 0th round")
                return "You can't double down before you see your cards",""
        else:
            logThis(user+" didn't specify a hand")
            return "You need to specify which hand"
    else:
        logThis(user+" tried to double without being in the game")
        return "You can't double when you're not in the game",""

# When players try to stand
def blackjackStand(channel,user,handNumber = 0):
    with open("resources/games/games.json", "r") as f:
        data = json.load(f)

    if user in data["blackjack games"][channel]["user hands"]:
        
        hand, handNumber = getHandNumber(data["blackjack games"][channel]["user hands"][user],handNumber)

        if hand != None:
            if data["blackjack games"][channel]["round"] > 0:
                if hand["hit"] == False:
                    if hand["standing"] == False:
                        hand["standing"] = True
                        with open("resources/games/games.json", "w") as f:
                            json.dump(data,f,indent=4)

                        response = "accept"
                        roundDone = isRoundDone(data["blackjack games"][channel])

                        return response + str(roundDone)[0] + str(data["blackjack games"][channel]["round"])  
                    else:
                        logThis(user+" is already standing")
                        return "You're already standing"
                else:
                    logThis(user+" has already hit this round")
                    return "You've already hit this round"
            else:
                logThis(user+" tried to stand on the first round")
                return "You can't stand before you see your cards"
        else:
            logThis(user+" didn't specify a hand")
            return "You need to specify which hand"
    else:
        logThis(user+" tried to stand without being in the game")
        return "You have to enter the game before you can stand"

# When players try to split
def blackjackSplit(channel,user,handNumber = 0):
    with open("resources/games/games.json", "r") as f:
        data = json.load(f)

    if data["blackjack games"][channel]["user hands"][user]["split"] == 0:
        hand = data["blackjack games"][channel]["user hands"][user]
        newHand = data["blackjack games"][channel]["user hands"][user]["other hand"]
        handNumber = 0
        otherHand = 2
    else:
        if handNumber != 0:
            if handNumber == 1:
                hand = data["blackjack games"][channel]["user hands"][user]
            elif handNumber == 2:
                hand = data["blackjack games"][channel]["user hands"][user]["other hand"]
            elif handNumber == 3:
                hand = data["blackjack games"][channel]["user hands"][user]["third hand"]
            else:
                logThis(user+" tried to hit without specifying which hand")
                return "You have to specify the hand you're hitting with."
            
            if data["blackjack games"][channel]["user hands"][user]["split"] == 1:
                newHand = data["blackjack games"][channel]["user hands"][user]["third hand"]
                otherHand = 3
            else:
                newHand = data["blackjack games"][channel]["user hands"][user]["fourth hand"]
                otherHand = 4
        else:
            logThis(user+" tried to split without specifying which hand")
            return "You have to specify the hand you're splitting.",""

    if data["blackjack games"][channel]["user hands"][user]["split"] < 3:
        if data["blackjack games"][channel]["round"] != 0:
            if hand["hit"] == False:
                if hand["standing"] == False:
                    if len(hand["hand"]) == 2:
                        firstCard = calcHandValue([hand["hand"][0]])
                        secondCard = calcHandValue([hand["hand"][1]])
                        if firstCard == secondCard:
                            bet = hand["bet"]
                            if money.checkBalance(user) >= bet:
                                money.addMoney(user,-1 * bet)
                                with open("resources/games/games.json", "r") as f:
                                    data = json.load(f)

                                
                                hand["hit"] = True
                                newHand["hit"] = True

                                newHand = {
                                        "hand":[],"bet":0,"standing":False,"busted":False,
                                        "blackjack":False,"hit":True,"doubled":False}

                                newHand["bet"] = hand["bet"]

                                newHand["hand"].append(hand["hand"].pop(1))
                                newHand["hand"].append(drawCard(channel))
                                hand["hand"].append(drawCard(channel))
                                
                                handValue = calcHandValue(hand["hand"])
                                otherHandValue = calcHandValue(newHand["hand"])
                                if handValue > 21:
                                    hand["busted"] = True
                                elif handValue == 21:
                                    hand["blackjack"] = True
                                
                                if otherHandValue > 21:
                                    newHand["busted"] = True
                                elif otherHandValue == 21:
                                    newHand["blackjack"] = True

                                if handNumber == 2:
                                    data["blackjack games"][channel]["user hands"][user]["other hand"] = hand
                                elif handNumber == 3:
                                    data["blackjack games"][channel]["user hands"][user]["third hand"] = hand
                                else:
                                    data["blackjack games"][channel]["user hands"][user] = hand
                                
                                if otherHand == 3:
                                    data["blackjack games"][channel]["user hands"][user]["third hand"] = newHand
                                elif otherHand == 4:
                                    data["blackjack games"][channel]["user hands"][user]["fourth hand"] = newHand
                                else:
                                    data["blackjack games"][channel]["user hands"][user]["other hand"] = newHand
                                
                                data["blackjack games"][channel]["user hands"][user]["split"] += 1

                                with open("resources/games/games.json", "w") as f:
                                    json.dump(data,f,indent=4)

                                roundDone = isRoundDone(data["blackjack games"][channel])

                                return "Splitting "+getName(user)+"'s hand into 2. Adding their original bet to the second hand. You can use \"!Blackjack hit/stand/double 1\" and \"!Blackjack hit/stand/double 2\" to play the different hands.",str(roundDone)[0] + str(data["blackjack games"][channel]["round"]) 
                            else:
                                logThis(user+" doesn't have enough GwendoBucks")
                                return "You don't have enough GwendoBucks",""
                        else:
                            logThis(user+" tried to split 2 different cards")
                            return "Your cards need to have the same value to split",""
                    else:
                        logThis(user+" tried to split later than they could")
                        return "You can only split on the first round",""
                else:
                    logThis(user+" is already standing")
                    return "You can't split when you're standing",""
            else:
                logThis(user+" has already hit this round")
                return "You've already hit this round",""
        else:
            logThis(user+" tried to split on the 0th round")
            return "You can't split before you see your cards",""
    else:
        logThis(user+" tried to split more than three times")
        return "You can only split 3 times",""

# Player enters the game and draws a hand
def blackjackPlayerDrawHand(channel,user,bet):
    with open("resources/games/games.json", "r") as f:
        data = json.load(f)

    logThis(getName(user)+" is trying to join the game in "+channel)
    
    if channel in data["blackjack games"]:
        if user not in data["blackjack games"][channel]["user hands"]:
            if len(data["blackjack games"][channel]["user hands"]) < 5:
                if data["blackjack games"][channel]["round"] == 0:
                    if bet >= 0:
                        if money.checkBalance(user) >= bet:
                            money.addMoney(user,-1 * bet)
                            playerHand = [drawCard(channel),drawCard(channel)]

                            handValue = calcHandValue(playerHand)

                            with open("resources/games/games.json", "r") as f:
                                data = json.load(f)

                            if handValue == 21:
                                data["blackjack games"][channel]["user hands"][user] = {"hand":playerHand,
                                    "bet":bet,"standing":False,"busted":False,"blackjack":True,"hit":True,
                                    "doubled":False,"split":0,"other hand":{},"third hand":{},"fourth hand":{}}
                            else:
                                data["blackjack games"][channel]["user hands"][user] = {"hand":playerHand,
                                    "bet":bet,"standing":False,"busted":False,"blackjack":False,"hit":True,
                                    "doubled":False,"split":0,"other hand":{},"third hand":{},"fourth hand":{}}

                            with open("resources/games/games.json", "w") as f:
                                json.dump(data,f,indent=4)

                            logThis(getName(user)+" entered the game")
                            return getName(user)+" entered the game"
                        else:
                            logThis(user+" doesn't have enough GwendoBucks")
                            return "You don't have enough GwendoBucks to place that bet"
                    else:
                        logThis(user+" tried to bet a negative amount")
                        return "You can't bet a negative amount"
                else:
                    logThis("The table is no longer open for bets")
                    return "The table is no longer open for bets"
            else:
                logThis("There are already 5 players in the game.")
                return "There's already a maximum of players at the table."
        else:
            logThis(user+" is already in the game")
            return "You've already entered this game"
    else:
        logThis("There is no game going on in "+channel)
        return "There is no game going on in this channel"

# Starts a game of blackjack
def blackjackStart(channel:str):
    with open("resources/games/games.json", "r") as f:
        data = json.load(f)
    
    logThis("Trying to start a blackjack game in "+channel)

    if channel not in data["blackjack games"]:

        dealerHand = [drawCard(channel),drawCard(channel)]
        gameID = datetime.datetime.now().strftime('%Y%m%d%H%M%S')

        data["blackjack games"][channel] = {"dealer hand": dealerHand,"dealer busted":False,"dealer blackjack":False,"user hands": {},"all standing":False,"round":0,"id":gameID}
        
        if calcHandValue(dealerHand) == 21:
            data["blackjack games"][channel]["dealer blackjack"] = True
        
        with open("resources/games/games.json", "w") as f:
            json.dump(data,f,indent=4)

        copyfile("resources/games/blackjackTable.png","resources/games/blackjackTables/blackjackTable"+channel+".png")

        return "started"
    else:
        logThis("There is already a blackjack game going on in "+channel)
        return "There's already a blackjack game going on. Try again in a few minutes."

# Ends the game and calculates winnings
def blackjackFinish(channel):
    finalWinnings = "*Final Winnings:*\n"

    with open("resources/games/games.json", "r") as f:
        data = json.load(f)

    dealerValue = calcHandValue(data["blackjack games"][channel]["dealer hand"])
    dealerBlackjack = data["blackjack games"][channel]["dealer blackjack"]
    dealerBusted = data["blackjack games"][channel]["dealer busted"]

    try:
        for user in data["blackjack games"][channel]["user hands"]:
            
            try:
                winnings, netWinnings, reason = calcWinnings(data["blackjack games"][channel]["user hands"][user],dealerValue,True,dealerBlackjack,dealerBusted)    
            except:
                logThis("Error calculating winnings for "+str(user)+" (error code 1312)")

            if winnings < 0:
                if winnings == -1:
                    finalWinnings += getName(user)+" lost "+str(-1 * winnings)+" GwendoBuck "+reason+"\n"
                else:
                    finalWinnings += getName(user)+" lost "+str(-1 * winnings)+" GwendoBucks "+reason+"\n"
            else:
                if winnings == 1:
                    finalWinnings += getName(user)+" won "+str(winnings)+" GwendoBuck "+reason+"\n"
                else:
                    finalWinnings += getName(user)+" won "+str(winnings)+" GwendoBucks "+reason+"\n"

            money.addMoney(user,netWinnings)

    except:
        logThis("Error calculating winnings (error code 1311)")

    with open("resources/games/games.json", "r") as f:
        data = json.load(f)

    del data["blackjack games"][channel]

    with open("resources/games/games.json", "w") as f:
        json.dump(data,f,indent=4)

    return finalWinnings


def calcWinnings(hand, dealerValue, topLevel, dealerBlackjack, dealerBusted):
    logThis("Calculating winnings")
    reason = ""
    bet = hand["bet"]
    winnings = -1 * bet
    netWinnings = 0
    handValue = calcHandValue(hand["hand"])

    if hand["blackjack"] and dealerBlackjack == False:
        reason += "(blackjack)"
        winnings +=  math.floor(2.5 * bet)
        netWinnings +=  math.floor(2.5 * bet)
    elif dealerBlackjack:
        reason += "(dealer blackjack)"
    elif hand["busted"]:
        reason += "(busted)" 
    else:
        if dealerBusted:
            reason = "(dealer busted)"
            winnings += 2 * bet
            netWinnings += 2 * bet
        elif handValue > dealerValue:
            winnings += 2 * bet
            netWinnings += 2 * bet
            reason = "(highest value)"
        elif handValue == dealerValue:
            reason = "(pushed)"
            winnings += bet
            netWinnings += bet
        else:
            reason = "(highest value)"
        
    
    if topLevel:
        if hand["split"] >= 1:
            winningsTemp, netWinningsTemp, reasonTemp = calcWinnings(hand["other hand"],dealerValue,False,dealerBlackjack,dealerBusted)
            winnings += winningsTemp
            netWinnings += netWinningsTemp
            reason += reasonTemp
        if hand["split"] >= 2:
            winningsTemp, netWinningsTemp, reasonTemp = calcWinnings(hand["third hand"],dealerValue,False,dealerBlackjack,dealerBusted)
            winnings += winningsTemp
            netWinnings += netWinningsTemp
            reason += reasonTemp
        if hand["split"] >= 3:
            winningsTemp, netWinningsTemp, reasonTemp = calcWinnings(hand["fourth hand"],dealerValue,False,dealerBlackjack,dealerBusted)
            winnings += winningsTemp
            netWinnings += netWinningsTemp
            reason += reasonTemp

    return winnings, netWinnings, reason

def getHandNumber(user,handNumber):
    try:
        hand = None

        if user["split"] == 0:
            hand = user
            handNumber = 0
        else:
            if handNumber != 0:
                if handNumber == 1:
                    hand = user
                elif handNumber == 2:
                    hand = user["other hand"]
                elif handNumber == 3:
                    hand = user["third hand"]
                elif handNumber == 4:
                    hand = user["fourth hand"]

        return hand, handNumber
    except:
        logThis("Problem with getHandNumber() (error code 1322)")         

def isRoundDone(game):
    roundDone = True

    for person in game["user hands"].values():
        if person["hit"] == False and person["standing"] == False:
            roundDone = False
        
        if person["split"] > 0:
            if person["other hand"]["hit"] == False and person["other hand"]["standing"] == False:
                roundDone = False

            if person["split"] > 1:
                if person["third hand"]["hit"] == False and person["third hand"]["standing"] == False:
                    roundDone = False

                if person["split"] > 2:
                    if person["fourth hand"]["hit"] == False and person["fourth hand"]["standing"] == False:
                        roundDone = False

    return roundDone
