{-# LANGUAGE
    FlexibleContexts
  #-}

-- |
-- Module      : Network.Wai.Trans
-- Copyright   : (c) 2015, 2016, 2017, 2018 Athan Clark
-- License     : BSD-style
-- Maintainer  : athan.clark@gmail.com
-- Stability   : experimental
-- Portability : GHC
--
-- Simple utilities for embedding a monad transformer stack in an 'Network.WebSockets.ClientApp'
-- or 'Network.WebSockets.ServerApp'.

module Network.WebSockets.Trans where

import Network.WebSockets (ConnectionOptions, ClientApp, ServerApp, Connection, PendingConnection)
import Network.Wai.Handler.WebSockets (websocketsOr)
import Network.Wai.Trans (MiddlewareT, runApplicationT, liftApplication)
import Data.Singleton.Class (Extractable (runSingleton))
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.Trans.Control.Aligned (MonadBaseControl (liftBaseWith))


-- * Websockets

type ServerAppT m = PendingConnection -> m ()

liftServerApp :: MonadIO m
              => ServerApp -- ^ To lift
              -> ServerAppT m
liftServerApp s = liftIO . s

runServerAppT :: MonadBaseControl IO m stM
              => Extractable stM
              => ServerAppT m -- ^ To run
              -> m ServerApp
runServerAppT s = liftBaseWith $ \runInBase ->
  pure $ \pending -> runSingleton <$> runInBase (s pending)

type ClientAppT m a = Connection -> m a

liftClientApp :: MonadIO m
              => ClientApp a -- ^ To lift
              -> ClientAppT m a
liftClientApp c = liftIO . c

runClientAppT :: MonadBaseControl IO m stM
              => Extractable stM
              => ClientAppT m a -- ^ To run
              -> m (ClientApp a)
runClientAppT c = liftBaseWith $ \runInBase ->
  pure $ \conn -> runSingleton <$> runInBase (c conn)


-- * WAI Compatability

-- | Respond with the WebSocket server when applicable, as a middleware
websocketsOrT :: MonadBaseControl IO m stM
              => Extractable stM
              => ConnectionOptions
              -> ServerAppT m -- ^ Server
              -> MiddlewareT m
websocketsOrT cOpts server app req respond = do
  server' <- runServerAppT server
  app' <- runApplicationT app
  liftApplication (websocketsOr cOpts server' app') req respond