-- | Fast start
--
-- Create log config:
--
-- @
--myCfg = logCfg [(\"\", Info), (\"app\", Trace), (\"app.sub\", Debug)]
-- @
--
-- Create log and run log monad
--
-- @
--run ∷ IO ()
--run = runLog myCfg [handler text (file \"out.log\")] $ yourFunction
-- @
--
-- Function within log monad:
--
-- @
--yourFunction ∷ MonadLog m ⇒ m ()
--yourFunction = component \"app\" $ scope \"your\" $ do
--	sendLog Trace \"Hello from your function\"
-- @
--
-- Each component can have different level in config, subcomponents are specified with \'.\'
-- Components have independent scopes
-- Scopes can be nested and separated with \'/\':
--
-- @
--function2 ∷ MonadLog m ⇒ m ()
--function2 = component \"app.sub\" $ scope \"foo\" $ do
--	scope \"bar/baz\" $ do
--		sendLog Info \"Component app.sub and scope foo/bar/baz\"
--		sendLog Trace \"Invisible: app.sub configured with debug level\"
--	sendLog Info \"Same component and scope foo\"
--	component \"module\" $ sendLog Info \"Component module and root scope\"
-- @
--
-- You can update config with @updateLogConfig@ (or @modifyLogConfig@ within log monad) function
-- And change handlers with @updateLogHandlers@ (@modifyLogHandlers@)
-- 
-- There're also global logger @globalLog@, that can be used with @runGlobalLog@
--
-- @
--test ∷ IO ()
--test = do
--	updateLogHandlers globalLog ([handler text (file \"test.log\")]:)
--	runGlobalLog $ do
--		sendLog Info \"This will go to test.log too\"
--		modifyLogConfig (set (ix \"\") Debug)
--		sendLog Debug \"Now debug is logged too\"
-- @
--
module System.Log.Simple (
	module System.Log.Simple.Base,
	module System.Log.Simple.Monad,
	module System.Log.Simple.Text,
	module System.Log.Simple.Stream,
	module System.Log.Simple.File,

	globalLog,
	runGlobalLog, runConsoleLog, runLogMsgs, runLogTexts
	) where

import Prelude.Unicode

import Control.Monad.IO.Class
import Control.Monad.Catch (MonadMask)
import Control.Concurrent
import Data.Maybe
import Data.Text (Text)
import System.IO.Unsafe (unsafePerformIO)

import System.Log.Simple.Base (
	Level(..), Message, Converter, Consumer, LogHandler, handler,
	LogConfig(..), defCfg, logCfg, componentCfg, Log(..),
	newLog, rootLog, getLog, subLog,
	updateLogConfig, updateLogHandlers, writeLog, stopLog)
import System.Log.Simple.Monad (
	MonadLog(..), LogT(..), noLog, withLog, runLog, sendLog,
	component, scope_, scope, scopeM, scoper, scoperM, trace,
	modifyLogConfig, modifyLogHandlers)
import System.Log.Simple.Text
import System.Log.Simple.Stream
import System.Log.Simple.File
import System.Log.Simple.Chan

globalLog ∷ Log
globalLog = unsafePerformIO $ newLog defCfg [handler text coloredConsole]

runGlobalLog ∷ LogT m a → m a
runGlobalLog = withLog globalLog

runConsoleLog ∷ (MonadIO m, MonadMask m) ⇒ LogConfig → LogT m a → m a
runConsoleLog cfg = runLog cfg [handler text coloredConsole]

runLogChan ∷ (MonadIO m, MonadMask m) ⇒ (Chan w → LogHandler) → LogConfig → LogT m a → m (a, [w])
runLogChan c cfg act = do
	ch ← liftIO newChan
	mch ← liftIO newChan
	_ ← liftIO $ forkIO $ getChanContents ch >>= mapM_ (writeChan mch . Just)
	r ← runLog cfg [c ch] act
	liftIO $ writeChan mch Nothing
	msgs ← liftIO ((catMaybes ∘ takeWhile isJust) <$> getChanContents mch)
	return (r, msgs)

runLogMsgs ∷ (MonadIO m, MonadMask m) ⇒ LogConfig → LogT m a → m (a, [Message])
runLogMsgs = runLogChan chan

runLogTexts ∷ (MonadIO m, MonadMask m) ⇒ Converter Text → LogConfig → LogT m a → m (a, [Text])
runLogTexts txt = runLogChan (handler txt ∘ chan)
