{-# LANGUAGE Rank2Types, UnboxedTuples, MagicHash #-}

module JavaScript.Array.ST
    ( STJSArray
    , build
    , create
    , length
    , null
    , append
    , fromList
    , toList
    , read
    , write
    , push
    , pop
    , unshift
    , shift
    , reverse
    , take
    , drop
    , freeze
    , unsafeFreeze
    ) where

import qualified JavaScript.Array.Internal as I
import           JavaScript.Array.Internal (SomeJSArray(..), JSArray, STJSArray)

import Prelude hiding (length, null, read, take, drop, reverse)
import GHC.ST

import GHCJS.Types
import Unsafe.Coerce
import Control.DeepSeq

build :: (forall s. STJSArray s -> ST s ()) -> JSArray
build m = runST $ do
  a <- create
  m a
  unsafeFreeze a
{-# INLINE build #-}

create :: ST s (STJSArray s)
create = ST I.js_create
{-# INLINE create #-}

length :: STJSArray s -> ST s Int
length x = ST (I.js_length x)
{-# INLINE length #-}

null :: STJSArray s -> ST s Bool
null = fmap (==0) . length
{-# INLINE null #-}

append :: STJSArray s -> STJSArray s -> ST s (STJSArray s)
append x y = ST (I.js_append x y)
{-# INLINE append #-}

fromList :: [JSVal] -> ST s (STJSArray s)
fromList xs = ST (\s -> rnf xs `seq` I.js_toJSArray (unsafeCoerce xs) s)
{-# INLINE fromList #-}

toList :: STJSArray s -> ST s [JSVal]
toList x = ST (unsafeCoerce (I.js_fromJSArray x))
{-# INLINE toList #-}

read :: Int -> STJSArray s -> ST s (JSVal)
read n x = ST (I.js_index n x)
{-# INLINE read #-}

write :: Int -> JSVal -> STJSArray s -> ST s ()
write n e x = ST (I.js_setIndex n e x)
{-# INLINE write #-}

push :: JSVal -> STJSArray s -> ST s ()
push e x = ST (I.js_push e x)
{-# INLINE push #-}

pop :: STJSArray s -> ST s JSVal
pop x = ST (I.js_pop x)
{-# INLINE pop #-}

unshift :: JSVal -> STJSArray s -> ST s ()
unshift e x = ST (I.js_unshift e x)
{-# INLINE unshift #-}

shift :: STJSArray s -> ST s JSVal
shift x = ST (I.js_shift x)
{-# INLINE shift #-}

reverse :: STJSArray s -> ST s ()
reverse x = ST (I.js_reverse x)
{-# INLINE reverse #-}

take :: Int -> STJSArray s -> ST s (STJSArray s)
take n x = ST (I.js_slice 0 n x)
{-# INLINE take #-}

drop :: Int -> STJSArray s -> ST s (STJSArray s)
drop n x = ST (I.js_slice1 n x)
{-# INLINE drop #-}

slice :: Int -> Int -> STJSArray s -> ST s (STJSArray s)
slice s n x = ST (I.js_slice s n x)
{-# INLINE slice #-}

freeze :: STJSArray s -> ST s JSArray
freeze x = ST (I.js_slice1 0 x)
{-# INLINE freeze #-}

unsafeFreeze :: STJSArray s -> ST s JSArray
unsafeFreeze (SomeJSArray x) = pure (SomeJSArray x)
{-# INLINE unsafeFreeze #-}