{-
 -      ``Data/Random/Source/DevRandom''
 -}
{-# LANGUAGE
    MultiParamTypeClasses, GADTs
  #-}

module Data.Random.Source.DevRandom 
    ( DevRandom(..)
    ) where

import Data.Random.Source
import Data.Random.Internal.Primitives

import System.IO (openBinaryFile, hGetBuf, Handle, IOMode(..))
import Foreign

-- |On systems that have it, \/dev\/random is a handy-dandy ready-to-use source
-- of nonsense.  Keep in mind that on some systems, Linux included, \/dev\/random
-- collects \"real\" entropy, and if you don't have a good source of it, such as
-- special hardware for the purpose or a *lot* of network traffic, it's pretty easy
-- to suck the entropy pool dry with entropy-intensive applications.  For many
-- purposes other than cryptography, \/dev\/urandom is preferable because when it
-- runs out of real entropy it'll still churn out pseudorandom data.
data DevRandom = DevRandom | DevURandom
    deriving (Eq, Show)

{-# NOINLINE devRandom  #-}
devRandom :: Handle
devRandom  = unsafePerformIO (openBinaryFile "/dev/random"  ReadMode)
{-# NOINLINE devURandom #-}
devURandom :: Handle
devURandom = unsafePerformIO (openBinaryFile "/dev/urandom" ReadMode)

dev :: DevRandom -> Handle
dev DevRandom  = devRandom
dev DevURandom = devURandom

instance RandomSource IO DevRandom where
    getRandomPrimFrom src = getPrimWhere supported getPrim
        where
            supported :: Prim a -> Bool
            supported PrimWord8          = True
            supported PrimWord16         = True
            supported PrimWord32         = True
            supported PrimWord64         = True
            supported _ = False
            
            getPrim :: Prim a -> IO a
            getPrim PrimWord8  = allocaBytes 1 $ \buf -> do
                1 <- hGetBuf (dev src) buf  1
                peek buf
            getPrim PrimWord16 = allocaBytes 2 $ \buf -> do
                2 <- hGetBuf (dev src) buf  2
                peek (castPtr buf)
            getPrim PrimWord32  = allocaBytes 4 $ \buf -> do
                4 <- hGetBuf (dev src) buf  4
                peek (castPtr buf)
            getPrim PrimWord64  = allocaBytes 8 $ \buf -> do
                8 <- hGetBuf (dev src) buf  8
                peek (castPtr buf)
            getPrim prim = error ("getRandomPrimFrom/" ++ show src ++ ": unsupported prim requested: " ++ show prim)