content: {-# LANGUAGE NamedFieldPuns #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Simplex.Messaging.Transport.HTTP2 where
  
  import qualified Control.Exception as E
  import Data.ByteString.Char8 (ByteString)
  import qualified Data.ByteString.Char8 as B
  import Data.Default (def)
  import Data.Maybe (fromMaybe)
  import Foreign (mallocBytes)
  import Network.HPACK (BufferSize)
  import Network.HTTP2.Client (Config (..), defaultPositionReadMaker, freeSimpleConfig)
  import qualified Network.HTTP2.Client as HC
  import qualified Network.HTTP2.Server as HS
  import Network.Socket (SockAddr (..))
  import qualified Network.TLS as T
  import qualified Network.TLS.Extra as TE
  import Simplex.Messaging.Transport (ALPN, TLS, Transport (cGet, cPut))
  import Simplex.Messaging.Transport.Buffer
  import qualified System.TimeManager as TI
  
  defaultHTTP2BufferSize :: BufferSize
  defaultHTTP2BufferSize = 32768
  
  withHTTP2 :: BufferSize -> (Config -> IO a) -> IO () -> TLS p -> IO a
  withHTTP2 sz run fin c = E.bracket (allocHTTP2Config c sz) (\cfg -> freeSimpleConfig cfg `E.finally` fin) run
  
  allocHTTP2Config :: TLS p -> BufferSize -> IO Config
  allocHTTP2Config c sz = do
    buf <- mallocBytes sz
    tm <- TI.initialize $ 30 * 1000000
    pure
      Config
        { confWriteBuffer = buf,
          confBufferSize = sz,
          confSendAll = cPut c,
          confReadN = cGet c,
          confPositionReadMaker = defaultPositionReadMaker,
          confTimeoutManager = tm,
          confMySockAddr = SockAddrInet 0 0,
          confPeerSockAddr = SockAddrInet 0 0
        }
  
  http2TLSParams :: T.Supported
  http2TLSParams =
    def
      { T.supportedVersions = [T.TLS13, T.TLS12],
        T.supportedCiphers = TE.ciphersuite_strong_det,
        T.supportedSecureRenegotiation = False
      }
  
  data HTTP2Body = HTTP2Body
    { bodyHead :: ByteString,
      bodySize :: Int,
      bodyPart :: Maybe (Int -> IO ByteString),
      bodyBuffer :: TBuffer
    }
  
  class HTTP2BodyChunk a where
    getBodyChunk :: a -> IO ByteString
    getBodySize :: a -> Maybe Int
  
  instance HTTP2BodyChunk HC.Response where
    getBodyChunk = HC.getResponseBodyChunk
    {-# INLINE getBodyChunk #-}
    getBodySize = HC.responseBodySize
    {-# INLINE getBodySize #-}
  
  instance HTTP2BodyChunk HS.Request where
    getBodyChunk = HS.getRequestBodyChunk
    {-# INLINE getBodyChunk #-}
    getBodySize = HS.requestBodySize
    {-# INLINE getBodySize #-}
  
  getHTTP2Body :: HTTP2BodyChunk a => a -> Int -> IO HTTP2Body
  getHTTP2Body r n = do
    bodyBuffer <- newTBuffer
    let getPart n' = getBuffered bodyBuffer n' Nothing $ getBodyChunk r
    bodyHead <- getPart n
    let bodySize = fromMaybe 0 $ getBodySize r
        -- TODO check bodySize once it is set
        bodyPart = if B.length bodyHead == n then Just getPart else Nothing
    pure HTTP2Body {bodyHead, bodySize, bodyPart, bodyBuffer}
  
  httpALPN :: [ALPN]
  httpALPN = ["h2", "http/1.1"]
  
  httpALPN11 :: ALPN
  httpALPN11 = "http/1.1"
  