# Deck of cards module
# This module makes three new classes,
# a single card, a hand, and a deck

# import function to shuffle a list
from random import shuffle

# A card is an object
# This is a very minimal class
class Card:
    # factory function; just save suit and number
    def __init__(self, suit, num):
        # self is the instance of a card being created
        # self is automatically returned by this function
        self.suit = suit 
        self.num = num
        # saves input values as internal data to the
        # class - these data items are
        # called the class attributes. 

    # makes a string representing the object
    # so it looks nice when printed
    def __str__(self):
        outStr = self.suit+str(self.num)
        return outStr

# A fanicer class
class Hand:
    # factory function
    # Makes a hand of cards out of a list
    def __init__(self,L):
        # save these as the cards in the instance
        self.cards = L

    # Make nice string representing the instance
    def __str__(self):
        # Output string concatenates the strings
        # for the individual cards
        outStr = ""
        for card in self.cards:
            outStr = outStr+str(card)+" "
            # str(card) gets the nice string for a card
            # that is produced in class Card, above.
        outStr = outStr.strip()
        return outStr


# an even fancier class that changes and extends
# the Hand class.  Notice the class statement takes
# the Hand class as input; it will form the starting
# point for the DeckOfCards class.  This is called
# inheretance. 
class DeckOfCards(Hand):

    # a new factory function
    # makes the list of cards itself
    # this is called "overriding" the factory
    # function from Hand
    def __init__(self):
        # Make a list representing the deck
        self.cards = []
        for suit in ["H","S","D","C"]:
            for num in range(1,14):
                # append tuple to list
                self.cards.append( Card(suit,num) )


    # The __str__ function is
    # same as Hand...so no need to put it here

    # a new method!
    def cardShuffle(self):
        shuffle(self.cards)

    # a method with a parameter
    # returns a Hand of cards,
    # which it takes out of the deck
    def deal(self, numToDeal):
        # are there enough cards?
        if numToDeal < len(self.cards):
            outList = []
            for i in range(0,numToDeal):
                # pop takes the last card off the list
                card = self.cards.pop()
                outList.append(card)
            return Hand(outList) # use the Hand class
            # factory function to make the list into a Hand
        else:
            print("Not enough cards to deal.")
            return []
        
    
        
# An example program using the class
def main():
    # detect if this is being run by itself or
    # because it was imported
    if __name__ != "__main__":
        # running because module was imported
        # do nothing
        return

    # Ace of spades...keep this up my sleeve 
    ace = Card("S",1)
    print("Ace of spades",ace,"\n")
    
    # must be running as a demo of the module
    # make a new deck of cards (new instance)
    deck = DeckOfCards() 
    deck.cardShuffle() # shuffle it
    print("Shuffled deck",deck,"\n")
    
    # deal two hands for poker
    # Bob and Alice are instances of the class Hand
    Bob = deck.deal(5)
    Alice = deck.deal(5)
    # Alice slips the ace of spades into her hand
    Alice.cards[0] = ace

    print("Bob's hand:",Bob)
    print("Alice's hand:",Alice,"\n")
    
    print("Remaining deck:",deck)
    
main()


