content: module GHCJS.Buffer
      ( Buffer
      , MutableBuffer
      , create
      , createFromArrayBuffer
      , thaw, freeze, clone
        -- * JavaScript properties
      , byteLength
      , getArrayBuffer
      , getUint8Array
      , getUint16Array
      , getInt32Array
      , getDataView
      , getFloat32Array
      , getFloat64Array
        -- * bytestring
      , toByteString, fromByteString
      ) where
  
  import GHCJS.Buffer.Types
  
  import Control.Lens.Operators ((^.))
  
  import Data.ByteString (ByteString)
  import qualified Data.ByteString as BS
  import qualified Data.ByteString.Base64 as B64 (encode, decode)
  import Data.Text.Encoding (encodeUtf8, decodeUtf8)
  
  import qualified JavaScript.TypedArray.Internal.Types as I
  import           JavaScript.TypedArray.ArrayBuffer.Internal (SomeArrayBuffer(..))
  import           JavaScript.TypedArray.DataView.Internal    (SomeDataView(..))
  
  import GHCJS.Marshal (FromJSVal(..))
  import Language.Javascript.JSaddle.Types (JSM, GHCJSPure(..), ghcjsPure)
  import Language.Javascript.JSaddle.Object (js, js2, jsg1, jsg3)
  
  create :: Int -> JSM MutableBuffer
  create n | n >= 0    = SomeBuffer <$> jsg1 "h$newByteArray" n
           | otherwise = error "create: negative size"
  {-# INLINE create #-}
  
  createFromArrayBuffer :: SomeArrayBuffer any -> GHCJSPure (SomeBuffer any)
  createFromArrayBuffer (SomeArrayBuffer buf) = GHCJSPure $ SomeBuffer <$> jsg1 "h$wrapBuffer" buf
  {-# INLINE createFromArrayBuffer #-}
  
  getArrayBuffer :: SomeBuffer any -> GHCJSPure (SomeArrayBuffer any)
  getArrayBuffer (SomeBuffer buf) = GHCJSPure $ SomeArrayBuffer <$> buf ^. js "buf"
  {-# INLINE getArrayBuffer #-}
  
  getInt32Array :: SomeBuffer any -> GHCJSPure (I.SomeInt32Array any)
  getInt32Array (SomeBuffer buf) = GHCJSPure $ I.SomeTypedArray <$> buf ^. js "i3"
  {-# INLINE getInt32Array #-}
  
  getUint8Array :: SomeBuffer any -> GHCJSPure (I.SomeUint8Array any)
  getUint8Array (SomeBuffer buf) = GHCJSPure $ I.SomeTypedArray <$> buf ^. js "u8"
  {-# INLINE getUint8Array #-}
  
  getUint16Array :: SomeBuffer any -> GHCJSPure (I.SomeUint16Array any)
  getUint16Array (SomeBuffer buf) = GHCJSPure $ I.SomeTypedArray <$> buf ^. js "u1"
  {-# INLINE getUint16Array #-}
  
  getFloat32Array :: SomeBuffer any -> GHCJSPure (I.SomeFloat32Array any)
  getFloat32Array (SomeBuffer buf) = GHCJSPure $ I.SomeTypedArray <$> buf ^. js "f3"
  {-# INLINE getFloat32Array #-}
  
  getFloat64Array :: SomeBuffer any -> GHCJSPure (I.SomeFloat64Array any)
  getFloat64Array (SomeBuffer buf) = GHCJSPure $ I.SomeTypedArray <$> buf ^. js "f6"
  {-# INLINE getFloat64Array #-}
  
  getDataView :: SomeBuffer any -> GHCJSPure (SomeDataView any)
  getDataView (SomeBuffer buf) = GHCJSPure $ SomeDataView  <$> buf ^. js "dv"
  {-# INLINE getDataView #-}
  
  freeze :: MutableBuffer -> JSM Buffer
  freeze = js_clone
  {-# INLINE freeze #-}
  
  thaw :: Buffer -> JSM MutableBuffer
  thaw  = js_clone
  {-# INLINE thaw #-}
  
  clone :: MutableBuffer -> JSM (SomeBuffer any2)
  clone = js_clone
  {-# INLINE clone #-}
  
  fromByteString :: ByteString -> GHCJSPure (Buffer, Int, Int)
  fromByteString bs = GHCJSPure $ do
    buffer <- SomeBuffer <$> jsg1 "h$newByteArrayFromBase64String" (decodeUtf8 $ B64.encode bs)
    return (buffer, 0, BS.length bs)
  {-# INLINE fromByteString #-}
  
  -- | Wrap a 'Buffer' into a 'ByteString' using the given offset
  -- and length.
  toByteString :: Int -> Maybe Int -> Buffer -> GHCJSPure ByteString
  toByteString off mbLen buf = GHCJSPure $ do
    bufLen <- ghcjsPure $ byteLength buf
    case mbLen of
      _        | off < 0            -> error "toByteString: negative offset"
               | off > bufLen       -> error "toByteString: offset past end of buffer"
      Just len | len < 0            -> error "toByteString: negative length"
               | len > bufLen - off -> error "toByteString: length past end of buffer"
               | otherwise          -> ghcjsPure $ unsafeToByteString off len buf
      Nothing                       -> ghcjsPure $ unsafeToByteString off (bufLen - off) buf
  
  unsafeToByteString :: Int -> Int -> Buffer -> GHCJSPure ByteString
  unsafeToByteString off len (SomeBuffer buf) = GHCJSPure $ do
    b64 <- jsg3 "h$byteArrayToBase64String" off len buf >>= fromJSValUnchecked
    return $ case B64.decode (encodeUtf8 b64) of
              Left err -> error $ "unsafeToByteString base 64 decode error :" ++ err
              Right bs -> bs
  
  byteLength :: SomeBuffer any -> GHCJSPure Int
  byteLength (SomeBuffer buf) = GHCJSPure $ buf ^. js "len" >>= fromJSValUnchecked
  {-# INLINE byteLength #-}
  
  js_clone :: SomeBuffer any1 -> JSM (SomeBuffer any2)
  js_clone (SomeBuffer buf) = SomeBuffer <$> jsg1 "h$wrapBuffer" (buf ^. js "buf" ^. js2 "slice" (buf ^. js "u8" ^. js "byteOffset") (buf ^. js "len"))
  