{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE JavaScriptFFI #-}
{-# OPTIONS_HADDOCK not-home #-}

-- | Implementation to be used when compiled with GHCJS
module Bitcoin.Hash.GHCJS
  ( hash160
  , hash256
  , ripemd160
  , sha256
  , hmacSHA512
  ) where

import qualified Data.ByteString as B
import qualified GHCJS.Buffer as Buf
import qualified JavaScript.TypedArray.ArrayBuffer as AB
import qualified JavaScript.TypedArray as TA

--------------------------------------------------------------------------------

-- | @'hash160' == 'ripemd160' . 'sha256'@
hash160
  :: B.ByteString  -- ^ Data to hash.
  -> B.ByteString  -- ^ 160-bit long digest.
{-# INLINE hash160 #-}
hash160 = byteStringFromUint8Array
        . js_ripemd160
        . js_sha256
        . byteStringToUint8Array

--------------------------------------------------------------------------------

-- | @'hash256' == 'sha256' . 'sha256'@
hash256
  :: B.ByteString  -- ^ Data to hash.
  -> B.ByteString  -- ^ 256-bit long digest.
{-# INLINE hash256 #-}
hash256 = byteStringFromUint8Array
        . js_sha256
        . js_sha256
        . byteStringToUint8Array

--------------------------------------------------------------------------------

-- | RIPEMD-160
ripemd160
  :: B.ByteString  -- ^ Data to hash.
  -> B.ByteString  -- ^ 160-bits long digest.
{-# INLINE ripemd160 #-}
ripemd160 = byteStringFromUint8Array . js_ripemd160 . byteStringToUint8Array

foreign import javascript unsafe "h$bitcoin_hash.ripemd160($1)"
  js_ripemd160 :: TA.Uint8Array -> TA.Uint8Array

--------------------------------------------------------------------------------

-- | SHA256
sha256
  :: B.ByteString  -- ^ Data to hash.
  -> B.ByteString  -- ^ 256-bits long digest.
{-# INLINE sha256 #-}
sha256 = byteStringFromUint8Array . js_sha256 . byteStringToUint8Array

foreign import javascript unsafe "h$bitcoin_hash.sha256($1)"
  js_sha256 :: TA.Uint8Array -> TA.Uint8Array

--------------------------------------------------------------------------------

-- | HMAC-SHA512
hmacSHA512
  :: B.ByteString  -- ^ Arbitrary length key.
  -> B.ByteString  -- ^ Data to hash.
  -> B.ByteString  -- ^ 64-byte long digest.
{-# INLINE hmacSHA512 #-}
hmacSHA512 k d =
  byteStringFromUint8Array $ js_hmacSHA512 (byteStringToUint8Array k)
                                           (byteStringToUint8Array d)

foreign import javascript unsafe "h$bitcoin_hash.hmacSHA512($1, $2)"
  js_hmacSHA512 :: TA.Uint8Array -> TA.Uint8Array -> TA.Uint8Array

--------------------------------------------------------------------------------

byteStringToUint8Array :: B.ByteString -> TA.Uint8Array
{-# INLINE byteStringToUint8Array #-}
byteStringToUint8Array = \x -> case B.length x of
  0 -> js_emptyUint8Array
  _ -> let (buf, off, len) = Buf.fromByteString x
       in js_newUint8Array (Buf.getArrayBuffer buf) off len

foreign import javascript unsafe "new Uint8Array($1, $2, $3)"
  js_newUint8Array :: AB.ArrayBuffer -> Int -> Int -> TA.Uint8Array

foreign import javascript unsafe "new Uint8Array(0)"
  js_emptyUint8Array :: TA.Uint8Array

--------------------------------------------------------------------------------

byteStringFromUint8Array :: TA.Uint8Array -> B.ByteString
{-# INLINE byteStringFromUint8Array #-}
byteStringFromUint8Array = \x ->
  Buf.toByteString (TA.byteOffset x)
                   (Just (TA.byteLength x))
                   (Buf.createFromArrayBuffer (TA.buffer x))