import random
from operator import attrgetter


def chunks(lst, n):
    """Yield successive n-sized chunks from lst."""
    for i in range(0, len(lst), n):
        yield lst[i:i + n]


class Card:
    def __init__(self, color, value):
        self.color = color
        self.value = value

    def __str__(self):
        sc = self.color if self.color else ' '
        sv = str(self.value)
        return sc+' '+sv

    def __repr__(self):
        return str(self)


def get_higher_card(card1: Card, card2: Card, trump_color: str):
    """
    compares two cards, assuming card1 was played first and card2 played second.
    returns: True if card2 beats card1, otherwise False
    """
    if card2.value == 'N':
        # no way to win if 'N' comes in second
        return False
    elif card1.value == 'Z':
        # no way to win if 'Z' was already played
        return False
    elif card1.value == 'N':
        # any other than 'N' will beat us here, and the special case
        # where card2 also is 'N' was already handled above
        return True
    elif card2.value == 'Z':
        # we only lose if card1 is also 'Z', and that was handled above,
        # so this is an easy case also
        return True
    else:
        if card1.color == card2.color:
            if card1.value > card2.value:
                return False
            else:
                return True
        elif card2.color == trump_color:
            return True
        else:
            return False


class Player:
    def __init__(self, name, player_id):
        self.name = name
        self.id = player_id
        self.cards = []
        self.tricks_taken = 0

    def set_cards(self, cards):
        self.cards = sorted(cards, key=attrgetter('color', 'value'))
        self.tricks_taken = 0

    def play_card(self, card_idx):
        c = self.cards[card_idx]
        self.cards.remove(c)
        return c

    def take_trick(self):
        self.tricks_taken += 1

    def add_card(self, card):
        self.cards.append(card)
        self.cards = sorted(self.cards, key=attrgetter('color', 'value'))


class WizGame:
    def __init__(self, player_names: list):
        self.players = [Player(pn, idx) for idx, pn in enumerate(player_names)]
        if len(self.players) > 6:
            raise Exception   # TODO: figure out more specific type
        self.card_deck = []
        self.trump_card = None
        self.start_player = 0
        self.active_player = 0
        self.current_round = 1
        self.num_tricks_played = 0
        self.played_cards = list()
        self.players_ordered = self.players
        self.last_trick = None
        self.websockets = []

    def start_game(self):
        self.create_deck()
        self.current_round = 1
        self.active_player = 0
        self.start_player = 0
        self.deal_cards(1)
        self.played_cards = list()
        self.players_ordered = self.players
        self.last_trick = [('-', '-')]*len(self.players)
        self.websockets = []

    def create_deck(self):
        self.card_deck = []
        for color in ["b", "r", "g", "y"]:
            for val in range(1, 14):
                self.card_deck.append(Card(color, val))
        for _ in range(1, 5):
            self.card_deck.append(Card('-', 'Z'))
        for _ in range(1, 5):
            self.card_deck.append(Card('-', 'N'))

    def get_trump_color(self):
        card = self.trump_card
        if card:
            if card.color:
                return card.color
            elif card.value == 'Z':
                return 'choose'
            else:
                return None
        else:
            return None

    def deal_cards(self, cards_per_player):
        random.shuffle(self.card_deck)
        cs = list(chunks(self.card_deck, cards_per_player))
        for idx, player in enumerate(self.players):
            player.set_cards(cs[idx])
        if len(cs) > len(self.players):
            cc = cs[len(self.players)]
            c = cc[0]
            self.trump_card = c
        else:
            self.trump_card = None

    def get_trick_winner(self):
        cards = self.played_cards
        high_player, high_card = cards[0]

        for player, card in cards[1:]:
            if get_higher_card(high_card, card, self.trump_card.color if self.trump_card else '-'):
                high_card = card
                high_player = player
        return high_player, high_card

    def is_trick_finished(self):
        if len(self.played_cards) < len(self.players):
            return False
        else:
            return True

    def is_round_finished(self):
        h = self.is_trick_finished()
        if h and self.num_tricks_played == self.current_round:
            return True
        else:
            return False

    def next_round(self):
        max_rounds = len(self.card_deck)/len(self.players)
        if self.current_round <= max_rounds:
            self.current_round += 1
            self.start_player += 1
            if self.start_player >= len(self.players):
                self.start_player = 0
            self.active_player = 0
            self.players_ordered = self.players[self.start_player:] + self.players[:self.start_player]
            self.played_cards.clear()
            self.deal_cards(self.current_round)
            self.num_tricks_played = 0
            return True
        else:
            return False

    def next_trick(self):
        self.last_trick = self.played_cards.copy()
        win = self.get_trick_winner()
        winner = win[0]
        starter = winner.id
        self.players_ordered = self.players[starter:] + self.players[:starter]
        self.active_player = 0
        self.played_cards.clear()

    def play_card(self, player, card):
        p = self.players[player]
        c = p.play_card(card)
        t = (p, c)
        self.played_cards.append(t)
        self.active_player += 1
        if self.is_trick_finished():
            self.num_tricks_played += 1

    def set_trump_color(self, trump_color):
        if self.trump_card.value != 'Z':
            return False
        else:
            if trump_color not in ["b", "r", "g", "y"]:
                return False
            else:
                self.trump_card = Card(trump_color, 'Z')
                return True

    def get_prev_player(self):
        if self.start_player == 0:
            return len(self.players)-1
        else:
            return self.start_player-1

    async def send_page_reload(self):
        for ws in self.websockets:
            try:
                msg = {"type": "message", "fct": "reload"};
                await ws.send_json(msg)
            except Exception as e:
                pass


p = ["A", "B", "C", "D", "E"]
the_game = WizGame(p)

