{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs     #-}

module Ouroboros.Network.Protocol.PeerSharing.Client where

import Network.TypedProtocol.Core (Peer (..), PeerHasAgency (..), PeerRole (..))
import Ouroboros.Network.Protocol.PeerSharing.Type (ClientHasAgency (..),
           Message (..), NobodyHasAgency (..), PeerSharing (..),
           PeerSharingAmount, ServerHasAgency (..))

data PeerSharingClient peerAddress m a where
  SendMsgShareRequest
    :: PeerSharingAmount
    -> ([peerAddress] -> m (PeerSharingClient peerAddress m a))
    -> PeerSharingClient peerAddress m a

  SendMsgDone
    :: m a -> PeerSharingClient peerAddress m a

-- | Interpret a particular client action sequence into the client side of the
-- 'PeerSharing' protocol.
--
peerSharingClientPeer :: Monad m
                      => PeerSharingClient peerAddress m a
                      -> Peer (PeerSharing peerAddress) AsClient StIdle m a
peerSharingClientPeer :: forall (m :: * -> *) peerAddress a.
Monad m =>
PeerSharingClient peerAddress m a
-> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a
peerSharingClientPeer (SendMsgShareRequest PeerSharingAmount
amount [peerAddress] -> m (PeerSharingClient peerAddress m a)
k) =
  -- Send MsgShareRequest message
  WeHaveAgency 'AsClient 'StIdle
-> Message (PeerSharing peerAddress) 'StIdle 'StBusy
-> Peer (PeerSharing peerAddress) 'AsClient 'StBusy m a
-> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a
forall (pr :: PeerRole) ps (st :: ps) (st' :: ps) (m :: * -> *) a.
WeHaveAgency pr st
-> Message ps st st' -> Peer ps pr st' m a -> Peer ps pr st m a
Yield (ClientHasAgency 'StIdle -> WeHaveAgency 'AsClient 'StIdle
forall {ps} (st :: ps).
ClientHasAgency st -> PeerHasAgency 'AsClient st
ClientAgency ClientHasAgency 'StIdle
forall {k} {peerAddress :: k}. ClientHasAgency 'StIdle
TokIdle) (PeerSharingAmount
-> Message (PeerSharing peerAddress) 'StIdle 'StBusy
forall {k} (peerAddress :: k).
PeerSharingAmount
-> Message (PeerSharing peerAddress) 'StIdle 'StBusy
MsgShareRequest PeerSharingAmount
amount) (Peer (PeerSharing peerAddress) 'AsClient 'StBusy m a
 -> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a)
-> Peer (PeerSharing peerAddress) 'AsClient 'StBusy m a
-> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a
forall a b. (a -> b) -> a -> b
$
    -- Wait for the reply (notice the agency proofs)
    TheyHaveAgency 'AsClient 'StBusy
-> (forall {st' :: PeerSharing peerAddress}.
    Message (PeerSharing peerAddress) 'StBusy st'
    -> Peer (PeerSharing peerAddress) 'AsClient st' m a)
-> Peer (PeerSharing peerAddress) 'AsClient 'StBusy m a
forall (pr :: PeerRole) ps (st :: ps) (m :: * -> *) a.
TheyHaveAgency pr st
-> (forall (st' :: ps). Message ps st st' -> Peer ps pr st' m a)
-> Peer ps pr st m a
Await (ServerHasAgency 'StBusy -> PeerHasAgency 'AsServer 'StBusy
forall {ps} (st :: ps).
ServerHasAgency st -> PeerHasAgency 'AsServer st
ServerAgency ServerHasAgency 'StBusy
forall {k} {peerAddress :: k}. ServerHasAgency 'StBusy
TokBusy) ((forall {st' :: PeerSharing peerAddress}.
  Message (PeerSharing peerAddress) 'StBusy st'
  -> Peer (PeerSharing peerAddress) 'AsClient st' m a)
 -> Peer (PeerSharing peerAddress) 'AsClient 'StBusy m a)
-> (forall {st' :: PeerSharing peerAddress}.
    Message (PeerSharing peerAddress) 'StBusy st'
    -> Peer (PeerSharing peerAddress) 'AsClient st' m a)
-> Peer (PeerSharing peerAddress) 'AsClient 'StBusy m a
forall a b. (a -> b) -> a -> b
$ \(MsgSharePeers [peerAddress1]
resp) ->
      -- We have our reply. We might want to perform some action with it so we
      -- run the continuation to handle t he response.
      m (Peer (PeerSharing peerAddress) 'AsClient st' m a)
-> Peer (PeerSharing peerAddress) 'AsClient st' m a
forall (m :: * -> *) ps (pr :: PeerRole) (st :: ps) a.
m (Peer ps pr st m a) -> Peer ps pr st m a
Effect (m (Peer (PeerSharing peerAddress) 'AsClient st' m a)
 -> Peer (PeerSharing peerAddress) 'AsClient st' m a)
-> m (Peer (PeerSharing peerAddress) 'AsClient st' m a)
-> Peer (PeerSharing peerAddress) 'AsClient st' m a
forall a b. (a -> b) -> a -> b
$ PeerSharingClient peerAddress m a
-> Peer (PeerSharing peerAddress) 'AsClient st' m a
PeerSharingClient peerAddress m a
-> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a
forall (m :: * -> *) peerAddress a.
Monad m =>
PeerSharingClient peerAddress m a
-> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a
peerSharingClientPeer (PeerSharingClient peerAddress m a
 -> Peer (PeerSharing peerAddress) 'AsClient st' m a)
-> m (PeerSharingClient peerAddress m a)
-> m (Peer (PeerSharing peerAddress) 'AsClient st' m a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [peerAddress] -> m (PeerSharingClient peerAddress m a)
k [peerAddress]
[peerAddress1]
resp
peerSharingClientPeer (SendMsgDone m a
result) =
    -- Perform some finishing action
    -- Perform a transition to the 'StDone' state
    m (Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a)
-> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a
forall (m :: * -> *) ps (pr :: PeerRole) (st :: ps) a.
m (Peer ps pr st m a) -> Peer ps pr st m a
Effect (m (Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a)
 -> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a)
-> m (Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a)
-> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a
forall a b. (a -> b) -> a -> b
$ WeHaveAgency 'AsClient 'StIdle
-> Message (PeerSharing peerAddress) 'StIdle 'StDone
-> Peer (PeerSharing peerAddress) 'AsClient 'StDone m a
-> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a
forall (pr :: PeerRole) ps (st :: ps) (st' :: ps) (m :: * -> *) a.
WeHaveAgency pr st
-> Message ps st st' -> Peer ps pr st' m a -> Peer ps pr st m a
Yield (ClientHasAgency 'StIdle -> WeHaveAgency 'AsClient 'StIdle
forall {ps} (st :: ps).
ClientHasAgency st -> PeerHasAgency 'AsClient st
ClientAgency ClientHasAgency 'StIdle
forall {k} {peerAddress :: k}. ClientHasAgency 'StIdle
TokIdle) Message (PeerSharing peerAddress) 'StIdle 'StDone
forall {k} (peerAddress :: k).
Message (PeerSharing peerAddress) 'StIdle 'StDone
MsgDone (Peer (PeerSharing peerAddress) 'AsClient 'StDone m a
 -> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a)
-> (a -> Peer (PeerSharing peerAddress) 'AsClient 'StDone m a)
-> a
-> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NobodyHasAgency 'StDone
-> a -> Peer (PeerSharing peerAddress) 'AsClient 'StDone m a
forall ps (st :: ps) a (pr :: PeerRole) (m :: * -> *).
NobodyHasAgency st -> a -> Peer ps pr st m a
Done NobodyHasAgency 'StDone
forall {k} {peerAddress :: k}. NobodyHasAgency 'StDone
TokDone (a -> Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a)
-> m a -> m (Peer (PeerSharing peerAddress) 'AsClient 'StIdle m a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m a
result