module Data.Text.Builder.Common.Internal where

import Data.Text (Text)
import Control.Monad.ST
import Data.Monoid
import Text.Printf
import Data.Char (ord)
import Data.Foldable (fold)
import qualified Data.Text as Text
import qualified Data.Text.Array as A
import qualified Data.Text.Internal.Unsafe.Char as TC

-- | This is slower that just pattern matching on the Text data constructor.
--   However, it will work with GHCJS. This should only be used in places
--   where we know that it will only be evaluated once.
portableTextArray :: Text -> A.Array
portableTextArray = fst . portableUntext
{-# INLINE portableTextArray #-}

-- | This length is not the character length. It is the length of Word16s
--   required by a UTF16 representation.
portableTextLength :: Text -> Int
portableTextLength = snd . portableUntext
{-# INLINE portableTextLength #-}

portableUntext :: Text -> (A.Array,Int)
portableUntext t =
  let str = Text.unpack t
      Sum len = foldMap (Sum . charUtf16Size) str
      arr = A.run $ do
        marr <- A.new len
        writeString marr str
        return marr
   in (arr,len)
{-# NOINLINE portableUntext #-}

writeString :: A.MArray s -> String -> ST s ()
writeString marr = go 0 where
  go i s = case s of
    c : cs -> do
      n <- TC.unsafeWrite marr i c
      go (i + n) cs
    [] -> return ()

charUtf16Size :: Char -> Int
charUtf16Size c = if ord c < 0x10000 then 1 else 2

hexValuesWord12Upper :: A.Array
hexValuesWord12Upper = portableTextArray $ fold
  $ map (Text.pack . printf "%03X") [0 :: Int ..4096]
{-# NOINLINE hexValuesWord12Upper #-}

hexValuesWord12Lower :: A.Array
hexValuesWord12Lower = portableTextArray $ fold
  $ map (Text.pack . printf "%03x") [0 :: Int ..4096]
{-# NOINLINE hexValuesWord12Lower #-}

hexValuesWord8Upper :: A.Array
hexValuesWord8Upper = portableTextArray $ fold
  $ map (Text.pack . printf "%02X") [0 :: Int ..255]
{-# NOINLINE hexValuesWord8Upper #-}

hexValuesWord8Lower :: A.Array
hexValuesWord8Lower = portableTextArray $ fold
  $ map (Text.pack . printf "%02x") [0 :: Int ..255]
{-# NOINLINE hexValuesWord8Lower #-}

twoDecimalDigits :: A.Array
twoDecimalDigits = portableTextArray
  $ foldMap (Text.pack . printf "%02d") [0 :: Int ..99]
{-# NOINLINE twoDecimalDigits #-}

threeDecimalDigits :: A.Array
threeDecimalDigits = portableTextArray
  $ foldMap (Text.pack . printf "%03d") [0 :: Int ..255]
{-# NOINLINE threeDecimalDigits #-}