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
forall a. Bits a => Int -> a -> BitPut
BitPut.bits (GameMode -> Int
numBits GameMode
gameModeAttribute) (GameMode -> Word8
word GameMode
gameModeAttribute)

bitGet :: Version.Version -> BitGet.BitGet GameMode
bitGet :: Version -> BitGet GameMode
bitGet Version
version = String -> BitGet GameMode -> BitGet GameMode
forall a. String -> BitGet a -> BitGet a
BitGet.label String
"GameMode" (BitGet GameMode -> BitGet GameMode)
-> BitGet GameMode -> BitGet GameMode
forall a b. (a -> b) -> a -> b
$ do
  let numBits :: Int
numBits = if Int -> Int -> Int -> Version -> Bool
Version.atLeast Int
868 Int
12 Int
0 Version
version then Int
8 else Int
2 :: Int
  Word8
word <- String -> BitGet Word8 -> BitGet Word8
forall a. String -> BitGet a -> BitGet a
BitGet.label String
"word" (BitGet Word8 -> BitGet Word8) -> BitGet Word8 -> BitGet Word8
forall a b. (a -> b) -> a -> b
$ Int -> BitGet Word8
forall a. Bits a => Int -> BitGet a
BitGet.bits Int
numBits
  GameMode -> BitGet 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 }