module JSONEncoder.Builders where

import JSONEncoder.Prelude hiding (length)
import ByteString.TreeBuilder
import qualified JSONEncoder.ByteStrings as ByteStrings
import qualified Data.Text


{-# INLINABLE intercalate #-}
intercalate :: Foldable f => Builder -> f Builder -> Builder
intercalate incut =
  foldl' (appendWithIncut incut) mempty

{-# INLINABLE appendWithIncut #-}
appendWithIncut :: Builder -> Builder -> Builder -> Builder
appendWithIncut incut a b =
  if length a == 0
    then b
    else if length b == 0
      then a
      else a <> incut <> b

{-# INLINE asciiChar #-}
asciiChar :: Char -> Builder
asciiChar =
  byte . fromIntegral . ord

{-# INLINE utf8Char #-}
utf8Char :: Char -> Builder
utf8Char =
  byteString . ByteStrings.utf8Char

{-# INLINABLE stringEncodedChar #-}
stringEncodedChar :: Char -> Builder
stringEncodedChar =
  \case
    '\"' -> "\\\""
    '\\' -> "\\\\"
    '\n' -> "\\n"
    '\r' -> "\\r"
    '\t' -> "\\t"
    char -> encodedChar char

{-# INLINABLE encodedChar #-}
encodedChar :: Char -> Builder
encodedChar char =
  if char < '\x20'
    then 
      let
        hex =
          fromString (showHex (fromEnum char) "")
        in "\\u" <> mconcat (replicate (4 - length hex) (asciiChar '0')) <> hex
    else
      utf8Char char

{-# INLINABLE stringLiteral #-}
stringLiteral :: Text -> Builder
stringLiteral string =
  asciiChar '"' <> encoded string <> asciiChar '"'
  where
    encoded =
      Data.Text.foldl' (\builder -> mappend builder . stringEncodedChar) mempty