--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- |
--Module       : Data.HCard.Card
--Author       : Joe Fredette
--License      : BSD3
--Copyright    : Joe Fredette
--
--Maintainer   : Joe Fredette <jfredett.at.gmail.dot.com>
--Stability    : Unstable
--Portability  : portable 
--
--------------------------------------------------------------------------------
--Description  : A module containing the main "Card" class, a sort of type-indexed
--      record type.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

{-# LANGUAGE TypeFamilies, MultiParamTypeClasses #-}
module Data.HCard.Card where

import Data.HCard.Misc

-- | The Main class, this is -- effectively -- a type-indexed record. Specifically, it requires 
-- two types, one representing the suit, the other representing the index/rank. The suit, 
-- index, and construct functions are generic forms of the record accessors. 
-- 
-- The bulk of the implementation takes place in generic type instances, supporting equality 
-- irrelevant of ordering, ordering, parsing from a "normal form" (<index>-<suit>) and enum/bounded
--
--
-- TODO: Write deriving instance?
class (Eq s, Eq i, Show s, Show i) => Card s i where
        data CardT s i  :: * 
        suit            :: CardT s i -> s
        index           :: CardT s i -> i
        construct       :: i -> s -> CardT s i
        (@@)            :: i -> s -> CardT s i 
        -- MRD : Everything but @@
        x @@ y = construct x y


instance Card s i => Show (CardT s i) where
        show c = (show . index $ c) ++ "-" ++ (show . suit $ c)

instance Card s i => Eq (CardT s i) where
        ca == cb = (suit ca == suit cb) && (index ca == index cb)

type Cards s i = [CardT s i]

-- | Parse is just a read instance with read exposed as the MRD. For most types, this will
-- just be Read, but when dealing w/ index types, it can be a pain to parse numbers via 
-- read and end up with the expected format of "<index>-<suit>"
class Parse a where
        parse :: String -> a

instance (Parse s, Parse i, Card s i) => Parse (CardT s i) where
        parse s = construct (parse idx) (parse suit)
                where (idx, suit) = (\(x,y) -> (x, tail y)) 
                                  $ break (=='-') s 

instance (Ord s, Ord i, Card s i) => Ord (CardT s i) where
        compare c1 c2 
                | suit c1 < suit c2     = LT
                | suit c1 > suit c2     = GT
                | suit c1 == suit c2    = compare (index c1) (index c2)


instance (Bounded s, Bounded i, Enum s, Enum i, Card s i) => Enum (CardT s i) where
        toEnum i = [construct x y | x <- allBdd, y <- allBdd] !! i    
        fromEnum v = ((1 + fromEnum (suit v)) * (fromEnum (index v))) - 1

instance (Bounded s, Bounded i, Enum s, Enum i, Card s i) => Bounded (CardT s i) where
        minBound = construct minBound minBound
        maxBound = construct maxBound maxBound