module System.Logging.LogSink.Core (
  Format
, defaultFormat
, stdErrSink
, sysLogSink
, combine
, filterByLogLevel
) where

import           Prelude ()
import           System.Logging.LogSink.Compat

import           Control.Concurrent.MVar
import           Control.Monad
import           System.IO
import           System.IO.Unsafe
import           System.Logging.Facade.Sink
import           System.Logging.Facade.Types
import           System.Posix.Syslog

import           System.Logging.LogSink.Format
import           System.Logging.LogSink.Internal
import           Foreign.C.String

-- | Default format function that formats log records like so:
-- > {level}: {message}
defaultFormat :: Format
defaultFormat =
  let Right format = parseFormat defaultFormatString
  in format

{-# NOINLINE stderrLock #-}
stderrLock :: MVar ()
stderrLock = unsafePerformIO $ newMVar ()

stdErrSink :: Format -> LogSink
stdErrSink format record = do
  s <- format record
  modifyMVar_ stderrLock $ \ () -> hPutStrLn stderr s

sysLogSink :: Format -> LogSink
sysLogSink format record = do
  str <- format record
  withCStringLen str (syslog Nothing (toPriority $ logRecordLevel record))
  where
    toPriority :: LogLevel -> Priority
    toPriority l = case l of
      TRACE -> Debug
      DEBUG -> Debug
      INFO -> Info
      WARN -> Warning
      ERROR -> Error

combine :: [LogSink] -> LogSink
combine sinks record = do
  forM_ sinks $ \sink -> sink record

filterByLogLevel :: LogLevel -> LogSink -> LogSink
filterByLogLevel level sink
  | level == minBound = sink
  | otherwise = filteringSink
  where
    filteringSink record
      | logRecordLevel record < level = return ()
      | otherwise = sink record
