module Rattletrap.Type.Attribute.GameMode where

import qualified Data.Word as Word
import qualified Rattletrap.BitGet as BitGet
import qualified Rattletrap.BitPut as BitPut
import qualified Rattletrap.Schema as Schema
import qualified Rattletrap.Type.Version as Version
import qualified Rattletrap.Utility.Json as Json

data GameMode = GameMode
  { GameMode -> Int
numBits :: Int
  -- ^ This field is guaranteed to be small. In other words, it won't overflow.
  -- It's stored as a regular 'Int' rather than something more precise like an
  -- 'Int8' because it just gets passed to functions that expect 'Int's.
  -- There's no reason to do a bunch of conversions.
  , GameMode -> Word8
word :: Word.Word8
  }
  deriving (GameMode -> GameMode -> Bool
(GameMode -> GameMode -> Bool)
-> (GameMode -> GameMode -> Bool) -> Eq GameMode
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GameMode -> GameMode -> Bool
$c/= :: GameMode -> GameMode -> Bool
== :: GameMode -> GameMode -> Bool
$c== :: GameMode -> GameMode -> Bool
Eq, Int -> GameMode -> ShowS
[GameMode] -> ShowS
GameMode -> String
(Int -> GameMode -> ShowS)
-> (GameMode -> String) -> ([GameMode] -> ShowS) -> Show GameMode
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GameMode] -> ShowS
$cshowList :: [GameMode] -> ShowS
show :: GameMode -> String
$cshow :: GameMode -> String
showsPrec :: Int -> GameMode -> ShowS
$cshowsPrec :: Int -> GameMode -> ShowS
Show)

instance Json.FromJSON GameMode where
  parseJSON :: Value -> Parser GameMode
parseJSON = String -> (Object -> Parser GameMode) -> Value -> Parser GameMode
forall a. String -> (Object -> Parser a) -> Value -> Parser a
Json.withObject String
"GameMode" ((Object -> Parser GameMode) -> Value -> Parser GameMode)
-> (Object -> Parser GameMode) -> Value -> Parser GameMode
forall a b. (a -> b) -> a -> b
$ \Object
object -> do
    Int
numBits <- Object -> String -> Parser Int
forall value. FromJSON value => Object -> String -> Parser value
Json.required Object
object String
"num_bits"
    Word8
word <- Object -> String -> Parser Word8
forall value. FromJSON value => Object -> String -> Parser value
Json.required Object
object String
"word"
    GameMode -> Parser GameMode
forall (f :: * -> *) a. Applicative f => a -> f a
pure GameMode :: Int -> Word8 -> GameMode
GameMode { Int
numBits :: Int
numBits :: Int
numBits, Word8
word :: Word8
word :: Word8
word }

instance Json.ToJSON GameMode where
  toJSON :: GameMode -> Value
toJSON GameMode
x =
    [Pair] -> Value
Json.object [String -> Int -> Pair
forall value pair.
(ToJSON value, KeyValue pair) =>
String -> value -> pair
Json.pair String
"num_bits" (Int -> Pair) -> Int -> Pair
forall a b. (a -> b) -> a -> b
$ GameMode -> Int
numBits GameMode
x, String -> Word8 -> Pair
forall value pair.
(ToJSON value, KeyValue pair) =>
String -> value -> pair
Json.pair String
"word" (Word8 -> Pair) -> Word8 -> Pair
forall a b. (a -> b) -> a -> b
$ GameMode -> Word8
word GameMode
x]

schema :: Schema.Schema
schema :: Schema
schema = String -> Value -> Schema
Schema.named String
"attribute-game-mode" (Value -> Schema) -> Value -> Schema
forall a b. (a -> b) -> a -> b
$ [(Pair, Bool)] -> Value
Schema.object
  [ (String -> Value -> Pair
forall value pair.
(ToJSON value, KeyValue pair) =>
String -> value -> pair
Json.pair String
"num_bits" (Value -> Pair) -> Value -> Pair
forall a b. (a -> b) -> a -> b
$ Schema -> Value
Schema.ref Schema
Schema.integer, Bool
True)
  , (String -> Value -> Pair
forall value pair.
(ToJSON value, KeyValue pair) =>
String -> value -> pair
Json.pair String
"word" (Value -> Pair) -> Value -> Pair
forall a b. (a -> b) -> a -> b
$ Schema -> Value
Schema.ref Schema
Schema.integer, Bool
True)
  ]

bitPut :: GameMode -> BitPut.BitPut
bitPut :: GameMode -> BitPut
bitPut GameMode
gameModeAttribute = do
  Int -> Word8 -> BitPut
BitPut.word8 (GameMode -> Int
numBits GameMode
gameModeAttribute) (GameMode -> Word8
word GameMode
gameModeAttribute)

bitGet :: Version.Version -> BitGet.BitGet GameMode
bitGet :: Version -> BitGet GameMode
bitGet Version
version =
  Int -> Word8 -> GameMode
GameMode (Version -> Int
numBits_ Version
version) (Word8 -> GameMode) -> BitGet Word8 -> BitGet GameMode
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> BitGet Word8
BitGet.word8 (Version -> Int
numBits_ Version
version)

numBits_ :: Version.Version -> Int
numBits_ :: Version -> Int
numBits_ Version
version = if Version -> Bool
has8Bits Version
version then Int
8 else Int
2

has8Bits :: Version.Version -> Bool
has8Bits :: Version -> Bool
has8Bits Version
v =
  Version -> Int
Version.major Version
v Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
868 Bool -> Bool -> Bool
&& Version -> Int
Version.minor Version
v Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
12 Bool -> Bool -> Bool
&& Version -> Int
Version.patch Version
v Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0