from Card import Card
from Deck import Deck
from Player import Player
from BasicStrategy import BasicStrategy, BasicStrategy_with_PlayerDeviation


class BlackJack:
    def __init__(
        self,
        g_min_bet,
        num_of_deck,
        num_of_deck_before_s,
        can_dealer_hit_on_soft,
        bankroll_value_list,
        num_of_players=1,
        basic_strategy=True,
    ):
        self.deck = Deck(num_of_deck)
        self.dealer = Player(True, self.deck, self)
        self.player_list = []
        self.g_min_bet = g_min_bet
        self.num_of_deck_before_s = num_of_deck_before_s
        self.create_players(num_of_players, bankroll_value_list)
        self.current_player_list = []
        self.basic_strategy = basic_strategy
        self.can_dealer_hit_on_soft = can_dealer_hit_on_soft

    def create_players(self, num_of_players, bankroll_value_list):
        for i in range(num_of_players):
            player = Player(
                False, self.deck, i + 1, self, bankroll_value_list[i], self.g_min_bet
            )

            self.player_list.append(player)

    def play_n_rounds(self, num_of_rounds):
        max_true_count = -10000000
        max_tc_round = 0
        for i in range(num_of_rounds):
            print(f"################## Round {i+1} ######################")
            current_tc = self.play_round()

            if current_tc > max_true_count:
                max_true_count = current_tc
                max_tc_round = i + 1

            self.clear_round()

        return max_true_count, max_tc_round

    def clear_round(self):

        # print(f"Current running count: {self.deck.count_card.running_count}")
        # print(f"Current true count: {self.deck.count_card.true_count}")

        self.deck.count_card.update_true_count(self.deck.num_of_deck_remaining())

        print(f"Current running count: {self.deck.count_card.running_count}")
        print(f"Current true count: {self.deck.count_card.true_count}")

        self.dealer.cards = []
        for player in self.player_list:
            player.cards = []
            player.has_insurance = False
            player.can_hit = True
            player.can_dd = True
            player.bankroll_value_list.append(player.bankroll)

        self.dealer.reveal_cards = False
        self.current_player_list = []

        for i in range(len(self.player_list)):
            self.player_list[i].player_index = str(i + 1)

    def ask_player_choice(self, current_player, dealer_status):
        choice = ""
        while choice != "S":

            player_card_score = current_player.score
            if current_player.is_pair:
                player_card_score = current_player.cards[-1].price

            # print("\nStrategy: ",BasicStrategy().choose_move(current_player.score, self.dealer.cards[0].price, current_player.is_pair, current_player.is_soft,player_card_score))

            # print("\nStrategy: ",
            # BasicStrategy_with_PlayerDeviation().choose_move_with_player_deviation(current_player.score,
            # self.dealer.cards[0].price,
            # current_player.is_pair,
            # current_player.is_soft,
            # player_card_score,
            # self.deck.count_card.true_count))
            print(
                f"""Enter for Player {current_player.player_index} \nS for Stand\nH for Hit\nD for Double Dowm\nSP for Split\n----> :"""
            )

            if self.basic_strategy:
                print(
                    "\nStrategy: ",
                    BasicStrategy().choose_move(
                        current_player.score,
                        self.dealer.cards[0].price,
                        current_player.is_pair,
                        current_player.is_soft,
                        player_card_score,
                        len(current_player.cards),
                    ),
                )
                print(
                    (
                        current_player.score,
                        self.dealer.cards[0].price,
                        current_player.is_pair,
                        current_player.is_soft,
                        player_card_score,
                        len(current_player.cards),
                    )
                )
                choice = BasicStrategy().choose_move(
                    current_player.score,
                    self.dealer.cards[0].price,
                    current_player.is_pair,
                    current_player.is_soft,
                    player_card_score,
                    len(current_player.cards),
                )
            else:
                print(
                    "\nStrategy: ",
                    BasicStrategy_with_PlayerDeviation().choose_move_with_player_deviation(
                        current_player.score,
                        self.dealer.cards[0].price,
                        current_player.is_pair,
                        current_player.is_soft,
                        player_card_score,
                        len(current_player.cards),
                        self.deck.count_card.true_count,
                    ),
                )
                print(
                    (
                        current_player.score,
                        self.dealer.cards[0].price,
                        current_player.is_pair,
                        current_player.is_soft,
                        player_card_score,
                        len(current_player.cards),
                        self.deck.count_card.true_count,
                    )
                )
                choice = BasicStrategy_with_PlayerDeviation().choose_move_with_player_deviation(
                    current_player.score,
                    self.dealer.cards[0].price,
                    current_player.is_pair,
                    current_player.is_soft,
                    player_card_score,
                    len(current_player.cards),
                    self.deck.count_card.true_count,
                )

            # choice = input()

            # choice = BasicStrategy_with_PlayerDeviation().choose_move(current_player.score,
            # self.dealer.cards[0].price, current_player.is_pair, current_player.is_soft, player_card_score)

            if choice == "H":
                p_bust = current_player.hit()
                current_player.show_cards()

                if p_bust == -1:
                    break

                if p_bust == 0:
                    print(f"Player {current_player.player_index} has Busted!!")
                    current_player.update_bankroll_value(-1)
                    return None

            if choice == "D":
                p_bust = current_player.double_down()
                current_player.show_cards()
                if p_bust == -1:
                    break
                if p_bust == 0:
                    print(f"Player {current_player.player_index} has Busted!!")
                    current_player.update_bankroll_value(-1)
                    return None
                choice = "S"

            if choice == "SP":
                split_player = current_player.split()
                # self.current_player_list.insert(i+1, split_player)
                does_split_player_exits = self.ask_player_choice(
                    split_player, dealer_status
                )
                if does_split_player_exits != None:
                    self.current_player_list.append(does_split_player_exits)

            if choice == -1:
                print("Some thing error caused!")
                break
        return current_player

    def play_round(self):

        if self.deck.num_of_deck_remaining() <= self.num_of_deck_before_s:
            self.deck.generate_cards()

        for player in self.player_list:
            player.update_bet()
            print(f"Player {player.player_index} is betting {player.current_bet}")

        self.current_player_list = self.player_list.copy()

        dealer_status = self.dealer.deal()
        self.dealer.show_cards()

        player_bj = []

        for i in range(len(self.player_list)):
            current_player = self.player_list[i]
            player_bj.append(current_player.deal())
            current_player.show_cards()

        if self.dealer.cards[0].value == "A":
            self.ask_for_insurance()

        if dealer_status:
            print("Dealer Has A BlackJack!!")
            for i in range(len(self.player_list)):
                current_player = self.player_list[i]

                if current_player.has_insurance:
                    print("Insurance Money Won!!")
                    current_player.update_bankroll_value(1)
                elif player_bj[i]:
                    print("Player Also Has A BlackJack!! PUSH!!")
                else:
                    self.player_list[i].update_bankroll_value(-1)
            return self.deck.count_card.true_count
        else:
            for i in range(len(self.player_list)):
                current_player = self.player_list[i]

                if player_bj[i] == 1:
                    print(f"Player {current_player.player_index} has a Blackjack!!")
                    current_player.update_bankroll_value(1.5)
                    self.current_player_list[i] = None

                if current_player.has_insurance:
                    print("Insurance Money Lost!")
                    current_player.update_bankroll_value(-0.5)

        for i in range(len(self.current_player_list)):

            if self.current_player_list[i] == None:
                continue

            self.current_player_list[i] = self.ask_player_choice(
                self.current_player_list[i], dealer_status
            )

        self.dealer.reveal_cards = True
        self.dealer.show_cards()

        player_exits = False
        for player in self.current_player_list:
            if player != None:
                player_exits = True

        if not player_exits:
            return self.deck.count_card.true_count

        while self.dealer.score < 17:
            if self.dealer.is_soft and not self.can_dealer_hit_on_soft:
                break
            d_bust_value = self.dealer.hit()
            self.dealer.show_cards()
            if d_bust_value == 0:
                print("Dealer Busted!")
                for current_player in self.current_player_list:

                    if current_player == None:
                        continue

                    current_player.update_bankroll_value(1)
                return self.deck.count_card.true_count

        for current_player in self.current_player_list:

            if current_player == None:
                continue

            if self.dealer.score == current_player.score:
                print("Push for Player", current_player.player_index)
                print("Bankroll_value", current_player.bankroll)
            elif self.dealer.score > current_player.score:
                print("Dealer Won!")
                current_player.update_bankroll_value(-1)
            elif self.dealer.score < current_player.score:
                print(f"Player {current_player.player_index} Won!")
                current_player.update_bankroll_value(1)

        # Experiment
        return self.deck.count_card.true_count

    def ask_for_insurance(self):
        if self.basic_strategy == False:
            for i in range(len(self.player_list)):
                current_player = self.player_list[i]
                print(
                    "Strategy: ",
                    BasicStrategy_with_PlayerDeviation().take_insurance(
                        self.deck.count_card.true_count
                    ),
                )

                print(
                    f"Enter for Player {current_player.player_index}\nWhether you wanna take the insurance: "
                )

                # choice = input()

                choice = BasicStrategy_with_PlayerDeviation().take_insurance(
                    self.deck.count_card.true_count
                )

                if choice == "I":
                    print(f"Player {current_player.player_index} took Insurance!")
                    current_player.has_insurance = True

from Card import Card
from Deck import Deck
from Player import Player
from BasicStrategy import BasicStrategy, BasicStrategy_with_PlayerDeviation


class BlackJack:
    def __init__(
        self,
        g_min_bet,
        num_of_deck,
        num_of_deck_before_s,
        can_dealer_hit_on_soft,
        bankroll_value_list,
        num_of_players=1,
        basic_strategy=True,
    ):
        self.deck = Deck(num_of_deck)
        self.dealer = Player(True, self.deck, self)
        self.player_list = []
        self.g_min_bet = g_min_bet
        self.num_of_deck_before_s = num_of_deck_before_s
        self.create_players(num_of_players, bankroll_value_list)
        self.current_player_list = []
        self.basic_strategy = basic_strategy
        self.can_dealer_hit_on_soft = can_dealer_hit_on_soft

    def create_players(self, num_of_players, bankroll_value_list):
        for i in range(num_of_players):
            player = Player(
                False, self.deck, i + 1, self, bankroll_value_list[i], self.g_min_bet
            )

            self.player_list.append(player)

    def play_n_rounds(self, num_of_rounds):
        max_true_count = -10000000
        max_tc_round = 0
        for i in range(num_of_rounds):
            print(f"################## Round {i+1} ######################")
            current_tc = self.play_round()

            if current_tc > max_true_count:
                max_true_count = current_tc
                max_tc_round = i + 1

            self.clear_round()

        return max_true_count, max_tc_round

    def clear_round(self):

        # print(f"Current running count: {self.deck.count_card.running_count}")
        # print(f"Current true count: {self.deck.count_card.true_count}")

        self.deck.count_card.update_true_count(self.deck.num_of_deck_remaining())

        print(f"Current running count: {self.deck.count_card.running_count}")
        print(f"Current true count: {self.deck.count_card.true_count}")

        self.dealer.cards = []
        for player in self.player_list:
            player.cards = []
            player.has_insurance = False
            player.can_hit = True
            player.can_dd = True
            player.bankroll_value_list.append(player.bankroll)

        self.dealer.reveal_cards = False
        self.current_player_list = []

        for i in range(len(self.player_list)):
            self.player_list[i].player_index = str(i + 1)

    def ask_player_choice(self, current_player, dealer_status):
        choice = ""
        while choice != "S":

            player_card_score = current_player.score
            if current_player.is_pair:
                player_card_score = current_player.cards[-1].price

            # print("\nStrategy: ",BasicStrategy().choose_move(current_player.score, self.dealer.cards[0].price, current_player.is_pair, current_player.is_soft,player_card_score))

            # print("\nStrategy: ",
            # BasicStrategy_with_PlayerDeviation().choose_move_with_player_deviation(current_player.score,
            # self.dealer.cards[0].price,
            # current_player.is_pair,
            # current_player.is_soft,
            # player_card_score,
            # self.deck.count_card.true_count))
            print(
                f"""Enter for Player {current_player.player_index} \nS for Stand\nH for Hit\nD for Double Dowm\nSP for Split\n----> :"""
            )

            if self.basic_strategy:
                print(
                    "\nStrategy: ",
                    BasicStrategy().choose_move(
                        current_player.score,
                        self.dealer.cards[0].price,
                        current_player.is_pair,
                        current_player.is_soft,
                        player_card_score,
                        len(current_player.cards),
                    ),
                )
                print(
                    (
                        current_player.score,
                        self.dealer.cards[0].price,
                        current_player.is_pair,
                        current_player.is_soft,
                        player_card_score,
                        len(current_player.cards),
                    )
                )
                choice = BasicStrategy().choose_move(
                    current_player.score,
                    self.dealer.cards[0].price,
                    current_player.is_pair,
                    current_player.is_soft,
                    player_card_score,
                    len(current_player.cards),
                )
            else:
                print(
                    "\nStrategy: ",
                    BasicStrategy_with_PlayerDeviation().choose_move_with_player_deviation(
                        current_player.score,
                        self.dealer.cards[0].price,
                        current_player.is_pair,
                        current_player.is_soft,
                        player_card_score,
                        len(current_player.cards),
                        self.deck.count_card.true_count,
                    ),
                )
                print(
                    (
                        current_player.score,
                        self.dealer.cards[0].price,
                        current_player.is_pair,
                        current_player.is_soft,
                        player_card_score,
                        len(current_player.cards),
                        self.deck.count_card.true_count,
                    )
                )
                choice = BasicStrategy_with_PlayerDeviation().choose_move_with_player_deviation(
                    current_player.score,
                    self.dealer.cards[0].price,
                    current_player.is_pair,
                    current_player.is_soft,
                    player_card_score,
                    len(current_player.cards),
                    self.deck.count_card.true_count,
                )

            # choice = input()

            # choice = BasicStrategy_with_PlayerDeviation().choose_move(current_player.score,
            # self.dealer.cards[0].price, current_player.is_pair, current_player.is_soft, player_card_score)

            if choice == "H":
                p_bust = current_player.hit()
                current_player.show_cards()

                if p_bust == -1:
                    break

                if p_bust == 0:
                    print(f"Player {current_player.player_index} has Busted!!")
                    current_player.update_bankroll_value(-1)
                    return None

            if choice == "D":
                p_bust = current_player.double_down()
                current_player.show_cards()
                if p_bust == -1:
                    break
                if p_bust == 0:
                    print(f"Player {current_player.player_index} has Busted!!")
                    current_player.update_bankroll_value(-1)
                    return None
                choice = "S"

            if choice == "SP":
                split_player = current_player.split()
                # self.current_player_list.insert(i+1, split_player)
                does_split_player_exits = self.ask_player_choice(
                    split_player, dealer_status
                )
                if does_split_player_exits != None:
                    self.current_player_list.append(does_split_player_exits)

            if choice == -1:
                print("Some thing error caused!")
                break
        return current_player

    def play_round(self):

        if self.deck.num_of_deck_remaining() <= self.num_of_deck_before_s:
            self.deck.generate_cards()

        for player in self.player_list:
            player.update_bet()
            print(f"Player {player.player_index} is betting {player.current_bet}")

        self.current_player_list = self.player_list.copy()

        dealer_status = self.dealer.deal()
        self.dealer.show_cards()

        player_bj = []

        for i in range(len(self.player_list)):
            current_player = self.player_list[i]
            player_bj.append(current_player.deal())
            current_player.show_cards()

        if self.dealer.cards[0].value == "A":
            self.ask_for_insurance()

        if dealer_status:
            print("Dealer Has A BlackJack!!")
            for i in range(len(self.player_list)):
                current_player = self.player_list[i]

                if current_player.has_insurance:
                    print("Insurance Money Won!!")
                    current_player.update_bankroll_value(1)
                elif player_bj[i]:
                    print("Player Also Has A BlackJack!! PUSH!!")
                else:
                    self.player_list[i].update_bankroll_value(-1)
            return self.deck.count_card.true_count
        else:
            for i in range(len(self.player_list)):
                current_player = self.player_list[i]

                if player_bj[i] == 1:
                    print(f"Player {current_player.player_index} has a Blackjack!!")
                    current_player.update_bankroll_value(1.5)
                    self.current_player_list[i] = None

                if current_player.has_insurance:
                    print("Insurance Money Lost!")
                    current_player.update_bankroll_value(-0.5)

        for i in range(len(self.current_player_list)):

            if self.current_player_list[i] == None:
                continue

            self.current_player_list[i] = self.ask_player_choice(
                self.current_player_list[i], dealer_status
            )

        self.dealer.reveal_cards = True
        self.dealer.show_cards()

        player_exits = False
        for player in self.current_player_list:
            if player != None:
                player_exits = True

        if not player_exits:
            return self.deck.count_card.true_count

        while self.dealer.score < 17:
            if self.dealer.is_soft and not self.can_dealer_hit_on_soft:
                break
            d_bust_value = self.dealer.hit()
            self.dealer.show_cards()
            if d_bust_value == 0:
                print("Dealer Busted!")
                for current_player in self.current_player_list:

                    if current_player == None:
                        continue

                    current_player.update_bankroll_value(1)
                return self.deck.count_card.true_count

        for current_player in self.current_player_list:

            if current_player == None:
                continue

            if self.dealer.score == current_player.score:
                print("Push for Player", current_player.player_index)
                print("Bankroll_value", current_player.bankroll)
            elif self.dealer.score > current_player.score:
                print("Dealer Won!")
                current_player.update_bankroll_value(-1)
            elif self.dealer.score < current_player.score:
                print(f"Player {current_player.player_index} Won!")
                current_player.update_bankroll_value(1)

        # Experiment
        return self.deck.count_card.true_count

    def ask_for_insurance(self):
        if self.basic_strategy == False:
            for i in range(len(self.player_list)):
                current_player = self.player_list[i]
                print(
                    "Strategy: ",
                    BasicStrategy_with_PlayerDeviation().take_insurance(
                        self.deck.count_card.true_count
                    ),
                )

                print(
                    f"Enter for Player {current_player.player_index}\nWhether you wanna take the insurance: "
                )

                # choice = input()

                choice = BasicStrategy_with_PlayerDeviation().take_insurance(
                    self.deck.count_card.true_count
                )

                if choice == "I":
                    print(f"Player {current_player.player_index} took Insurance!")
                    current_player.has_insurance = True

################# Round {i+1} ######################") # print(f"Current running count: {self.deck.count_card.running_count}") # print(f"Current true count: {self.deck.count_card.true_count}") # print("\nStrategy: ",BasicStrategy().choose_move(current_player.score, self.dealer.cards[0].price, current_player.is_pair, current_player.is_soft,player_card_score)) # print("\nStrategy: ", # BasicStrategy_with_PlayerDeviation().choose_move_with_player_deviation(current_player.score, # self.dealer.cards[0].price, # current_player.is_pair, # current_player.is_soft, # player_card_score, # self.deck.count_card.true_count)) Enter for Player {current_player.player_index} \nS for Stand\nH for Hit\nD for Double Dowm\nSP for Split\n----> : # choice = input() # choice = BasicStrategy_with_PlayerDeviation().choose_move(current_player.score, # self.dealer.cards[0].price, current_player.is_pair, current_player.is_soft, player_card_score) # self.current_player_list.insert(i+1, split_player) # Experiment # choice = input()