    --------------------------------------------------------------------------------
    --------------------------------------------------------------------------------
    -- |
    --Module       : Data.HCard.Deck
    --Author       : Joe Fredette
    --License      : BSD3
    --Copyright    : Joe Fredette
    --
    --Maintainer   : Joe Fredette <jfredett.at.gmail.dot.com>
    --Stability    : Unstable
    --Portability  : portable
    --
    --------------------------------------------------------------------------------
    --Description  : Functions relating to a Deck of cards, eg shuffling, dealing,
    --      etc.
    --------------------------------------------------------------------------------
    --------------------------------------------------------------------------------

    module Data.HCard.Deck where

    import System.Random
    import System.Random.Shuffle

    import Control.Monad.State

    import Data.HCard.Misc
    import Data.HCard.Card
    import Data.HCard.Hand

    -- | Separate Deck from Hand, even though the types are isomorphic, we don't want shuffling to
    -- be to liberal.
    --
    -- TODO: Make Deck clever enough to support reshuffling when the deck runs out -- it should store cards it has
    --  already seen till it runs out of the main deck, reshuffle, redeal.
    newtype Deck s i = Deck (Cards s i)
            deriving (Show)

    -- | Type wrapper for stateful decks, useful for sorting
    type DeckST s i = State (Deck s i)

    instance Card s i => Eq (Deck s i) where
            (Deck h1) == (Deck h2) = (h1 `subset` h2) && (h2 `subset` h1)

    -- | Creates a deck, used as in: `mkDeck::<your deck type here>`, or w/ inference.
    mkDeck :: (Bounded s, Bounded i, Enum s, Enum i, Card s i) => Deck s i
    mkDeck = Deck $ enumFromTo minBound maxBound

    -- | Shuffles a deck given a generator
    shuffleDeck :: (Card s i, RandomGen g) => Deck s i -> g -> Deck s i
    shuffleDeck (Deck ds) g = Deck (shuffle' ds (length ds) g)

    -- | Shuffles using the standard generator
    shuffleDeckIO :: Card s i => Deck s i -> IO (Deck s i)
    shuffleDeckIO (Deck ds) = do
                    gen <- getStdGen
                    return $ Deck (shuffle' ds (length ds) gen)

    -- | Deals `n` hands of `qty` cards, written in the state monad.
    dealHands :: Card s i => Int -> Int -> (DeckST s i) [Hand s i]
    dealHands 0   _   = return []
    dealHands _   0   = error "Can't deal zero cards."
    dealHands num qty = do
                    (Deck deck) <- get
                    if (length deck < qty) then error "Not enough cards in deck"
                                           else do { hand <- dealHand qty
                                                   ; next <- dealHands (num - 1) qty
                                                   ; return $ hand : next
                                                   }
    -- | Helper for dealHands, also somewhat useful, equiv. to `dealHands 1 qty`
    dealHand :: Card s i => Int -> (DeckST s i) (Hand s i)
    dealHand qty = do
            (Deck deck) <- get
            let (hand, rem) = splitAt qty deck
            put (Deck rem)
            return $ Hand hand