-- |
-- Module      : Relay.Reliable.Inbox
-- Description : Receive half of the reliable-delivery protocol.
module Relay.Reliable.Inbox
  ( Inbox (..),
    emptyInbox,
    recv,
    ackFrontier,
  )
where

import Relay.ReceiverHalf (ReceiverHalf, emptyReceiver, expected, receive)
import Relay.Session (Frame (..), Nat)

-- | The receive half of one reliable session: the dual of 'Outbox'.
--
-- It wraps a 'ReceiverHalf', which tracks the /delivery frontier/: the
-- highest sequence number released in order so far. Every sequence
-- number up to and including the frontier has been delivered to the
-- application; nothing above it has (out-of-order arrivals wait in the
-- reorder buffer until the gap fills). 'Inbox' adds the cumulative ack
-- value derived from that frontier.
newtype Inbox a = Inbox {forall a. Inbox a -> ReceiverHalf a
half :: ReceiverHalf a}
  deriving (Int -> Inbox a -> ShowS
[Inbox a] -> ShowS
Inbox a -> String
(Int -> Inbox a -> ShowS)
-> (Inbox a -> String) -> ([Inbox a] -> ShowS) -> Show (Inbox a)
forall a. Show a => Int -> Inbox a -> ShowS
forall a. Show a => [Inbox a] -> ShowS
forall a. Show a => Inbox a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> Inbox a -> ShowS
showsPrec :: Int -> Inbox a -> ShowS
$cshow :: forall a. Show a => Inbox a -> String
show :: Inbox a -> String
$cshowList :: forall a. Show a => [Inbox a] -> ShowS
showList :: [Inbox a] -> ShowS
Show, Inbox a -> Inbox a -> Bool
(Inbox a -> Inbox a -> Bool)
-> (Inbox a -> Inbox a -> Bool) -> Eq (Inbox a)
forall a. Eq a => Inbox a -> Inbox a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. Eq a => Inbox a -> Inbox a -> Bool
== :: Inbox a -> Inbox a -> Bool
$c/= :: forall a. Eq a => Inbox a -> Inbox a -> Bool
/= :: Inbox a -> Inbox a -> Bool
Eq)

-- | Initial inbox state: nothing received, expecting sequence 1.
emptyInbox :: Inbox a
emptyInbox :: forall a. Inbox a
emptyInbox = ReceiverHalf a -> Inbox a
forall a. ReceiverHalf a -> Inbox a
Inbox ReceiverHalf a
forall a. ReceiverHalf a
emptyReceiver

-- | Accept one arriving frame, delegating dedup, reorder buffering, and
-- in-order release to 'receive'. Returns the updated inbox and any newly
-- deliverable payloads.
recv :: Frame a -> Inbox a -> (Inbox a, [a])
recv :: forall a. Frame a -> Inbox a -> (Inbox a, [a])
recv Frame a
frame (Inbox ReceiverHalf a
rh) =
  let (ReceiverHalf a
rh', [a]
delivered) = Nat -> a -> ReceiverHalf a -> (ReceiverHalf a, [a])
forall a. Nat -> a -> ReceiverHalf a -> (ReceiverHalf a, [a])
receive (Frame a -> Nat
forall a. Frame a -> Nat
frameSeq Frame a
frame) (Frame a -> a
forall a. Frame a -> a
framePayload Frame a
frame) ReceiverHalf a
rh
   in (ReceiverHalf a -> Inbox a
forall a. ReceiverHalf a -> Inbox a
Inbox ReceiverHalf a
rh', [a]
delivered)

-- | The cumulative acknowledgement value: the delivery frontier
-- expressed as an ack. 'Nothing' before sequence 1 has arrived
-- (one-based convention), otherwise @Just (expected - 1)@ — the highest
-- sequence number delivered in order so far.
ackFrontier :: Inbox a -> Maybe Nat
ackFrontier :: forall a. Inbox a -> Maybe Nat
ackFrontier (Inbox ReceiverHalf a
rh)
  | ReceiverHalf a -> Nat
forall a. ReceiverHalf a -> Nat
expected ReceiverHalf a
rh Nat -> Nat -> Bool
forall a. Ord a => a -> a -> Bool
<= Nat
1 = Maybe Nat
forall a. Maybe a
Nothing
  | Bool
otherwise = Nat -> Maybe Nat
forall a. a -> Maybe a
Just (ReceiverHalf a -> Nat
forall a. ReceiverHalf a -> Nat
expected ReceiverHalf a
rh Nat -> Nat -> Nat
forall a. Num a => a -> a -> a
- Nat
1)