{-# language BangPatterns #-}
{-# language DataKinds #-}

module Socket.Stream.Interruptible.Addr
  ( send
  , receiveExactly
  , receiveOnce
  , receiveBetween
  ) where

import Data.Bytes.Types (UnmanagedBytes(..))
import Data.Primitive.Addr (Addr)
import Control.Concurrent.STM (TVar)
import Socket.Stream (Connection,ReceiveException,SendException)
import Socket (Interruptibility(Interruptible))

import qualified Socket.Stream.Interruptible.Addr.Send as Send
import qualified Socket.Stream.Interruptible.Addr.Receive as Receive

-- | Send an exact number of bytes starting from a given address. If needed,
--   this calls POSIX @send@ repeatedly until the requested number of bytes
--   has been sent.
send ::
     TVar Bool
     -- ^ Interrupt. On 'True', give up and return @'Left' 'SendInterrupted'@.
  -> Connection -- ^ Connection
  -> Addr -- ^ Start of buffer
  -> Int -- ^ Number of bytes to send
  -> IO (Either (SendException 'Interruptible) ())
{-# inline send #-}
send !tv !conn !addr !len = Send.send tv conn (UnmanagedBytes addr len)

-- | Receive the requested number of bytes into memory beginning at
--   the specified address. If needed, this may call @recv@ repeatedly until
--   the requested number of bytes have been received.
receiveExactly ::
     TVar Bool
     -- ^ Interrupt. On 'True', give up and return @'Left' 'ReceiveInterrupted'@.
  -> Connection -- ^ Connection
  -> Addr -- ^ Start of buffer
  -> Int -- ^ Exact number of bytes to receive
  -> IO (Either (ReceiveException 'Interruptible) ())
{-# inline receiveExactly #-}
receiveExactly !tv !conn !addr !len =
  Receive.receiveExactly tv conn (UnmanagedBytes addr len)

-- | Receive at most the specified number of bytes. This
-- only makes multiple calls to POSIX @recv@ if EAGAIN is returned. It makes at
-- most one @recv@ call that successfully fills the buffer.
receiveOnce ::
     TVar Bool
     -- ^ Interrupt. On 'True', give up and return @'Left' 'ReceiveInterrupted'@.
  -> Connection -- ^ Connection
  -> Addr -- ^ Start of buffer
  -> Int -- ^ Maximum number of bytes to receive
  -> IO (Either (ReceiveException 'Interruptible) Int)
{-# inline receiveOnce #-}
receiveOnce tv conn addr len =
  Receive.receiveOnce tv conn (UnmanagedBytes addr len)

-- | Receive a number of bytes that is between the inclusive lower and
--   upper bounds. If needed, this may call @recv@ repeatedly until the
--   minimum requested number of bytes have been received.
receiveBetween ::
     TVar Bool
     -- ^ Interrupt. On 'True', give up and return @'Left' 'ReceiveInterrupted'@.
  -> Connection -- ^ Connection
  -> Addr -- ^ Start of buffer
  -> Int
     -- ^ Minimum number of bytes to receive
  -> Int -- ^ Maximum number of bytes to receive
  -> IO (Either (ReceiveException 'Interruptible) Int)
{-# inline receiveBetween #-}
receiveBetween tv conn addr minLen maxLen =
  Receive.receiveBetween tv conn (UnmanagedBytes addr maxLen) minLen