module Cards where
import Global
import System.Random
import Test.HUnit hiding (Path)

  {- REPRESENTATION CONVENTION: Represents the rank/value of the playing card.
     REPRESENTATION INVARIANT:  True
   -}

data Rank = One | Two | Three | Four | Five
            | Six | Seven | Eight | Nine | Ten
            | Jack | Queen | King | Ace
            deriving(Eq, Ord, Show)

{- REPRESENTATION CONVENTION: Represents the suit of the playing card.
   REPRESENTATION INVARIANT:  True
-}

data Suit = Diamonds
            | Clubs
            | Heart
            | Spades
            deriving(Eq,Ord,Show)

{- REPRESENTATION CONVENTION: Holds to datatypes that represents the value of the playing card.
   REPRESENTATION INVARIANT:  True
-}
data Card = Card Suit Rank deriving (Eq, Ord, Show)

{- cardList [Card] -> Card
   Purpose: Appends the card to the given list.
   Pre: True
   Post: List of Cards.
   Side-effect:
-}

cardList :: [Card] -> Card -> IO [Card] -- Tar in en lista och en kort och retunerar en ny lista med kortet i den
cardList l c = do
  return (l ++ [c])

{- genCard
   Purpose: Generates a random Card.
   Pre: True
   Post: True
   Side-effect:
-}

genCard :: IO Card
genCard = do -- Genererar ett random kort
  s <- (randomRIO (1,4))
  r <- (randomRIO (2,14))
  return (Card (intToSuit s) (intToRank r))

{- genCardList Int [Card]
   Purpose: generates a list of unique Cards with the lenght of the given Int.
   Pre: True
   Post: List of Cards.
   Side-effect:
-}

genCardList :: Int -> [Card]-> IO [Card]
genCardList 0 l = do
  return l
genCardList i l = do
  x <- genCard
--  y <- Global.exists l x -- Kollar om det nya kortet finns i listan
  if Global.exists l x then do -- Om det gör det körs denna kod
    genCardList (i) l -- Kör om funktionen utan att ta -1 på i
    else do -- om det är unikt lägg in i listan och kör om funktionen på i-1
      z <- cardList l x
      genCardList (i-1) z

{- intToRank Int
   Purpose: Translates the given Int to a Rank
   Pre: 0 < i < 15
   Post: True
   Side-effect:
-}

intToRank :: Int -> Rank
intToRank i = case i of -- Håller i vilket värde korten ska ha med en
  1 -> One
  2 -> Two
  3 -> Three
  4 -> Four
  5 -> Five
  6 -> Six
  7 -> Seven
  8 -> Eight
  9 -> Nine
  10 -> Ten
  11 -> Jack
  12 -> Queen
  13 -> King
  14 -> Ace

{- intToSuit Int
   Purpose: Translates the given Int to a Suit
   Pre: 0 < i < 5
   Post: True
   Side-effect:
-}

intToSuit :: Int -> Suit
intToSuit i = case i of -- Håller i vilken färg kortet ska ha med en Int
  1 -> Diamonds
  2 -> Clubs
  3 -> Heart
  4 -> Spades

class CTI a where
  bjPoints :: a -> Int

{- bjPoints Card
   Purpose: Translates the Card Rank to an Int.
   Pre: True
   Post: True
   Side-effect:
-}

instance CTI Card where -- Värdet för alla kort i Blackjack
  bjPoints (Card s One) = 1
  bjPoints (Card s Two) = 2
  bjPoints (Card s Three) = 3
  bjPoints (Card s Four) = 4
  bjPoints (Card s Five) = 5
  bjPoints (Card s Six) = 6
  bjPoints (Card s Seven) = 7
  bjPoints (Card s Eight) = 8
  bjPoints (Card s Nine) = 9
  bjPoints (Card s Ten) = 10
  bjPoints (Card s Jack) = 10
  bjPoints (Card s Queen) = 10
  bjPoints (Card s King) = 10
  bjPoints (Card s Ace) = 11

{- bjPoints [Card]
   Purpose: Get the value of the whole deck as an Int.
   Pre: True
   Post: True
   Side-effect:
-}

instance CTI a => CTI [a] where
  bjPoints [] = 0
  bjPoints (c:cs) = bjPoints c + bjPoints cs

{- first [Card]
   Purpose: Gets the first element in the Card list.
   Pre: True
   Post: True
   Side-effect:
-}

first :: [Card] -> IO Card
first (x:xs) = do
  return x

{- first [Card]
   Purpose: Gets a list of Card excluding the first element of the given list.
   Pre: True
   Post: True
   Side-effect:
-}

rest :: [Card] -> IO [Card]
rest (x:xs) = do
  return xs

{- first [Card] Int
   Purpose: Drops the first i cards in the list.
   Pre: True
   Post: True
   Side-effect:
-}

dropFirst :: [Card] -> Int -> IO [Card]
dropFirst (x:xs) 0 = do
  return (x:xs)
dropFirst (x:xs) i = do
  dropFirst xs (i-1)



-- test cases
test0 = TestCase $ assertBool "True"
                   (intToRank 2 == Two)

test1 = TestCase $ assertEqual "bjPoints [Card Spades One, Card Spades Two, Card Heart Nine]" 12 (bjPoints [Card Spades One, Card Spades Two, Card Heart Nine])

test2 = TestCase $ assertEqual "bjPoints [Card Spades Ace, Card Spades Seven, Card Heart Queen]" 28 (bjPoints [Card Spades Ace, Card Spades Seven, Card Heart Queen])

test3 = TestCase $ assertEqual "bjPoints (Card Diamonds Five)" 5 (bjPoints (Card Diamonds Five))

runTests = runTestTT $ TestList [test0, test1, test2, test3]
