{-# LANGUAGE DeriveGeneric #-}
-- | Card value data type and its processing functions

module CardParts.Values(Value(..), parseValue) where

import Data.List (elemIndex)
import Data.Char ( toLower, isDigit, digitToInt )
import GHC.Generics (Generic)
import Data.Aeson (ToJSON, FromJSON)

-- | This type represents card value.

data Value = Two
    | Three
    | Four
    | Five
    | Six
    | Seven
    | Eight
    | Nine
    | Ten
    | Jack
    | Queen
    | King
    | Ace deriving (Int -> Value -> ShowS
[Value] -> ShowS
Value -> String
(Int -> Value -> ShowS)
-> (Value -> String) -> ([Value] -> ShowS) -> Show Value
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Value] -> ShowS
$cshowList :: [Value] -> ShowS
show :: Value -> String
$cshow :: Value -> String
showsPrec :: Int -> Value -> ShowS
$cshowsPrec :: Int -> Value -> ShowS
Show, Value -> Value -> Bool
(Value -> Value -> Bool) -> (Value -> Value -> Bool) -> Eq Value
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Value -> Value -> Bool
$c/= :: Value -> Value -> Bool
== :: Value -> Value -> Bool
$c== :: Value -> Value -> Bool
Eq, Int -> Value
Value -> Int
Value -> [Value]
Value -> Value
Value -> Value -> [Value]
Value -> Value -> Value -> [Value]
(Value -> Value)
-> (Value -> Value)
-> (Int -> Value)
-> (Value -> Int)
-> (Value -> [Value])
-> (Value -> Value -> [Value])
-> (Value -> Value -> [Value])
-> (Value -> Value -> Value -> [Value])
-> Enum Value
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: Value -> Value -> Value -> [Value]
$cenumFromThenTo :: Value -> Value -> Value -> [Value]
enumFromTo :: Value -> Value -> [Value]
$cenumFromTo :: Value -> Value -> [Value]
enumFromThen :: Value -> Value -> [Value]
$cenumFromThen :: Value -> Value -> [Value]
enumFrom :: Value -> [Value]
$cenumFrom :: Value -> [Value]
fromEnum :: Value -> Int
$cfromEnum :: Value -> Int
toEnum :: Int -> Value
$ctoEnum :: Int -> Value
pred :: Value -> Value
$cpred :: Value -> Value
succ :: Value -> Value
$csucc :: Value -> Value
Enum, Eq Value
Eq Value
-> (Value -> Value -> Ordering)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Value)
-> (Value -> Value -> Value)
-> Ord Value
Value -> Value -> Bool
Value -> Value -> Ordering
Value -> Value -> Value
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Value -> Value -> Value
$cmin :: Value -> Value -> Value
max :: Value -> Value -> Value
$cmax :: Value -> Value -> Value
>= :: Value -> Value -> Bool
$c>= :: Value -> Value -> Bool
> :: Value -> Value -> Bool
$c> :: Value -> Value -> Bool
<= :: Value -> Value -> Bool
$c<= :: Value -> Value -> Bool
< :: Value -> Value -> Bool
$c< :: Value -> Value -> Bool
compare :: Value -> Value -> Ordering
$ccompare :: Value -> Value -> Ordering
$cp1Ord :: Eq Value
Ord, Value
Value -> Value -> Bounded Value
forall a. a -> a -> Bounded a
maxBound :: Value
$cmaxBound :: Value
minBound :: Value
$cminBound :: Value
Bounded, (forall x. Value -> Rep Value x)
-> (forall x. Rep Value x -> Value) -> Generic Value
forall x. Rep Value x -> Value
forall x. Value -> Rep Value x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Value x -> Value
$cfrom :: forall x. Value -> Rep Value x
Generic)

instance FromJSON Value
instance ToJSON Value

-- | Shorthand for 'Value' 'Either' wrapper

type ValueResult = Either String Value

{- | This function gets a char which represents card value
and returns a 'Value' wrapped with 'Maybe'.

Char should be a digit between 2 and 9 or an uppercase symbol from [AKQJT] list.
Otherwise - 'Nothing' returns.

__Examples:__

@
parseValue \'2\' = 'Right' 'Two'
parseValue \'Q\' = 'Right' 'Queen'
parseValue \'z\' = 'Left' "There is no broadway card, which could be represented with \'z\'"
parseValue \'1\' = 'Left' "There is no number card with value 1"
@
-}
parseValue :: Char -> ValueResult
parseValue :: Char -> ValueResult
parseValue Char
symbol
    | Char -> Bool
isDigit Char
symbol = Int -> ValueResult
getDigitValue (Int -> ValueResult) -> (Char -> Int) -> Char -> ValueResult
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Int
digitToInt (Char -> ValueResult) -> Char -> ValueResult
forall a b. (a -> b) -> a -> b
$ Char
symbol
    | Bool
otherwise = Char -> ValueResult
getBroadwayValue Char
symbol
    where
        -- | If digit satisfies the required conditions -

        -- calculate the index in the 'Value' enum, wrap it with 'Right' and return.

        -- otherwise - 'Left' with err msg returns.

        getDigitValue :: Int -> ValueResult
        getDigitValue :: Int -> ValueResult
getDigitValue Int
digit
            | Int
digit Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
1 Bool -> Bool -> Bool
&& Int
digit Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
10 = Value -> ValueResult
forall a b. b -> Either a b
Right (Int -> Value
forall a. Enum a => Int -> a
toEnum (Int -> Value) -> Int -> Value
forall a b. (a -> b) -> a -> b
$ Int
digit Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2 :: Value)
            | Bool
otherwise = String -> ValueResult
forall a b. a -> Either a b
Left (String -> ValueResult) -> String -> ValueResult
forall a b. (a -> b) -> a -> b
$ String
"There is no number card with value " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
digit

        -- | Just a shortcut for all 'Value's as a list

        allValues :: [Value]
        allValues :: [Value]
allValues = [Value
forall a. Bounded a => a
minBound .. Value
forall a. Bounded a => a
maxBound] :: [Value]

        -- | Broadway 'Value' symbols are "AKQJT".

        -- This function take last five 'Value's' first letter to form this list.

        broadwaySymbols :: [Char]
        broadwaySymbols :: String
broadwaySymbols = [String -> Char
forall a. [a] -> a
head (String -> Char) -> (Value -> String) -> Value -> Char
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Value -> String
forall a. Show a => a -> String
show (Value -> Char) -> Value -> Char
forall a b. (a -> b) -> a -> b
$ Value
s | Value
s <- Int -> [Value] -> [Value]
forall a. Int -> [a] -> [a]
take Int
5 ([Value] -> [Value]) -> [Value] -> [Value]
forall a b. (a -> b) -> a -> b
$ [Value] -> [Value]
forall a. [a] -> [a]
reverse [Value]
allValues]

        -- | If given char is present in [AKQJT] list - calc its index in 'Value's enum;

        -- Then wrap it with 'Right' and return.

        -- If char is invalid - return 'Left' with err msg.

        getBroadwayValue :: Char -> ValueResult
        getBroadwayValue :: Char -> ValueResult
getBroadwayValue Char
v = case Char
v Char -> String -> Maybe Int
forall a. Eq a => a -> [a] -> Maybe Int
`elemIndex` String
broadwaySymbols of
            Maybe Int
Nothing -> String -> ValueResult
forall a b. a -> Either a b
Left (String -> ValueResult) -> String -> ValueResult
forall a b. (a -> b) -> a -> b
$ String
"There is no broadway card, which could be represented with " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Char -> String
forall a. Show a => a -> String
show Char
v
            Just Int
index -> Value -> ValueResult
forall a b. b -> Either a b
Right (Int -> Value
forall a. Enum a => Int -> a
toEnum (Int -> Value) -> Int -> Value
forall a b. (a -> b) -> a -> b
$ [Value] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Value]
allValues Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
index Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 :: Value)