module System.Log.Handler.Simple(streamHandler, fileHandler,
                                      GenericHandler (..),
                                      verboseStreamHandler)
    where
import Control.Exception (tryJust)
import Control.DeepSeq
import Data.Char (ord)
import System.Log
import System.Log.Handler
import System.Log.Formatter
import System.IO
import System.IO.Error
import Control.Concurrent.MVar
data GenericHandler a = GenericHandler {priority :: Priority,
                                        formatter :: LogFormatter (GenericHandler a),
                                        privData :: a,
                                        writeFunc :: a -> String -> IO (),
                                        closeFunc :: a -> IO () }
instance LogHandler (GenericHandler a) where
    setLevel sh p = sh{priority = p}
    getLevel sh = priority sh
    setFormatter sh f = sh{formatter = f}
    getFormatter sh = formatter sh
    emit sh (_,msg) _ = (writeFunc sh) (privData sh) msg
    close sh = (closeFunc sh) (privData sh)
streamHandler :: Handle -> Priority -> IO (GenericHandler Handle)
streamHandler h pri =
    do lock <- newMVar ()
       let mywritefunc hdl msg =
               msg `deepseq`
               withMVar lock (\_ -> do writeToHandle hdl msg
                                       hFlush hdl
                             )
       return (GenericHandler {priority = pri,
                               formatter = nullFormatter,
                               privData = h,
                               writeFunc = mywritefunc,
                               closeFunc = \_ -> return ()})
    where
      writeToHandle hdl msg = do
          rv <- tryJust myException (hPutStrLn hdl msg)
          either (handleWriteException hdl msg) return rv
      myException e
          | isDoesNotExistError e = Just e
          | otherwise = Nothing
      handleWriteException hdl msg e =
          let msg' = "Error writing log message: " ++ show e ++
                     " (original message: " ++ msg ++ ")"
          in hPutStrLn hdl (encodingSave msg')
      encodingSave = concatMap (\c -> if ord c > 127
                                         then "\\" ++ show (ord c)
                                         else [c])
fileHandler :: FilePath -> Priority -> IO (GenericHandler Handle)
fileHandler fp pri = do
                     h <- openFile fp AppendMode
                     sh <- streamHandler h pri
                     return (sh{closeFunc = hClose})
verboseStreamHandler :: Handle -> Priority -> IO (GenericHandler Handle)
verboseStreamHandler h pri = let fmt = simpleLogFormatter "[$loggername/$prio] $msg"
                             in do hndlr <- streamHandler h pri
                                   return $ setFormatter hndlr fmt