"""
 cards_v1.py

 An example of objects for card games.
 Version 1 ... may be expanded later. (Nudge nudge wink wink.)

 Running it might look like this :

   $ python cards_v1.py 
   The deck is <Deck of 52 cards>
   Dealt the top card which is king of spades.
   The deck is now : <Deck of 51 cards>
   Adding four cards to a hand ...
   The hand is now
  <Hand of 3 cards: ace of diamonds, eight of hearts, seven of diamonds, >

 TODO: add doctests.

 Jim Mahoney | cs.marlboro.college | Nov 2019 | MIT License
"""
import random

class Card:
    """ A playing card. """

    # Note: This is class data, which you refer to later as e.g. Card.suits 
    suits = ('clubs', 'diamonds', 'hearts', 'spades')
    ranks = ('ace', 'two', 'three', 'four', 'five',
             'six', 'seven', 'eight', 'nine', 'ten',
             'jack', 'queen', 'king')
    
    def __init__(self, rank=None, suit=None):
        # TODO : fill in a random value if one isn't supplied or is illegal
        self.rank = rank
        self.suit = suit
        
    def __str__(self):
        """ A card as a string, e.g. 'two of hearts' """
        return "{} of {}".format(self.rank, self.suit)

    def __repr__(self):
        """ A card as displayed at the interactive prompt, 
            e.g. "Card(rank='two', suit='spades') """
        return "Card(rank='{}', suit='{}')".format(self.rank, self.suit)

    def value(self):
        """ Return value of this card """
        # TODO : change this to match some specific game,
        #        or override in a child class like BlackJackCard
        return 1
    
class Hand:
    """ some cards """
    
    def __init__(self):
        self.cards = []
        
    def value(self):
        """ Return the value of this hand """
        # Here the value is just the sum of the card values.
        result = 0
        for card in self.cards:
            result = result + card.value()
        return result
    
    def __str__(self):
        result = "<Hand of {} cards: ".format(len(self.cards))
        for card in self.cards:
            result = result + str(card) + ", "
        return result + ">"
    
    def add(self, card):
        """ add a card to this hand """
        self.cards.append(card)
    
class Deck:
    """ A deck of cards. """
    
    def __init__(self):
        # TODO: add option to shuffle deck during initialization.
        # TODO: add in option to include jokers??
        self.cards = []
        for suit in Card.suits:
            for rank in Card.ranks:
                card = Card(rank, suit)
                self.cards.append(card)
                
    def __str__(self):
        return "<Deck of {} cards>".format(len(self.cards))
    
    def shuffle(self):
        random.shuffle(self.cards)
        
    def add(self, card):
        """ add a card onto the top of the deck """
        # Here the 'top' is the right end of the list,
        # in other words the highest index.
        self.cards.append(card)
        
    def deal(self, random=False):
        """ remove one card from the top of the deck 
            (or from a random place in the deck)
            and return it.
        """
        if random:
            index = random.randrange(len(self.cards))
        else:
            index = -1  # i.e. the last, top card
        card = self.cards.pop(index)
        return card

    # TODO : add a deal_hand method that removes some
    #        number of cards and returns a Hand object.

class Game:
    """ Would you like to play a game? """
    # TODO : expand this into a more typical card game.

    def __init__(self):
        self.deck = Deck()
        self.deck.shuffle()

    def play(self):
        print("The deck is {}".format(self.deck))
        top_card = self.deck.deal()
        print("Dealt the top card which is {}.".format(top_card))
        print("The deck is now : {}".format(self.deck))
        print("Adding four cards to a hand ...")
        # TODO : make this next part into a method somewhere ...
        hand = Hand()
        for i in range(3):
            card = self.deck.deal()
            hand.add(card)
        print("The hand is now")
        print(hand)
        # TODO : should we save this hand somewhere?

# Let 'er rip :
game = Game()
game.play()

##
## Or running it could be just look like this :
##
##     Game().play()
##
## (Quick quiz: why are there two empty parens in that line?
##