{-# LANGUAGE CPP               #-}
------------------------------------------------------------------------------
-- | This module contains functionality common among multiple back-ends.
--

module Snap.Snaplet.Session.Common
  ( RNG
  , mkRNG
  , withRNG
  , randomToken
  , mkCSRFToken
  ) where

------------------------------------------------------------------------------
import           Control.Concurrent
import           Control.Monad
import           Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B
import qualified Data.Text.Encoding as T
import           Data.Text (Text)
import           Numeric
import           System.Random.MWC

#if !MIN_VERSION_base(4,8,0)
import           Control.Applicative
#endif


------------------------------------------------------------------------------
-- | High speed, mutable random number generator state
newtype RNG = RNG (MVar GenIO)

------------------------------------------------------------------------------
-- | Perform given action, mutating the RNG state
withRNG :: RNG
        -> (GenIO -> IO a)
        -> IO a
withRNG (RNG rng) m = withMVar rng m


------------------------------------------------------------------------------
-- | Create a new RNG
mkRNG :: IO RNG
mkRNG = withSystemRandom (newMVar >=> return . RNG)


------------------------------------------------------------------------------
-- | Generates a random salt of given length
randomToken :: Int -> RNG -> IO ByteString
randomToken n rng = do
    is <- withRNG rng $ \gen -> sequence . take n . repeat $ mk gen
    return . B.pack . concat . map (flip showHex "") $ is
  where
    mk :: GenIO -> IO Int
    mk = uniformR (0,15)


------------------------------------------------------------------------------
-- | Generate a randomized CSRF token
mkCSRFToken :: RNG -> IO Text
mkCSRFToken rng = T.decodeUtf8 <$> randomToken 40 rng