{-# LANGUAGE ExistentialQuantification #-}
-- | Maintainer: Stephan Friedrichs, Henning Guenther
module Network.AdHoc.MessageID
        (MessageID
        ,MessageIDGenerator
        ,counter
        ,scrambler
        ,hasher
        ) where

import Data.List as List
import qualified Data.Digest.SHA512 as SHA512
import qualified Codec.Binary.Base64 as Base64
import Codec.Utils
import System.Random

-- | This is just a 'String'.
type MessageID = String

-- | Generate an infinite(!) stream of 'MessageID's.
type MessageIDGenerator g = g -> [MessageID]

-- | Simplest possible generator type: counts from 0 upwards. Very insecure.
counter :: MessageIDGenerator g
counter _ = fmap show [(0::Int)..]

-- | Generates a stream of 'MessageID's by shuffling a sorted stream couting from 0 upwards (@[0..]@).
--   @scrambler n g@ would randomly remove the i-th element (where i is a random number between 0 and n)
--   from the sorted list and place it at the head of the result list. It deals with the following
--   elements in the same way.
scrambler :: RandomGen g => Int -> MessageIDGenerator g
scrambler offset gen = fmap show $ scramble gen offset [(0::Int)..]

-- | Generates a stream of IDs by hashing an up-counting number and a random number with SHA512.
hasher :: RandomGen g => MessageIDGenerator g
hasher gen = fmap ((Base64.encode).(SHA512.hash)) (zipWith (++) (fmap (toOctets (256::Int)) [(0::Int)..]) (fmap toTwosComp (randoms gen::[Int])))

-- | Scrambles an infinite(!) list. The result will be a permutation of the original list.
--   The higher the offset (second argument), the further elements can occur from their
--   original indices.
scramble :: RandomGen g => g -> Int -> [a] -> [a]
scramble gen offset xs  = let
        (r, gen')       = randomR (0, offset) gen
        (left, x:right) = List.splitAt r xs
        xs'             = scramble gen' offset (left ++ right)
        in x : xs'