{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Octane.Type.Primitive.PCString (PCString(..)) where

import qualified Data.ByteString.Char8 as BS8
import qualified Data.Text as Text
import Octane.Core
import Octane.Type.Primitive.Int32LE

-- | A length-prefixed null-terminated string.
newtype PCString = PCString
    { getPCString :: Text
    } deriving (Eq, Generic, NFData, Ord, Show)

instance Binary PCString where
    get = do
        (Int32LE size) <- get
        string <- if size == 0
            then fail ("invalid PCString size " ++ show size)
            else if size < 0
            then do
                let actualSize = 2 * negate size
                bytes <- getByteString (fromIntegral actualSize)
                bytes & decodeUtf16LE & return
            else do
                bytes <- getByteString (fromIntegral size)
                bytes & decodeLatin1 & return
        string & Text.dropEnd 1 & PCString & return

    put (PCString string) = do
        let cString = Text.snoc string '\NUL'
        let size = cString & Text.length & fromIntegral
        if Text.all isLatin1 cString
        then do
            size & Int32LE & put
            cString & encodeLatin1 & putByteString
        else do
            size & negate & Int32LE & put
            cString & encodeUtf16LE & putByteString

encodeLatin1 :: Text -> ByteString
encodeLatin1 text = text & Text.unpack & BS8.pack