{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Ouroboros.Network.Diffusion
  ( -- * Common API
    P2P (..)
  , DiffusionTracer (..)
  , Tracers (..)
  , nullTracers
  , ExtraTracers (..)
  , Failure (..)
  , Arguments (..)
  , ExtraArguments (..)
  , Applications (..)
  , ExtraApplications (..)
    -- * Run data diffusion
  , run
    -- * Re-exports
  , P2P.AbstractTransitionTrace
  , PublicPeerSelectionState
  , makePublicPeerSelectionStateVar
  ) where

import Control.Exception (IOException)
import Data.Functor (void)

import Network.Socket (Socket)

import Ouroboros.Network.NodeToClient (LocalAddress, LocalSocket,
           NodeToClientVersion, NodeToClientVersionData)
import Ouroboros.Network.NodeToNode (NodeToNodeVersion, NodeToNodeVersionData,
           RemoteAddress)
import Ouroboros.Network.PeerSelection.Governor.Types

import Ouroboros.Network.Diffusion.Common as Common
import Ouroboros.Network.Diffusion.NonP2P qualified as NonP2P
import Ouroboros.Network.Diffusion.P2P qualified as P2P

-- | Promoted data types.
--
data P2P = P2P | NonP2P

-- | Tracers which depend on p2p mode.
--
data ExtraTracers (p2p :: P2P) where
  P2PTracers
    :: P2P.TracersExtra
           RemoteAddress  NodeToNodeVersion   NodeToNodeVersionData
           LocalAddress   NodeToClientVersion NodeToClientVersionData
           IOException IO
    -> ExtraTracers 'P2P

  NonP2PTracers
    :: NonP2P.TracersExtra
    -> ExtraTracers 'NonP2P


-- | Diffusion arguments which depend on p2p mode.
--
data ExtraArguments (p2p :: P2P) m where
  P2PArguments
    :: P2P.ArgumentsExtra m
    -> ExtraArguments 'P2P m

  NonP2PArguments
    :: NonP2P.ArgumentsExtra
    -> ExtraArguments 'NonP2P m


-- | Application data which depend on p2p mode.
--
data ExtraApplications (p2p :: P2P) ntnAddr m a where
  P2PApplications
    :: P2P.ApplicationsExtra ntnAddr m a
    -> ExtraApplications 'P2P ntnAddr m a

  NonP2PApplications
    :: NonP2P.ApplicationsExtra
    -> ExtraApplications 'NonP2P ntnAddr m a


-- | Run data diffusion in either 'P2P' or 'NonP2P' mode.
--
run :: forall (p2p :: P2P) a.
       Tracers
         RemoteAddress NodeToNodeVersion
         LocalAddress  NodeToClientVersion
         IO
    -> ExtraTracers p2p
    -> Arguments
         IO
         Socket      RemoteAddress
         LocalSocket LocalAddress
    -> ExtraArguments p2p IO
    -> Applications
         RemoteAddress  NodeToNodeVersion   NodeToNodeVersionData
         LocalAddress   NodeToClientVersion NodeToClientVersionData
         IO a
    -> ExtraApplications p2p RemoteAddress IO a
    -> IO ()
run :: forall (p2p :: P2P) a.
Tracers
  RemoteAddress NodeToNodeVersion LocalAddress NodeToClientVersion IO
-> ExtraTracers p2p
-> Arguments IO Socket RemoteAddress LocalSocket LocalAddress
-> ExtraArguments p2p IO
-> Applications
     RemoteAddress
     NodeToNodeVersion
     NodeToNodeVersionData
     LocalAddress
     NodeToClientVersion
     NodeToClientVersionData
     IO
     a
-> ExtraApplications p2p RemoteAddress IO a
-> IO ()
run Tracers
  RemoteAddress NodeToNodeVersion LocalAddress NodeToClientVersion IO
tracers (P2PTracers TracersExtra
  RemoteAddress
  NodeToNodeVersion
  NodeToNodeVersionData
  LocalAddress
  NodeToClientVersion
  NodeToClientVersionData
  IOException
  IO
tracersExtra)
            Arguments IO Socket RemoteAddress LocalSocket LocalAddress
args (P2PArguments ArgumentsExtra IO
argsExtra)
            Applications
  RemoteAddress
  NodeToNodeVersion
  NodeToNodeVersionData
  LocalAddress
  NodeToClientVersion
  NodeToClientVersionData
  IO
  a
apps (P2PApplications ApplicationsExtra RemoteAddress IO a
appsExtra) =
    IO Void -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO Void -> IO ()) -> IO Void -> IO ()
forall a b. (a -> b) -> a -> b
$
    Tracers
  RemoteAddress NodeToNodeVersion LocalAddress NodeToClientVersion IO
-> TracersExtra
     RemoteAddress
     NodeToNodeVersion
     NodeToNodeVersionData
     LocalAddress
     NodeToClientVersion
     NodeToClientVersionData
     IOException
     IO
-> Arguments IO Socket RemoteAddress LocalSocket LocalAddress
-> ArgumentsExtra IO
-> Applications
     RemoteAddress
     NodeToNodeVersion
     NodeToNodeVersionData
     LocalAddress
     NodeToClientVersion
     NodeToClientVersionData
     IO
     a
-> ApplicationsExtra RemoteAddress IO a
-> IO Void
forall a.
Tracers
  RemoteAddress NodeToNodeVersion LocalAddress NodeToClientVersion IO
-> TracersExtra
     RemoteAddress
     NodeToNodeVersion
     NodeToNodeVersionData
     LocalAddress
     NodeToClientVersion
     NodeToClientVersionData
     IOException
     IO
-> Arguments IO Socket RemoteAddress LocalSocket LocalAddress
-> ArgumentsExtra IO
-> Applications
     RemoteAddress
     NodeToNodeVersion
     NodeToNodeVersionData
     LocalAddress
     NodeToClientVersion
     NodeToClientVersionData
     IO
     a
-> ApplicationsExtra RemoteAddress IO a
-> IO Void
P2P.run Tracers
  RemoteAddress NodeToNodeVersion LocalAddress NodeToClientVersion IO
tracers TracersExtra
  RemoteAddress
  NodeToNodeVersion
  NodeToNodeVersionData
  LocalAddress
  NodeToClientVersion
  NodeToClientVersionData
  IOException
  IO
tracersExtra
            Arguments IO Socket RemoteAddress LocalSocket LocalAddress
args ArgumentsExtra IO
argsExtra
            Applications
  RemoteAddress
  NodeToNodeVersion
  NodeToNodeVersionData
  LocalAddress
  NodeToClientVersion
  NodeToClientVersionData
  IO
  a
apps ApplicationsExtra RemoteAddress IO a
appsExtra
run Tracers
  RemoteAddress NodeToNodeVersion LocalAddress NodeToClientVersion IO
tracers (NonP2PTracers TracersExtra
tracersExtra)
            Arguments IO Socket RemoteAddress LocalSocket LocalAddress
args (NonP2PArguments ArgumentsExtra
argsExtra)
            Applications
  RemoteAddress
  NodeToNodeVersion
  NodeToNodeVersionData
  LocalAddress
  NodeToClientVersion
  NodeToClientVersionData
  IO
  a
apps (NonP2PApplications ApplicationsExtra
appsExtra) =
    Tracers
  RemoteAddress NodeToNodeVersion LocalAddress NodeToClientVersion IO
-> TracersExtra
-> Arguments IO Socket RemoteAddress LocalSocket LocalAddress
-> ArgumentsExtra
-> Applications
     RemoteAddress
     NodeToNodeVersion
     NodeToNodeVersionData
     LocalAddress
     NodeToClientVersion
     NodeToClientVersionData
     IO
     a
-> ApplicationsExtra
-> IO ()
forall a.
Tracers
  RemoteAddress NodeToNodeVersion LocalAddress NodeToClientVersion IO
-> TracersExtra
-> Arguments IO Socket RemoteAddress LocalSocket LocalAddress
-> ArgumentsExtra
-> Applications
     RemoteAddress
     NodeToNodeVersion
     NodeToNodeVersionData
     LocalAddress
     NodeToClientVersion
     NodeToClientVersionData
     IO
     a
-> ApplicationsExtra
-> IO ()
NonP2P.run Tracers
  RemoteAddress NodeToNodeVersion LocalAddress NodeToClientVersion IO
tracers TracersExtra
tracersExtra
               Arguments IO Socket RemoteAddress LocalSocket LocalAddress
args ArgumentsExtra
argsExtra
               Applications
  RemoteAddress
  NodeToNodeVersion
  NodeToNodeVersionData
  LocalAddress
  NodeToClientVersion
  NodeToClientVersionData
  IO
  a
apps ApplicationsExtra
appsExtra