{-# LANGUAGE CPP, FlexibleContexts #-}
module Network.Wai.Session.Map (mapStore, mapStore_) where
import Control.Monad
import Data.StateVar
import Data.ByteString (ByteString)
import Control.Monad.IO.Class (liftIO, MonadIO)
import Data.IORef
import Network.Wai.Session (Session, SessionStore, genSessionId)
import Data.Map (Map)
import qualified Data.Map as Map
mapStore :: (Ord k, MonadIO m) =>
	IO ByteString
	
	-> IO (SessionStore m k v)
mapStore gen =
	liftM (mapStore' gen) (newThreadSafeStateVar Map.empty)
	where
	mapStore' _ ssv (Just k) = do
		m <- get ssv
		case Map.lookup k m of
			Just sv -> return (sessionFromMapStateVar sv, return k)
			
			Nothing -> mapStore' gen ssv Nothing
	mapStore' genNewKey ssv Nothing = do
		newKey <- genNewKey
		sv <- newThreadSafeStateVar Map.empty
		ssv $~ Map.insert newKey sv
		return (sessionFromMapStateVar sv, return newKey)
mapStore_ :: (Ord k, MonadIO m) => IO (SessionStore m k v)
mapStore_ = mapStore genSessionId
newThreadSafeStateVar :: a -> IO (StateVar a)
newThreadSafeStateVar a = do
	ref <- newIORef a
	return $ makeStateVar
		(atomicModifyIORef ref (\x -> (x,x)))
		(\x -> atomicModifyIORef ref (const (x,())))
#if MIN_VERSION_StateVar(1,1,0)
sessionFromMapStateVar :: (Ord k, MonadIO m) =>
	StateVar (Map k v) ->
#else
sessionFromMapStateVar :: (HasGetter sv, HasSetter sv, Ord k, MonadIO m) =>
	sv (Map k v) ->
#endif
	Session m k v
sessionFromMapStateVar sv = (
		(\k -> liftIO (Map.lookup k `liftM` get sv)),
		(\k v -> liftIO (sv $~ Map.insert k v))
	)