-- | Initialize a new incremental HMAC context
initialize :: (ByteArrayAccess key, HashAlgorithm a)
           => key       -- ^ Secret key
           -> Context a
initialize secret = unsafeDoIO (doHashAlg undefined)
  where
        doHashAlg :: HashAlgorithm a => a -> IO (Context a)
        doHashAlg alg = do
            !withKey <- case B.length secret `compare` blockSize of
                            EQ -> return $ B.withByteArray secret
                            LT -> do key <- B.alloc blockSize $ \k -> do
                                        memSet k 0 blockSize
                                        B.withByteArray secret $ \s -> memCopy k s (B.length secret)
                                     return $ B.withByteArray (key :: ScrubbedBytes)
                            GT -> do
                                -- hash the secret key
                                ctx <- hashMutableInitWith alg
                                hashMutableUpdate ctx secret
                                digest <- hashMutableFinalize ctx
                                hashMutableReset ctx
                                -- pad it if necessary
                                if digestSize < blockSize
                                    then do
                                        key <- B.alloc blockSize $ \k -> do
                                            memSet k 0 blockSize
                                            B.withByteArray digest $ \s -> memCopy k s (B.length digest)
                                        return $ B.withByteArray (key :: ScrubbedBytes)
                                    else
                                       return $ B.withByteArray digest
            (inner, outer) <- withKey $ \keyPtr ->
                (,) <$> B.alloc blockSize (\p -> memXorWith p 0x36 keyPtr blockSize)
                    <*> B.alloc blockSize (\p -> memXorWith p 0x5c keyPtr blockSize)
            return $ Context (hashUpdates initCtx [outer :: ScrubbedBytes])
                             (hashUpdates initCtx [inner :: ScrubbedBytes])
          where 
                blockSize  = hashBlockSize alg
                digestSize = hashDigestSize alg
                initCtx    = hashInitWith alg

initialize :: (ByteArrayAccess key, HashAlgorithm a)
           => key       -- ^ Secret key
           -> Context a
initialize secret = unsafeDoIO (doHashAlg undefined)
  where
        doHashAlg :: HashAlgorithm a => a -> IO (Context a)
        doHashAlg alg = do
            !withKey <- case B.length secret `compare` blockSize of
                            EQ -> return $ B.withByteArray secret
                            LT -> do key <- B.alloc blockSize $ \k -> do
                                        memSet k 0 blockSize
                                        B.withByteArray secret $ \s -> memCopy k s (B.length secret)
                                     return $ B.withByteArray (key :: ScrubbedBytes)
                            GT -> do
                                -- hash the secret key
                                ctx <- hashMutableInitWith alg
                                hashMutableUpdate ctx secret
                                digest <- hashMutableFinalize ctx
                                hashMutableReset ctx
                                -- pad it if necessary
                                if digestSize < blockSize
                                    then do
                                        key <- B.alloc blockSize $ \k -> do
                                            memSet k 0 blockSize
                                            B.withByteArray digest $ \s -> memCopy k s (B.length digest)
                                        return $ B.withByteArray (key :: ScrubbedBytes)
                                    else
                                       return $ B.withByteArray digest
            (inner, outer) <- withKey $ \keyPtr ->
                (,) <$> B.alloc blockSize (\p -> memXorWith p 0x36 keyPtr blockSize)
                    <*> B.alloc blockSize (\p -> memXorWith p 0x5c keyPtr blockSize)
            return $ Context (hashUpdates initCtx [outer :: ScrubbedBytes])
                             (hashUpdates initCtx [inner :: ScrubbedBytes])
          where 
                blockSize  = hashBlockSize alg
                digestSize = hashDigestSize alg
                initCtx    = hashInitWith alg

initialize secret = unsafeDoIO (doHashAlg undefined)
  where
        doHashAlg :: HashAlgorithm a => a -> IO (Context a)
        doHashAlg alg = do
            !withKey <- case B.length secret `compare` blockSize of
                            EQ -> return $ B.withByteArray secret
                            LT -> do key <- B.alloc blockSize $ \k -> do
                                        memSet k 0 blockSize
                                        B.withByteArray secret $ \s -> memCopy k s (B.length secret)
                                     return $ B.withByteArray (key :: ScrubbedBytes)
                            GT -> do
                                -- hash the secret key
                                ctx <- hashMutableInitWith alg
                                hashMutableUpdate ctx secret
                                digest <- hashMutableFinalize ctx
                                hashMutableReset ctx
                                -- pad it if necessary
                                if digestSize < blockSize
                                    then do
                                        key <- B.alloc blockSize $ \k -> do
                                            memSet k 0 blockSize
                                            B.withByteArray digest $ \s -> memCopy k s (B.length digest)
                                        return $ B.withByteArray (key :: ScrubbedBytes)
                                    else
                                       return $ B.withByteArray digest
            (inner, outer) <- withKey $ \keyPtr ->
                (,) <$> B.alloc blockSize (\p -> memXorWith p 0x36 keyPtr blockSize)
                    <*> B.alloc blockSize (\p -> memXorWith p 0x5c keyPtr blockSize)
            return $ Context (hashUpdates initCtx [outer :: ScrubbedBytes])
                             (hashUpdates initCtx [inner :: ScrubbedBytes])
          where 
                blockSize  = hashBlockSize alg
                digestSize = hashDigestSize alg
                initCtx    = hashInitWith alg
