content: {-# LANGUAGE TypeFamilies #-}
  -- |
  -- Module:
  --   Data.FastMutableIntMap
  -- Description:
  --   A mutable version of 'IntMap'
  module Data.FastMutableIntMap
    ( FastMutableIntMap
    , new
    , newEmpty
    , insert
    , isEmpty
    , getFrozenAndClear
    , size
    , applyPatch
    , PatchIntMap (..)
    , traverseIntMapPatchWithKey
    , lookup
    , forIntersectionWithImmutable_
    , for_
    , patchIntMapNewElements
    , patchIntMapNewElementsMap
    , getDeletions
    , toList
    ) where
  
  --TODO: Pure JS version
  --TODO: Fast copy to FastIntMap
  --TODO: Fast patch type
  
  import Prelude hiding (lookup)
  
  import Control.Monad.IO.Class
  import Data.Foldable (traverse_)
  import Data.IntMap.Strict (IntMap)
  import qualified Data.IntMap.Strict as IntMap
  import Data.IORef
  import Data.Patch.Class
  import Data.Patch.IntMap
  
  -- | A 'FastMutableIntMap' holds a map of values of type @a@ and allows low-overhead modifications via IO.
  -- Operations on 'FastMutableIntMap' run in IO.
  newtype FastMutableIntMap a = FastMutableIntMap (IORef (IntMap a))
  
  -- | Create a new 'FastMutableIntMap' out of an 'IntMap'
  new :: IntMap a -> IO (FastMutableIntMap a)
  new m = FastMutableIntMap <$> newIORef m
  
  -- | Create a new empty 'FastMutableIntMap'
  newEmpty :: IO (FastMutableIntMap a)
  newEmpty = FastMutableIntMap <$> newIORef IntMap.empty
  
  -- | Insert an element into a 'FastMutableIntMap' at the given key
  insert :: FastMutableIntMap a -> Int -> a -> IO ()
  insert (FastMutableIntMap r) k v = modifyIORef' r $ IntMap.insert k v
  
  -- | Attempt to lookup an element by key in a 'FastMutableIntMap'
  lookup :: FastMutableIntMap a -> Int -> IO (Maybe a)
  lookup (FastMutableIntMap r) k = IntMap.lookup k <$> readIORef r
  
  -- | Runs the provided action over the intersection of a 'FastMutableIntMap' and an 'IntMap'
  forIntersectionWithImmutable_ :: MonadIO m => FastMutableIntMap a -> IntMap b -> (a -> b -> m ()) -> m ()
  forIntersectionWithImmutable_ (FastMutableIntMap r) b f = do
    a <- liftIO $ readIORef r
    traverse_ (uncurry f) $ IntMap.intersectionWith (,) a b
  
  -- | Runs the provided action over the values of a 'FastMutableIntMap'
  for_ :: MonadIO m => FastMutableIntMap a -> (a -> m ()) -> m ()
  for_ (FastMutableIntMap r) f = do
    a <- liftIO $ readIORef r
    traverse_ f a
  
  -- | Checks whether a 'FastMutableIntMap' is empty
  isEmpty :: FastMutableIntMap a -> IO Bool
  isEmpty (FastMutableIntMap r) = IntMap.null <$> readIORef r
  
  -- | Retrieves the size of a 'FastMutableIntMap'
  size :: FastMutableIntMap a -> IO Int
  size (FastMutableIntMap r) = IntMap.size <$> readIORef r
  
  -- | Make an immutable snapshot of the datastructure and clear it
  getFrozenAndClear :: FastMutableIntMap a -> IO (IntMap a)
  getFrozenAndClear (FastMutableIntMap r) = do
    result <- readIORef r
    writeIORef r IntMap.empty
    return result
  
  -- | Updates the value of a 'FastMutableIntMap' with the given patch (see 'Data.Patch.IntMap'),
  -- and returns an 'IntMap' with the modified keys and values.
  applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
  applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
    v <- readIORef r
    writeIORef r $! applyAlways p v
    return $ IntMap.intersection v m
  
  toList :: FastMutableIntMap a -> IO [(Int, a)]
  toList (FastMutableIntMap r) = IntMap.toList <$> readIORef r