-- | Card
module Game.FrenchCards.Card
  ( module Game.FrenchCards.Rank
  , module Game.FrenchCards.Suit
  , Card (Card)
  -- * Construction
  , newCard
  -- * Accessors
  , cardRank
  , cardSuit
  , cardWord
  ) where

import Game.FrenchCards.Rank
import Game.FrenchCards.Suit

import Data.Word


-- | Card type
newtype Card = Card Word8
  deriving (Eq, Ord, Read, Show)

-- | @
-- 'minBound' == 'newCard' 'R2' 'Clubs'
-- 'maxBound' == 'newCard' 'Ace' 'Spades'
-- @
instance Bounded Card where
  {-# INLINE minBound #-}
  minBound = Card 0
  {-# INLINE maxBound #-}
  maxBound = Card 51

-- | Indexes are from 0 to 51
instance Enum Card where
  {-# INLINE toEnum #-}
  toEnum x | x >= 0 && x < 52 = Card $ fromIntegral x
           | otherwise = error $ "Card toEnum " ++ show x
  {-# INLINE fromEnum #-}
  fromEnum = fromIntegral . cardWord


-- | Create a new card
{-# INLINE newCard #-}
newCard :: Rank -> Suit -> Card
newCard r s = Card $ fromIntegral $ fromEnum r * 4 + fromEnum s


-- | Get a card rank
{-# INLINE cardRank #-}
cardRank :: Card -> Rank
cardRank (Card x) = toEnum $ fromIntegral $ x `div` 4

-- | Get a card suit
{-# INLINE cardSuit #-}
cardSuit :: Card -> Suit
cardSuit (Card x) = toEnum $ fromIntegral $ x `mod` 4

-- | Get an underlying card word
{-# INLINE cardWord #-}
cardWord :: Card -> Word8
cardWord (Card x) = x