content: {-# LANGUAGE NamedFieldPuns    #-}
  
  {-# LANGUAGE NoImplicitPrelude #-}
  module Control.Concurrent.Capataz.Util (
      buildLogWorkerSpec1
    , buildLogWorkerSpec
    , buildLogWorkerOptions1
    , buildLogWorkerOptions
    ) where
  
  import Control.Concurrent.Capataz
      ( ProcessSpec
      , WorkerName
      , WorkerOptions
      , WorkerRestartStrategy (Permanent)
      , buildWorkerOptions
      , workerRestartStrategyL
      , workerSpec
      )
  import RIO
  
  data LogMsg
    = LogMsg
    {
      lmCallStack :: !CallStack
    , lmLogSource :: !LogSource
    , lmLogLevel  :: !LogLevel
    , lmPayload   :: !Utf8Builder
    }
  
  
  runLoggerThread :: MonadUnliftIO m => LogOptions -> TBQueue LogMsg -> m a
  runLoggerThread logOptions inputQueue = withLogFunc logOptions $ \logFunc ->
    runRIO logFunc $ forever $ do
      logMsg <- atomically $ readTBQueue inputQueue
      let LogMsg { lmLogSource, lmLogLevel, lmPayload } = logMsg
      -- TODO: create a ticket for a logGenericWithStack function in rio
      logGeneric lmLogSource lmLogLevel lmPayload
  
  -- | Builds a 'ProcessSpec' that spawns a thread that logs messages written with
  -- the returned 'LogFunc'. Use this function when your want your logger to be
  -- part of a static supervision tree.
  --
  --
  -- __IMPORTANT__ If you use the returned 'LogFunc' to log functions and the
  -- 'ProcessSpec' is not used in a supervision tree, your logging won't work and
  -- your application will eventually block the current thread when logging.
  --
  -- A minimal example:
  --
  -- @
  -- {-# LANGUAGE OverloadedStrings #-}
  -- {-# LANGUAGE NoImplicitPrelude #-}
  -- import RIO
  -- import Capataz
  --
  -- main :: IO ()
  -- main = do
  --   logOptions <- logOptionsHandle stdout True
  --   (loggerSpec, logFunc) <- buildLogWorkerSpec logOptions "app-logger" 100 id
  --   runRIO logFunc $ do
  --     bracket (forkCapataz "application" (set supervisorProcessSpecListL [loggerSpec]))
  --             terminateCapataz_ $ \_capataz -> do
  --       logInfo "this log message is written by a dedicated supervised thread"
  --       threadDelay 1000100
  -- @
  --
  -- @since 0.2.1.0
  buildLogWorkerSpec1
    :: (MonadUnliftIO m, MonadIO m0)
    => LogOptions  -- ^ options for the 'LogFunc' instance
    -> WorkerName  -- ^ name of the logger worker process
    -> Natural     -- ^ how many log messages can be in-flight when writer is slow?
    -> (WorkerOptions m -> WorkerOptions m) -- ^ worker process modifier
    -> m0 (ProcessSpec m, LogFunc)
  buildLogWorkerSpec1 logOptions procName bufferSize modOptions = do
    inputQueue <- newTBQueueIO bufferSize
    let myLogFunc =
          mkLogFunc $ \lmCallStack lmLogSource lmLogLevel lmPayload -> atomically
            (writeTBQueue
              inputQueue
              LogMsg {lmCallStack , lmLogSource , lmLogLevel , lmPayload }
            )
  
        loggerSpec = workerSpec
          procName
          (runLoggerThread logOptions inputQueue)
          (modOptions . set workerRestartStrategyL Permanent)
  
    return (loggerSpec, myLogFunc)
  
  -- | Deprecated in favour of 'buildLogWorkerSpec1'.
  --
  -- __IMPORTANT__ Since 0.2.1.0 this function throws a runtime error if
  -- the argumet of the type 'Int' is negative.
  --
  -- @since 0.2.0.0
  buildLogWorkerSpec
    :: (MonadUnliftIO m, MonadIO m0)
    => LogOptions  -- ^ options for the 'LogFunc' instance
    -> WorkerName  -- ^ name of the logger worker process
    -> Int         -- ^ how many log messages can be in-flight when writer is slow?
    -> (WorkerOptions m -> WorkerOptions m) -- ^ worker process modifier
    -> m0 (ProcessSpec m, LogFunc)
  buildLogWorkerSpec logOptions procName bufferSize =
    buildLogWorkerSpec1 logOptions procName (fromIntegral bufferSize)
  {-# DEPRECATED buildLogWorkerSpec "Use buildLogWorkerSpec1 instead" #-}
  
  -- | Builds a 'WorkerOptions' record that spawns a thread that logs messages
  -- written with the returned 'LogFunc'. Use this function if you want to build a
  -- logger thread dynamically via 'forkWorker'.
  --
  --
  -- __IMPORTANT__ If you use the returned 'LogFunc' to log functions and the
  -- 'WorkerOptions' is not used in a 'forkWorker' call, your logging won't work
  -- and your application will eventually block the current thread when logging.
  --
  -- A minimal example:
  --
  -- @
  -- {-# LANGUAGE OverloadedStrings #-}
  -- {-# LANGUAGE NoImplicitPrelude #-}
  -- import RIO
  -- import Capataz
  --
  -- main :: IO ()
  -- main = do
  --   logOptions <- logOptionsHandle stdout True
  --   (loggerOptions, logFunc) <- buildLogWorkerOptions logOptions "app-logger" 100 id
  --   runRIO logFunc $ do
  --     bracket (forkCapataz "application" id)
  --             terminateCapataz_ $ \capataz -> do
  --       _workerId <- forkWorker loggerOptions capataz
  --       logInfo "this log message is written by a dedicated supervised thread"
  --       threadDelay 1000100
  -- @
  --
  -- @since 0.2.1.0
  buildLogWorkerOptions1
    :: (MonadUnliftIO m, MonadIO m0)
    => LogOptions
    -> WorkerName
    -> Natural
    -> (WorkerOptions m -> WorkerOptions m)
    -> m0 (WorkerOptions m, LogFunc)
  buildLogWorkerOptions1 logOptions procName bufferSize modOptions = do
    inputQueue <- newTBQueueIO bufferSize
    let myLogFunc =
          mkLogFunc $ \lmCallStack lmLogSource lmLogLevel lmPayload -> atomically
            (writeTBQueue
              inputQueue
              LogMsg {lmCallStack , lmLogSource , lmLogLevel , lmPayload }
            )
  
        loggerSpec = buildWorkerOptions
          procName
          (runLoggerThread logOptions inputQueue)
          (modOptions . set workerRestartStrategyL Permanent)
  
    return (loggerSpec, myLogFunc)
  
  -- | Deprecated in favour of 'buildLogWorkerOptions1'.
  --
  -- __IMPORTANT__ Since 0.2.1.0 this function throws a runtime error if
  -- the argumet of the type 'Int' is negative.
  --
  -- @since 0.2.0.0
  buildLogWorkerOptions
    :: (MonadUnliftIO m, MonadIO m0)
    => LogOptions
    -> WorkerName
    -> Int
    -> (WorkerOptions m -> WorkerOptions m)
    -> m0 (WorkerOptions m, LogFunc)
  buildLogWorkerOptions logOptions procName bufferSize =
    buildLogWorkerOptions1 logOptions procName (fromIntegral bufferSize)
  {-# DEPRECATED buildLogWorkerOptions "Use buildLogWorkerOptions1 instead" #-}
  