module Database.Tansu.Backend.Ephemeral
         ( EphemeralDb
         , withEphemeralDb
         , withNewEphemeralDb
         , createEphemeralDb
         ) where

import Control.Concurrent.MVar
import Data.ByteString (ByteString)
import Data.IORef
import           Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Database.Tansu (Tansu)
import Database.Tansu.Internal

type Table = Map ByteString ByteString

ephemeralRunTransaction :: MVar () -> IO a -> IO a
ephemeralRunTransaction lock comp = withMVar lock $ \ () -> comp

ephemeralSet :: IORef Table -> ByteString -> ByteString -> IO (Either TansuError ())
ephemeralSet table key val =
  fmap return $ modifyIORef table (M.insert key val)

ephemeralGet :: IORef Table -> ByteString -> IO (Either TansuError ByteString)
ephemeralGet table key = do
  rs <- M.lookup key `fmap` readIORef table
  case rs of
    Just x  -> return (return x)
    Nothing -> return (Left (KeyNotFound key))

ephemeralDel :: IORef Table -> ByteString -> IO (Either TansuError ())
ephemeralDel table key =
  fmap return $ modifyIORef table (M.delete key)

-- | An 'EphemeralDb' is just an in-memory map, with no way of saving it.
--   It is intended to be used for testing Tansu code.
newtype EphemeralDb = EDB { fromEDB :: Table }

-- | Create an in-memory table to use for testing.
createEphemeralDb :: [(ByteString, ByteString)] -> EphemeralDb
createEphemeralDb = EDB . M.fromList

-- | Run a 'Tansu' operation with an empty in-memory table.
withNewEphemeralDb :: (TansuDb -> IO a) -> IO a
withNewEphemeralDb = withEphemeralDb $ EDB M.empty

-- | Run a 'Tansu' operation with an existing in-memory table.
withEphemeralDb :: EphemeralDb -> (TansuDb -> IO a) -> IO a
withEphemeralDb init comp = do
  lock  <- newMVar ()
  table <- newIORef (fromEDB init)
  comp $ TansuDb
    { dbRunTransaction = ephemeralRunTransaction lock
    , dbSet = ephemeralSet table
    , dbGet = ephemeralGet table
    , dbDel = ephemeralDel table
    }
