content: {-# LANGUAGE LambdaCase #-}
  {-# LANGUAGE MultiWayIf #-}
  {-# LANGUAGE PolyKinds #-}
  {-# LANGUAGE RecordWildCards #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TupleSections #-}
  {-# LANGUAGE ViewPatterns #-}
  
  module Network.GRPC.LowLevel.CompletionQueue.Unregistered where
  
  import Control.Monad.Managed
  import Control.Monad.Trans.Class (MonadTrans (lift))
  import Control.Monad.Trans.Except
  import Foreign.Storable (peek)
  import Network.GRPC.LowLevel.Call
  import qualified Network.GRPC.LowLevel.Call.Unregistered as U
  import Network.GRPC.LowLevel.CompletionQueue.Internal
  import Network.GRPC.LowLevel.GRPC
  import qualified Network.GRPC.Unsafe as C
  import qualified Network.GRPC.Unsafe.Constants as C
  import qualified Network.GRPC.Unsafe.Metadata as C
  import qualified Network.GRPC.Unsafe.Time as C
  
  channelCreateCall ::
    C.Channel ->
    C.Call ->
    C.PropagationMask ->
    CompletionQueue ->
    MethodName ->
    Endpoint ->
    C.CTimeSpecPtr ->
    IO (Either GRPCIOError ClientCall)
  channelCreateCall chan parent mask cq@CompletionQueue{..} meth endpt deadline =
    withPermission Push cq $ do
      call <-
        C.grpcChannelCreateCall
          chan
          parent
          mask
          unsafeCQ
          (unMethodName meth)
          (unEndpoint endpt)
          deadline
          C.reserved
      return $ Right $ ClientCall call
  
  serverRequestCall ::
    C.Server ->
    -- | server CQ / notification CQ
    CompletionQueue ->
    -- | call CQ
    CompletionQueue ->
    IO (Either GRPCIOError U.ServerCall)
  serverRequestCall s scq ccq =
    withPermission Push scq . with allocs $ \(call, meta, cd) ->
      withPermission Pluck scq $ do
        md <- peek meta
        tag <- newTag scq
        dbug $ "got pluck permission, registering call for tag=" ++ show tag
        ce <- C.grpcServerRequestCall s call cd md (unsafeCQ ccq) (unsafeCQ scq) tag
        runExceptT $ case ce of
          C.CallOk -> do
            ExceptT $ do
              let
                rec = do
                  -- yield every second, for interruptibility
                  r <- pluck' scq tag (Just 1)
                  case r of
                    Left GRPCIOTimeout -> rec
                    _ -> return r
              r <- rec
              dbug $ "pluck' finished: " ++ show r
              return r
            lift $
              U.ServerCall
                <$> peek call
                <*> return ccq
                <*> C.getAllMetadataArray md
                <*> (C.timeSpec <$> C.callDetailsGetDeadline cd)
                <*> (MethodName <$> C.callDetailsGetMethod cd)
                <*> (Host <$> C.callDetailsGetHost cd)
          _ -> do
            lift $ dbug $ "Throwing callError: " ++ show ce
            throwE $ GRPCIOCallError ce
    where
      allocs =
        (,,)
          <$> mgdPtr
          <*> managed C.withMetadataArrayPtr
          <*> managed C.withCallDetails
      dbug = grpcDebug . ("serverRequestCall(U): " ++)
  