module Foreign.Marshal.Array.Guarded.Plain (
   -- * immutable arrays
   create,
   alloca,
   -- * mutable arrays
   MutablePtr,
   new,
   withMutablePtr,
   freeze,
   freezeInplace,
   thaw,
   thawInplace,
   ) where

import Foreign.Marshal.Array (allocaArray, copyArray)
import Foreign.Storable (Storable)
import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, mallocForeignPtrArray)
import Foreign.Ptr (Ptr)

import Control.Applicative ((<$>))


create :: (Storable a) => Int -> (Ptr a -> IO b) -> IO (ForeignPtr a, b)
create size f = do
   fptr <- mallocForeignPtrArray size
   (,) fptr <$> withForeignPtr fptr f

alloca :: (Storable a) => Int -> (Ptr a -> IO b) -> IO b
alloca = allocaArray


newtype MutablePtr a = MutablePtr (ForeignPtr a)

new :: (Storable a) => Int -> IO (MutablePtr a)
new size = MutablePtr <$> mallocForeignPtrArray size

withMutablePtr :: MutablePtr a -> (Ptr a -> IO b) -> IO b
withMutablePtr (MutablePtr fptr) = withForeignPtr fptr

{- |
The 'size' parameter must match the size passed to 'new'.
This is not checked.
-}
freeze :: (Storable a) => Int -> MutablePtr a -> IO (ForeignPtr a)
freeze size (MutablePtr src) = copyToNew size src

{- |
'freezeInplace' must be the last operation on the 'MutablePtr'
and its associated array.
This is not checked.
-}
{-# INLINE freezeInplace #-}
freezeInplace :: (Storable a) => Int -> MutablePtr a -> IO (ForeignPtr a)
freezeInplace _ (MutablePtr fptr) = return fptr


thaw :: (Storable a) => Int -> ForeignPtr a -> IO (MutablePtr a)
thaw size src = MutablePtr <$> copyToNew size src

{-# INLINE thawInplace #-}
thawInplace :: (Storable a) => Int -> ForeignPtr a -> IO (MutablePtr a)
thawInplace _size fptr = return $ MutablePtr fptr


copyToNew :: (Storable a) => Int -> ForeignPtr a -> IO (ForeignPtr a)
copyToNew size src = do
   dst <- mallocForeignPtrArray size
   withForeignPtr dst $ \dstPtr ->
      withForeignPtr src $ \srcPtr ->
      copyArray dstPtr srcPtr size
   return dst