content: {-# LANGUAGE EmptyDataDecls #-}
  
  module Data.MutMap
    ( -- * MutMap
      MutMap
    , mutEmpty
    , mutFromList
    , mutLookup
    , mutElems
    , mutKeys
    , mutAssocs
    , mutClone
    , mutMapM
    , mutMapM_
    , mutMapMaybeM
    , mutInsert
    , mutDelete
    , mutClear
    )
    where
  
  import Data.Defined
  import Data.MutMap.Internal
  import Data.Text (Text)
  import FFI
  import Prelude
  
  data MutMap a
  
  -- Construction
  
  mutEmpty :: Fay (MutMap a)
  mutEmpty = ffi "{}"
  
  mutFromList :: [(Text, a)] -> Fay (MutMap a)
  mutFromList = mutFromListI . map (\(key, val) -> KeyValI (addSalt key) val)
  
  mutFromListI :: [KeyValI a] -> Fay (MutMap a)
  mutFromListI = ffi "function() { var r = {}; for(var key in %1) { r[%1[key].slot1] = %1[key].slot2; } return r; }()"
  
  -- Query
  
  mutLookup :: Text -> MutMap a -> Fay (Maybe a)
  mutLookup k m = return . fromDefined =<< mutLookupI (addSalt k) m
  
  mutLookupI :: Salted -> MutMap a -> Fay (Defined a)
  mutLookupI = ffi "%2[%1]"
  
  mutElems :: MutMap a -> Fay [a]
  mutElems = ffi "function() { var r = []; for (var k in %1) { r.push(%1[k]); } return r; }()"
  
  mutKeys :: MutMap a -> Fay [Text]
  mutKeys m = return . map unsalt =<< mutKeysI m
  
  mutKeysI :: MutMap a -> Fay [Salted]
  mutKeysI = ffi "function() { var r = []; for (var k in %1) { r.push(k); } return r; }()"
  
  mutAssocs :: MutMap a -> Fay [(Text, a)]
  mutAssocs m = return . map (\(KeyValI key val) -> (unsalt key, val)) =<< mutAssocsI m
  
  mutAssocsI :: MutMap a -> Fay [KeyValI a]
  mutAssocsI = ffi "function() { var r = []; for (var k in %1) { r.push({ instance : 'KeyValI', slot1 : k, slot2 : %1[k] }); } return r; }()"
  
  mutClone :: MutMap a -> Fay (MutMap a)
  mutClone = ffi "Fay$$objConcat({}, %1)"
  
  -- Note: Also clones.
  mutMapM :: (a -> Fay b) -> MutMap a -> MutMap b
  mutMapM = ffi "function () { var r = {}; for(var key in %2){ r[key] = %1(%2[key]); } return r;}()"
  
  mutMapM_ :: (a -> Fay ()) -> MutMap a -> Fay ()
  mutMapM_ = ffi "function () { var r = {}; for(var key in %2){ %1(%2[key]); } return;}()"
  
  -- Note: Also clones.
  mutMapMaybeM :: (a -> Fay (Maybe b)) -> MutMap a -> MutMap b
  mutMapMaybeM f = mutMapMaybeMI $ \x -> f x >>= return . toDefined
  
  mutMapMaybeMI :: (a -> Fay (Defined b)) -> MutMap a -> MutMap b
  mutMapMaybeMI = ffi "function () { var r = {}; for(var key in %2){ r[key] = %1(%2[key]); } return r;}()"
  
  -- Mutation
  
  mutInsert :: Text -> a -> MutMap a -> Fay ()
  mutInsert = mutInsertI . addSalt
  
  mutInsertI :: Salted -> a -> MutMap a -> Fay ()
  mutInsertI = ffi "%3[%1] = %2"
  
  mutDelete :: Text -> MutMap a -> Fay ()
  mutDelete = mutDeleteI . addSalt
  
  mutDeleteI :: Salted -> MutMap a -> Fay ()
  mutDeleteI = ffi "delete %2[%1]"
  
  mutClear :: MutMap a -> Fay ()
  mutClear = ffi "function() { for (var k in %1) { delete %1[k]; } }()"
  
  {- NOTE: I put out the effort to write these, but they are untested and ended
     up being unnecessary..
  
  -- Reinserts everything into an object, in order to force serialization, using
  -- the still-salted keys.  'Nothing' indicates a removal.
  mutToObject :: (a -> Fay (Maybe (Automatic b))) -> MutMap a -> Fay Object
  mutToObject f m = do
    obj <- objNew
    mutAssocsI m >>= mapM (\(KeyValI k v) -> do
        mv <- f v
        whenJust mv $ \v' -> objInsert (unsafeCoerce k) v' obj
      )
    return obj
  
  objectToMut :: (Automatic b -> Fay (Maybe a)) -> Object -> Fay (MutMap a)
  objectToMut f obj = do
    let obj' = unsafeCoerce obj
    m <- mutEmpty
    mutAssocsI obj' >>= mapM (\(KeyValI k v) -> do
        mv <- f v
        whenJust mv $ \v' -> when (checkSalted k) $ mutInsertI k v' m
      )
    return m
  -}
  