module Rattletrap.Encode.Str
  ( putText
  , putTextBits
  )
where

import Rattletrap.Encode.Int32le
import Rattletrap.Type.Common
import Rattletrap.Type.Int32le
import Rattletrap.Type.Str
import Rattletrap.Utility.Bytes

import qualified Data.Binary as Binary
import qualified Data.Binary.Bits.Put as BinaryBits
import qualified Data.Binary.Put as Binary
import qualified Data.ByteString as Bytes
import qualified Data.Char as Char
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text

putText :: Str -> Binary.Put
putText text = do
  let size = getTextSize text
  let encode = getTextEncoder size
  putInt32 size
  Binary.putByteString (encode (addNull (strValue text)))

putTextBits :: Str -> BinaryBits.BitPut ()
putTextBits text = do
  let size = getTextSize text
  let encode = getTextEncoder size
  putInt32Bits size
  BinaryBits.putByteString (reverseBytes (encode (addNull (strValue text))))

getTextSize :: Str -> Int32le
getTextSize text =
  let
    value = strValue text
    scale = if Text.all Char.isLatin1 value then 1 else -1 :: Int32
    rawSize = if Text.null value
      then 0
      else fromIntegral (Text.length value) + 1 :: Int32
    size = if value == Text.pack "\x00\x00\x00None"
      then 0x05000000
      else scale * rawSize :: Int32
  in
    Int32le size

getTextEncoder :: Int32le -> Text.Text -> Bytes.ByteString
getTextEncoder size text =
  if size < Int32le 0 then Text.encodeUtf16LE text else encodeLatin1 text

addNull :: Text.Text -> Text.Text
addNull text = if Text.null text then text else Text.snoc text '\x00'