{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

module Logging.Global.Internal
  ( log
  , run
  ) where


import           Control.Exception
import           Control.Monad
import           Control.Monad.IO.Class
import           Data.IORef
import           Prelude                hiding (log)
import           System.IO.Unsafe

import           Logging.Level
import           Logging.Logger
import           Logging.Manager
import qualified Logging.Monad.Internal as M

{-# NOINLINE ref #-}
ref :: IORef Manager
ref = unsafePerformIO $ newIORef $
  error "Global logging manager is not set, see Logging.Global.run"


log :: MonadIO m
     => Logger -> Level -> String -> (String, String, String, Int)
     -> m ()
log logger level message location = liftIO $ do
  manager <- readIORef ref
  M.runLoggingT (M.log logger level message location) manager

{-| Run the global logging environment.

You should always run you application in the global logging environment.

  @
    main :: IO ()
    main = run manager app

    app :: IO ()
    app = do
      $(info) \"App\" "..."
      ...
  @

Never run multiple global logging environments concurrently.

  @
    -- bad useage
    main :: IO ()
    main = do
      forkIO $ run manager1 app1
      forkIO $ run manager2 app2
      ...

    -- correct useage
    main :: IO ()
    main = run manager $ do
      forkIO app1
      forkIO app2
      ...
  @
-}
run :: Manager -> IO a -> IO a
run manager@Manager{..} io = do
    bracket_ (initialize manager >> atomicWriteIORef ref manager)
             (terminate manager)
             (runInner catchUncaughtException io)
  where
    unknownLoc = ("unknown file", "unknown package", "unknown module", 0)

    runInner :: Bool -> IO a -> IO a
    runInner False io = io
    runInner True io  = catch io $ \e -> do
      log "" "ERROR" (show (e :: SomeException)) unknownLoc
      runInner True io