module Rattletrap.Type.Common
  ( Int.Int8
  , Int.Int32
  , Int.Int64
  , Map.Map
  , Text.Text
  , Word.Word8
  , Word.Word16
  , Word.Word32
  , Word.Word64
  , deriveJson
  )
where

import qualified Data.Aeson as Json
import qualified Data.Aeson.TH as Json
import qualified Data.Char as Char
import qualified Data.Int as Int
import qualified Data.Map as Map
import qualified Data.Maybe as Maybe
import qualified Data.Text as Text
import qualified Data.Word as Word
import qualified Language.Haskell.TH as TH

deriveJson :: TH.Name -> TH.Q [TH.Dec]
deriveJson name = Json.deriveJSON (jsonOptions (TH.nameBase name)) name

jsonOptions :: String -> Json.Options
jsonOptions prefix = Json.defaultOptions
  { Json.constructorTagModifier = toSnakeCase . partialDropPrefix prefix
  , Json.fieldLabelModifier = toSnakeCase
    . partialDropPrefix (lowerFirst prefix)
  , Json.omitNothingFields = True
  , Json.sumEncoding = Json.ObjectWithSingleField
  , Json.unwrapUnaryRecords = True
  }

lowerFirst :: String -> String
lowerFirst string = case string of
  "" -> string
  first : rest -> Char.toLower first : rest

toSnakeCase :: String -> String
toSnakeCase = Json.camelTo2 '_'

partialDropPrefix :: (Eq a, Show a) => [a] -> [a] -> [a]
partialDropPrefix prefix list = Maybe.fromMaybe
  (error (unwords [show prefix, "is not a prefix of", show list]))
  (dropPrefix prefix list)

dropPrefix :: Eq a => [a] -> [a] -> Maybe [a]
dropPrefix prefix list = case prefix of
  [] -> Just list
  ph : pt -> case list of
    [] -> Nothing
    lh : lt -> if ph == lh then dropPrefix pt lt else Nothing