module Rattletrap.Text where

import Rattletrap.Int32
import Rattletrap.Utility

import qualified Data.Binary as Binary
import qualified Data.Binary.Bits.Get as BinaryBit
import qualified Data.Binary.Bits.Put as BinaryBit
import qualified Data.Binary.Get as Binary
import qualified Data.Binary.Put as Binary
import qualified Data.ByteString.Lazy.Char8 as ByteString
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Encoding

data Text = Text
  { textSize :: Int32
  , textValue :: Text.Text
  } deriving (Eq, Ord, Show)

getText :: Binary.Get Text
getText = do
  rawSize <- getInt32
  let decode = getTextDecoder rawSize
  let size = getTextSize rawSize
  bytes <- Binary.getLazyByteString size
  let text = decode bytes
  pure (Text rawSize text)

putText :: Text -> Binary.Put
putText text = do
  let size = textSize text
  let encode = getTextEncoder size
  putInt32 size
  Binary.putLazyByteString (encode (textValue text))

getTextBits :: BinaryBit.BitGet Text
getTextBits = do
  rawSize <- getInt32Bits
  let decode = getTextDecoder rawSize
  let size = getTextSize rawSize
  bytes <- BinaryBit.getLazyByteString size
  let text = decode (reverseBytes bytes)
  pure (Text rawSize text)

putTextBits :: Text -> BinaryBit.BitPut ()
putTextBits text = do
  let size = textSize text
  let encode = getTextEncoder size
  putInt32Bits size
  BinaryBit.putByteString
    (ByteString.toStrict (reverseBytes (encode (textValue text))))

stringToText :: String -> Text
stringToText string =
  let value = Text.snoc (Text.pack string) '\x00'
      size = Int32 (fromIntegral (Text.length value))
  in Text size value

textToString :: Text -> String
textToString text = Text.unpack (Text.dropWhileEnd (== '\x00') (textValue text))

getTextSize
  :: Integral a
  => Int32 -> a
getTextSize (Int32 size) =
  if size == 0x05000000
    then 8
    else if size < 0
           then (-2 * fromIntegral size)
           else fromIntegral size

getTextDecoder :: Int32 -> ByteString.ByteString -> Text.Text
getTextDecoder (Int32 size) bytes =
  let decode =
        if size < 0
          then Encoding.decodeUtf16LE
          else Encoding.decodeLatin1
  in decode (ByteString.toStrict bytes)

getTextEncoder :: Int32 -> Text.Text -> ByteString.ByteString
getTextEncoder (Int32 size) text =
  if size < 0
    then ByteString.fromStrict (Encoding.encodeUtf16LE text)
    else encodeLatin1 text

encodeLatin1 :: Text.Text -> ByteString.ByteString
encodeLatin1 text = ByteString.pack (Text.unpack text)