def suit_to_value(suit):
    return {
        "2": 2,
        "3": 3,
        "4": 4,
        "5": 5,
        "6": 6,
        "7": 7,
        "8": 8,
        "9": 9,
        "T": 10,
        "J": 11,
        "Q": 12,
        "K": 13,
        "A": 14,
    }[suit]


assert(suit_to_value('K') == 13)


def is_flush(colors):
    first_color = colors[0]
    for color in colors:
        if color != first_color:
            return False
    return True


assert(is_flush(["H", "H", "H", "H", "H"]))
assert(is_flush(["H", "H", "D", "H", "H"]) is False)


def is_straight(suits):
    suits = sorted(suits)
    first_suit = suits[0]
    for suit in suits[1:]:
        if first_suit + 1 != suit:
            return False
        first_suit = suit
    return True


assert(is_straight([6, 3, 4, 5, 7]))
assert(is_straight([6, 3, 4, 5, 8]) is False)


def get_numbered_groups(ns):
    """ Takes [0, 3, 0, 3, 1] and returns
        [(2, 3), (2, 0), (1, 1)]
    """
    rs = []
    current_group = []
    for n in sorted(ns, reverse=True):
        if not current_group or n in current_group:
            current_group.append(n)
        else:
            rs.append(current_group)
            current_group = [n]
    rs.append(current_group)
    rs = sorted([(len(r), r[0]) for r in rs], reverse=True)
    return rs


assert(get_numbered_groups([0, 3, 0, 3, 1]) == [(2, 3), (2, 0), (1, 1)])


def rank(hand):
    """ A hand must be provided as a list of two letter strings.
        The first letter is the suit and the second the suit of a card.

        The function returns a tuple. The first value represents the hand as
        an integer where 0 means high card and 9 means Straight Flush.  The
        second value is a list of integers ranking the value of the respective
        rank. For example, a Royal Flush would be (9, [14, 13, 12, 11, 10]),
        while 22aqj would be (1, [2, 2, 14, 12, 11]). By doing this we can
        simply compare to hands by first comparing the rank itself and then
        the list of integers.

        We get something like ["5H", "6S", "7S", "5C", "KD"].
    """

    suits, colors = zip(*(map(lambda s: (s[0], s[1]), hand)))
    suits = sorted(map(suit_to_value, suits))

    flush = is_flush(colors)
    straight = is_straight(suits)
    numbered_suits = get_numbered_groups(suits)
    if flush and straight:
        return [8, numbered_suits]
    if flush:
        return [5, numbered_suits]
    if straight:
        return [4, numbered_suits]
    if numbered_suits[0][0] == 4:
        return [7, numbered_suits]
    if numbered_suits[0][0] == 3 and numbered_suits[1][0] == 2:
        return [6, numbered_suits]
    if numbered_suits[0][0] == 3:
        return [3, numbered_suits]
    if numbered_suits[0][0] == 2 and numbered_suits[1][0] == 2:
        return [2, numbered_suits]
    if numbered_suits[0][0] == 2:
        return [1, numbered_suits]
    return [0, numbered_suits]


def read_hands():
    """ Reads a list of tuples where each field
        in the tuple represents a hand.
    """
    hands = []
    with open("../txt/EulerProblem054.txt", "r") as f:
        for line in f.readlines():
            cards = line.strip().split(" ")
            hands.append((cards[:5], cards[5:]))
    return hands


def euler_054():
    p1_wins = 0

    for p1_hand, p2_hand in read_hands():
        if rank(p1_hand) > rank(p2_hand):
            p1_wins += 1
            # msg = "P1 hand {} wins over P2 hand {}."
            # print(msg.format(p1_hand, p2_hand))
        else:
            pass
            # msg = "P1 hand {} loses versus P2 hand {}."
            # print(msg.format(p1_hand, p2_hand))

    return p1_wins


if __name__ == "__main__":
    print("e054.py: " + str(euler_054()))
    assert(euler_054() == 376)
