-- | Globally Unique IDentifiers.
module Data.CQRS.GUID
       ( GUID
       , fromByteString
       , base64Decode
       , base64Encode
       , hexDecode
       , hexEncode
       , newGUID
       , toByteString
       ) where

import           Control.DeepSeq (NFData(..))
import           Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Base16 as B16
import qualified Data.ByteString.Base64 as B64
import           Data.ByteString.Char8 ()
import           Data.Data (Data)
import           Data.DeriveTH (derive, makeNFData)
import           Data.Typeable (Typeable)
import           Data.Word (Word8)
import           System.Random (randomRIO)

import           Data.CQRS.Serializable

-- | A Globally Unique IDentifier.
newtype GUID = GUID ByteString
              deriving (Typeable, Eq, Ord, Data)

-- | Get a random byte.
randomWord8 :: IO Word8
randomWord8 = fmap fromInteger $ randomRIO (0,255)

-- | Create a new random GUID.
newGUID :: IO GUID
newGUID = do
  uuid <- sequence $ replicate 16 randomWord8
  return $ GUID $ B.pack uuid

-- | Serialize instance.
instance Serializable GUID where
    serialize = toByteString
    deserialize = Just . GUID

-- | Hex encode a GUID.
hexEncode :: GUID -> ByteString
hexEncode (GUID s) = B16.encode s

-- | Base64 encode a GUID.
base64Encode :: GUID -> ByteString
base64Encode (GUID s) = B64.encode s

-- | Showing a GUID.
instance Show GUID where
    show = show . base64Encode

-- | Decode a GUID from hex representation.
hexDecode :: ByteString -> Maybe GUID
hexDecode s =
  case B16.decode s of
    (a,b) | B.length b == 0 -> Just $ GUID a
    _                       -> Nothing

-- | Decode a GUID from base64 representation.
base64Decode :: ByteString -> Maybe GUID
base64Decode s =
  case B64.decode s of
    Right a -> Just $ GUID a
    Left _  -> Nothing

-- | Convert from ByteString.
fromByteString :: ByteString -> GUID
fromByteString = GUID

-- | Convert to ByteString.
toByteString :: GUID -> ByteString
toByteString (GUID g) = g

-- Instances
$(derive makeNFData ''GUID)