{-# LANGUAGE DataKinds      #-}
{-# LANGUAGE LambdaCase     #-}
{-# LANGUAGE NamedFieldPuns #-}
module Convex.NodeClient.Resuming(
  ResumingFrom(..),
  resumingClient) where

import           Cardano.Api                                          (ChainPoint (..),
                                                                       ChainTip (..))
import           Convex.NodeClient.Types                              (ClientBlock,
                                                                       PipelinedLedgerStateClient (..))
import           Network.TypedProtocol.Pipelined                      (N (Z))
import qualified Ouroboros.Network.Protocol.ChainSync.ClientPipelined as CSP

{-| Where we start processing blocks. This is the intersection between the
chain points passed to 'runNodeClients' and the blockchain that the node has.
-}
data ResumingFrom =
  ResumingFromChainPoint{ ResumingFrom -> ChainPoint
chainPoint :: ChainPoint, ResumingFrom -> ChainTip
serverTip :: ChainTip}
  | ResumingFromOrigin{serverTip :: ChainTip}

{-| Turn a 'PipelinedLedgerStateClient' into one that resumes processing from one
of a list of 'ChainPoint's.
-}
resumingClient ::
  -- | List of synchronisation points. If the list is empty, the client will receive all blocks, starting from genesis.
  [ChainPoint] ->
  -- | Function that returns the actual node client, depending on where we resumed from
  (ResumingFrom -> PipelinedLedgerStateClient) ->
  PipelinedLedgerStateClient
resumingClient :: [ChainPoint]
-> (ResumingFrom -> PipelinedLedgerStateClient)
-> PipelinedLedgerStateClient
resumingClient [ChainPoint]
syncPoints ResumingFrom -> PipelinedLedgerStateClient
f = ChainSyncClientPipelined
  (BlockInMode CardanoMode) ChainPoint ChainTip IO ()
-> PipelinedLedgerStateClient
PipelinedLedgerStateClient forall a b. (a -> b) -> a -> b
$ forall header point tip (m :: * -> *) a.
m (ClientPipelinedStIdle 'Z header point tip m a)
-> ChainSyncClientPipelined header point tip m a
CSP.ChainSyncClientPipelined forall a b. (a -> b) -> a -> b
$ do
  let initialise :: CSP.ClientPipelinedStIdle 'Z ClientBlock ChainPoint ChainTip IO ()
      initialise :: ClientPipelinedStIdle
  'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ()
initialise = forall point header tip (m :: * -> *) a.
[point]
-> ClientPipelinedStIntersect header point tip m a
-> ClientPipelinedStIdle 'Z header point tip m a
CSP.SendMsgFindIntersect [ChainPoint]
syncPoints forall a b. (a -> b) -> a -> b
$
        CSP.ClientPipelinedStIntersect {
          recvMsgIntersectFound :: ChainPoint
-> ChainTip
-> IO
     (ClientPipelinedStIdle
        'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ())
CSP.recvMsgIntersectFound    = \ChainPoint
chainPoint ChainTip
srvTip -> do
            let CSP.ChainSyncClientPipelined{IO
  (ClientPipelinedStIdle
     'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ())
runChainSyncClientPipelined :: forall header point tip (m :: * -> *) a.
ChainSyncClientPipelined header point tip m a
-> m (ClientPipelinedStIdle 'Z header point tip m a)
runChainSyncClientPipelined :: IO
  (ClientPipelinedStIdle
     'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ())
CSP.runChainSyncClientPipelined} = PipelinedLedgerStateClient
-> ChainSyncClientPipelined
     (BlockInMode CardanoMode) ChainPoint ChainTip IO ()
getPipelinedLedgerStateClient (ResumingFrom -> PipelinedLedgerStateClient
f forall a b. (a -> b) -> a -> b
$ ChainPoint -> ChainTip -> ResumingFrom
ResumingFromChainPoint ChainPoint
chainPoint ChainTip
srvTip)
            IO
  (ClientPipelinedStIdle
     'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ())
runChainSyncClientPipelined,
          recvMsgIntersectNotFound :: ChainTip
-> IO
     (ClientPipelinedStIdle
        'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ())
CSP.recvMsgIntersectNotFound = \ChainTip
srvTip   -> do
            let CSP.ChainSyncClientPipelined{IO
  (ClientPipelinedStIdle
     'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ())
runChainSyncClientPipelined :: IO
  (ClientPipelinedStIdle
     'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ())
runChainSyncClientPipelined :: forall header point tip (m :: * -> *) a.
ChainSyncClientPipelined header point tip m a
-> m (ClientPipelinedStIdle 'Z header point tip m a)
CSP.runChainSyncClientPipelined} = PipelinedLedgerStateClient
-> ChainSyncClientPipelined
     (BlockInMode CardanoMode) ChainPoint ChainTip IO ()
getPipelinedLedgerStateClient (ResumingFrom -> PipelinedLedgerStateClient
f forall a b. (a -> b) -> a -> b
$ ChainTip -> ResumingFrom
ResumingFromOrigin ChainTip
srvTip)
            IO
  (ClientPipelinedStIdle
     'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ())
runChainSyncClientPipelined
        }

  forall (f :: * -> *) a. Applicative f => a -> f a
pure ClientPipelinedStIdle
  'Z (BlockInMode CardanoMode) ChainPoint ChainTip IO ()
initialise