module Rattletrap.Type.Dictionary
  ( Dictionary(..)
  , dictionaryLookup
  )
where

import Rattletrap.Type.Common
import Rattletrap.Type.Str

import qualified Control.Monad as Monad
import qualified Data.Aeson as Json
import qualified Data.Aeson.Types as Json
import qualified Data.Map as Map
import qualified Data.Text as Text

data Dictionary a
  = DictionaryElement Str a (Dictionary a)
  | DictionaryEnd Str
  deriving (Dictionary a -> Dictionary a -> Bool
(Dictionary a -> Dictionary a -> Bool)
-> (Dictionary a -> Dictionary a -> Bool) -> Eq (Dictionary a)
forall a. Eq a => Dictionary a -> Dictionary a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Dictionary a -> Dictionary a -> Bool
$c/= :: forall a. Eq a => Dictionary a -> Dictionary a -> Bool
== :: Dictionary a -> Dictionary a -> Bool
$c== :: forall a. Eq a => Dictionary a -> Dictionary a -> Bool
Eq, Eq (Dictionary a)
Eq (Dictionary a)
-> (Dictionary a -> Dictionary a -> Ordering)
-> (Dictionary a -> Dictionary a -> Bool)
-> (Dictionary a -> Dictionary a -> Bool)
-> (Dictionary a -> Dictionary a -> Bool)
-> (Dictionary a -> Dictionary a -> Bool)
-> (Dictionary a -> Dictionary a -> Dictionary a)
-> (Dictionary a -> Dictionary a -> Dictionary a)
-> Ord (Dictionary a)
Dictionary a -> Dictionary a -> Bool
Dictionary a -> Dictionary a -> Ordering
Dictionary a -> Dictionary a -> Dictionary a
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
forall a. Ord a => Eq (Dictionary a)
forall a. Ord a => Dictionary a -> Dictionary a -> Bool
forall a. Ord a => Dictionary a -> Dictionary a -> Ordering
forall a. Ord a => Dictionary a -> Dictionary a -> Dictionary a
min :: Dictionary a -> Dictionary a -> Dictionary a
$cmin :: forall a. Ord a => Dictionary a -> Dictionary a -> Dictionary a
max :: Dictionary a -> Dictionary a -> Dictionary a
$cmax :: forall a. Ord a => Dictionary a -> Dictionary a -> Dictionary a
>= :: Dictionary a -> Dictionary a -> Bool
$c>= :: forall a. Ord a => Dictionary a -> Dictionary a -> Bool
> :: Dictionary a -> Dictionary a -> Bool
$c> :: forall a. Ord a => Dictionary a -> Dictionary a -> Bool
<= :: Dictionary a -> Dictionary a -> Bool
$c<= :: forall a. Ord a => Dictionary a -> Dictionary a -> Bool
< :: Dictionary a -> Dictionary a -> Bool
$c< :: forall a. Ord a => Dictionary a -> Dictionary a -> Bool
compare :: Dictionary a -> Dictionary a -> Ordering
$ccompare :: forall a. Ord a => Dictionary a -> Dictionary a -> Ordering
$cp1Ord :: forall a. Ord a => Eq (Dictionary a)
Ord, Int -> Dictionary a -> ShowS
[Dictionary a] -> ShowS
Dictionary a -> String
(Int -> Dictionary a -> ShowS)
-> (Dictionary a -> String)
-> ([Dictionary a] -> ShowS)
-> Show (Dictionary a)
forall a. Show a => Int -> Dictionary a -> ShowS
forall a. Show a => [Dictionary a] -> ShowS
forall a. Show a => Dictionary a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Dictionary a] -> ShowS
$cshowList :: forall a. Show a => [Dictionary a] -> ShowS
show :: Dictionary a -> String
$cshow :: forall a. Show a => Dictionary a -> String
showsPrec :: Int -> Dictionary a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Dictionary a -> ShowS
Show)

instance Json.FromJSON a => Json.FromJSON (Dictionary a) where
  parseJSON :: Value -> Parser (Dictionary a)
parseJSON = String
-> (Object -> Parser (Dictionary a))
-> Value
-> Parser (Dictionary a)
forall a. String -> (Object -> Parser a) -> Value -> Parser a
Json.withObject
    String
"Dictionary"
    (\Object
o -> do
      [Text]
keys <- Object -> String -> Parser [Text]
forall a. FromJSON a => Object -> String -> Parser a
get Object
o String
"keys"
      Str
lastKey <- Object -> String -> Parser Str
forall a. FromJSON a => Object -> String -> Parser a
get Object
o String
"last_key"
      Map Text a
value <- Object -> String -> Parser (Map Text a)
forall a. FromJSON a => Object -> String -> Parser a
get Object
o String
"value"
      (Dictionary a -> Text -> Parser (Dictionary a))
-> Dictionary a -> [Text] -> Parser (Dictionary a)
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
Monad.foldM
        (\Dictionary a
d Text
k -> case Text -> Map Text a -> Maybe a
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Text
k Map Text a
value of
          Maybe a
Nothing -> String -> Parser (Dictionary a)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail ([String] -> String
unwords [String
"missing key", Text -> String
forall a. Show a => a -> String
show Text
k])
          Just a
v -> Dictionary a -> Parser (Dictionary a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Str -> a -> Dictionary a -> Dictionary a
forall a. Str -> a -> Dictionary a -> Dictionary a
DictionaryElement (Text -> Str
Str Text
k) a
v Dictionary a
d)
        )
        (Str -> Dictionary a
forall a. Str -> Dictionary a
DictionaryEnd Str
lastKey)
        ([Text] -> [Text]
forall a. [a] -> [a]
reverse [Text]
keys)
    )

instance Json.ToJSON a => Json.ToJSON (Dictionary a) where
  toJSON :: Dictionary a -> Value
toJSON Dictionary a
d = [Pair] -> Value
Json.object
    [ String -> [Str] -> Pair
forall a. ToJSON a => String -> a -> Pair
pair String
"keys" (Dictionary a -> [Str]
forall a. Dictionary a -> [Str]
dictionaryKeys Dictionary a
d)
    , String -> Str -> Pair
forall a. ToJSON a => String -> a -> Pair
pair String
"last_key" (Dictionary a -> Str
forall a. Dictionary a -> Str
dictionaryLastKey Dictionary a
d)
    , String -> Map Text a -> Pair
forall a. ToJSON a => String -> a -> Pair
pair String
"value" (Dictionary a -> Map Text a
forall a. Dictionary a -> Map Text a
dictionaryValue Dictionary a
d)
    ]

dictionaryKeys :: Dictionary a -> [Str]
dictionaryKeys :: Dictionary a -> [Str]
dictionaryKeys = ((Str, a) -> Str) -> [(Str, a)] -> [Str]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Str, a) -> Str
forall a b. (a, b) -> a
fst ([(Str, a)] -> [Str])
-> (Dictionary a -> [(Str, a)]) -> Dictionary a -> [Str]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Dictionary a -> [(Str, a)]
forall a. Dictionary a -> [(Str, a)]
toList

dictionaryLastKey :: Dictionary a -> Str
dictionaryLastKey :: Dictionary a -> Str
dictionaryLastKey Dictionary a
x = case Dictionary a
x of
  DictionaryElement Str
_ a
_ Dictionary a
y -> Dictionary a -> Str
forall a. Dictionary a -> Str
dictionaryLastKey Dictionary a
y
  DictionaryEnd Str
y -> Str
y

dictionaryLookup :: Str -> Dictionary a -> Maybe a
dictionaryLookup :: Str -> Dictionary a -> Maybe a
dictionaryLookup Str
k Dictionary a
x = case Dictionary a
x of
  DictionaryElement Str
j a
v Dictionary a
y -> if Str
k Str -> Str -> Bool
forall a. Eq a => a -> a -> Bool
== Str
j then a -> Maybe a
forall a. a -> Maybe a
Just a
v else Str -> Dictionary a -> Maybe a
forall a. Str -> Dictionary a -> Maybe a
dictionaryLookup Str
k Dictionary a
y
  DictionaryEnd Str
_ -> Maybe a
forall a. Maybe a
Nothing

dictionaryValue :: Dictionary a -> Map Text a
dictionaryValue :: Dictionary a -> Map Text a
dictionaryValue = (Str -> Text) -> Map Str a -> Map Text a
forall k2 k1 a. Ord k2 => (k1 -> k2) -> Map k1 a -> Map k2 a
Map.mapKeys Str -> Text
strValue (Map Str a -> Map Text a)
-> (Dictionary a -> Map Str a) -> Dictionary a -> Map Text a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Str, a)] -> Map Str a
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(Str, a)] -> Map Str a)
-> (Dictionary a -> [(Str, a)]) -> Dictionary a -> Map Str a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Dictionary a -> [(Str, a)]
forall a. Dictionary a -> [(Str, a)]
toList

get :: Json.FromJSON a => Json.Object -> String -> Json.Parser a
get :: Object -> String -> Parser a
get Object
o String
k = Object
o Object -> Text -> Parser a
forall a. FromJSON a => Object -> Text -> Parser a
Json..: String -> Text
Text.pack String
k

pair :: Json.ToJSON a => String -> a -> (Text, Json.Value)
pair :: String -> a -> Pair
pair String
k a
v = (String -> Text
Text.pack String
k, a -> Value
forall a. ToJSON a => a -> Value
Json.toJSON a
v)

toList :: Dictionary a -> [(Str, a)]
toList :: Dictionary a -> [(Str, a)]
toList Dictionary a
x = case Dictionary a
x of
  DictionaryElement Str
k a
v Dictionary a
y -> (Str
k, a
v) (Str, a) -> [(Str, a)] -> [(Str, a)]
forall a. a -> [a] -> [a]
: Dictionary a -> [(Str, a)]
forall a. Dictionary a -> [(Str, a)]
toList Dictionary a
y
  DictionaryEnd Str
_ -> []