{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell   #-}

{- |
Module      :  ELynx.Tools.Logger
Description :  Monad logger utility functions
Copyright   :  (c) Dominik Schrempf 2019
License     :  GPL-3

Maintainer  :  dominik.schrempf@gmail.com
Stability   :  unstable
Portability :  portable

Creation date: Fri Sep  6 14:43:19 2019.

-}

module ELynx.Tools.Logger
  ( logNewSection
  , eLynxWrapper
  ) where

import           Control.Exception.Lifted
import           Control.Monad.Base
import           Control.Monad.IO.Class
import           Control.Monad.Logger
import           Control.Monad.Trans.Control
import qualified Data.ByteString.Char8       as B
import           Data.Text
-- import qualified Data.Text                   as T
import           System.IO
import           System.Log.FastLogger

import           ELynx.Tools.Options

-- | Unified way of creating a new section in the log.
logNewSection :: MonadLogger m => Text -> m ()
logNewSection s = do
  $(logInfo) ""
  $(logInfo) $ "-- " <> s

-- | The 'LoggingT' wrapper for ELynx. Prints a header and a footer, logs to
-- 'stderr' if no file is provided. If a log file is provided, log to the file
-- and to 'stderr'.
eLynxWrapper :: (MonadBaseControl IO m, MonadIO m)
             => LogLevel -> Maybe FilePath -> String -> LoggingT m () -> m ()
eLynxWrapper lvl logFile headerMsg worker = runELynxLoggingT lvl logFile $
  do
    h <- liftIO $ logHeader headerMsg
    $(logInfo) $ pack h
    worker
    f <- liftIO logFooter
    $(logInfo) $ pack f

runELynxLoggingT :: (MonadBaseControl IO m, MonadIO m)
                 => LogLevel -> Maybe FilePath -> LoggingT m a -> m a
runELynxLoggingT lvl f = case f of
  Nothing -> runELynxStderrLoggingT . filterLogger (\_ l -> l >= lvl)
  Just fn -> runELynxFileLoggingT fn . filterLogger (\_ l -> l >= lvl)

runELynxFileLoggingT :: MonadBaseControl IO m => FilePath -> LoggingT m a -> m a
runELynxFileLoggingT fp logger = bracket
    (liftBase $ openFile fp AppendMode)
    (liftBase . hClose)
    $ \h -> liftBase (hSetBuffering h LineBuffering) >> runLoggingT logger (output2H stderr h)

runELynxStderrLoggingT :: MonadIO m => LoggingT m a -> m a
runELynxStderrLoggingT = (`runLoggingT` output stderr)

output :: Handle
       -> Loc
       -> LogSource
       -> LogLevel
       -> LogStr
       -> IO ()
output h loc src lvl msg =
  B.hPutStr h ls
  where
    ls = logStrToBS loc src lvl msg

output2H :: Handle
         -> Handle
         -> Loc
         -> LogSource
         -> LogLevel
         -> LogStr
         -> IO ()
output2H h1 h2 loc src lvl msg = do
  B.hPutStr h1 ls
  B.hPutStr h2 ls
  where
    ls = logStrToBS loc src lvl msg

logStrToBS :: Loc
           -> LogSource
           -> LogLevel
           -> LogStr
           -> B.ByteString
-- logStrToBS loc src lvl msg =
--   fromLogStr $ getLogStr loc src lvl msg
logStrToBS _ _ _ msg = fromLogStr msg <> "\n"

-- getLogStr :: Loc
--           -> LogSource
--           -> LogLevel
--           -> LogStr
--           -> LogStr
-- getLogStr _ src level msg =
--     "[" `mappend` logLevelStr level `mappend`
--     (if T.null src
--         then mempty
--         else "#" `mappend` toLogStr src) `mappend`
--     "] " `mappend`
--     msg `mappend` "\n"

-- logLevelStr :: LogLevel -> LogStr
-- logLevelStr level = case level of
--     LevelOther t -> toLogStr t
--     _            -> toLogStr $ B.pack $ drop 5 $ show level