{- This file is part of irc-fun-client.
 -
 - Written in 2015 by fr33domlover <fr33domlover@rel4tion.org>.
 -
 - ♡ Copying is an act of love. Please copy, reuse and share.
 -
 - The author(s) have dedicated all copyright and related and neighboring
 - rights to this software to the public domain worldwide. This software is
 - distributed without any warranty.
 -
 - You should have received a copy of the CC0 Public Domain Dedication along
 - with this software. If not, see
 - <http://creativecommons.org/publicdomain/zero/1.0/>.
 -}

{-# LANGUAGE OverloadedStrings #-}

module Network.IRC.Fun.Client.ChannelLogger
    ( Logger ()
    , ChanLogEvent (..)
    , LogEvent (..)
    , logFilePath
    , newLogger
    , removeLogger
    , logEvent
    , fromClientEvent
    )
where

import Control.Monad (liftM)
import Data.Monoid ((<>))
import Network.IRC.Fun.Client.Events (Event (..))
import Network.IRC.Fun.Messages.TypeAliases (NickName, ChannelName)
import System.Log.FastLogger

data Logger = Logger
    { loggerSet     :: LoggerSet
    , loggerGetTime :: IO String
    }

data ChanLogEvent
    = EnterChan NickName
    | LeaveChan NickName
    | MessageChan NickName String
    | RenameInChan NickName NickName
    deriving Show

data LogEvent
    = Enter NickName ChannelName
    | Leave NickName ChannelName
    | LeaveAll NickName
    | Message NickName ChannelName String
    | Rename NickName NickName
    deriving Show

-- | Utility for constructing a log file path.
logFilePath :: FilePath    -- ^ Directory in which to place log files. Relative
                           -- to the program's working directory, or absolute.
            -> String      -- ^ Server label, e.g. @\"freenode\"@
            -> ChannelName -- ^ IRC channel name, e.g. @\"#freepost\"@
            -> FilePath
logFilePath dir server chan = dir ++ "/" ++ server ++ "." ++ chan ++ ".irc"

-- | Create a logger for a given IRC channel.
newLogger :: IO String -- ^ Action which returns a formatted time string. You
                       -- can use "Network.IRC.Fun.Client.Time" to create such
                       -- an action.
          -> FilePath  -- ^ Path of the log file
          -> IO Logger
newLogger getTime path = do
    lset <- newFileLoggerSet defaultBufSize path
    return $ Logger
        { loggerSet     = lset
        , loggerGetTime = getTime
        }

-- | Flush buffers and release resources.
--
-- When the logger is paused for a long period of time (i.e. not momentarily -
-- e.g. by a user disabling channel logging via UI), you can use this to
-- release resources. Later, when logging is needed again, create a fresh new
-- logger.
--
-- If your client joins many channels but log few of them, you can save
-- resources by keeping open loggers only for the few logged channels.
removeLogger :: Logger -> IO ()
removeLogger logger = rmLoggerSet $ loggerSet logger

formatEvent :: ChanLogEvent -> LogStr
formatEvent e = case e of
    EnterChan nick       -> "|-->| " <> toLogStr nick <> " has joined"
    LeaveChan nick       -> "|<--| " <> toLogStr nick <> " has left"
    MessageChan nick msg -> "<" <> toLogStr nick <> "> " <> toLogStr msg
    RenameInChan old new -> "|---| " <> toLogStr old <> " is now known as "
                                     <> toLogStr new

formatLine :: IO String -> ChanLogEvent -> IO LogStr
formatLine getTime event = do
    t <- getTime
    return $ toLogStr t <> " " <> formatEvent event

-- | Write a log message corresponding to a given event.
logEvent :: Logger -> ChanLogEvent -> IO ()
logEvent logger event = do
    line <- formatLine (loggerGetTime logger) event
    pushLogStrLn (loggerSet logger) line

-- | If an IRC client event can be logged, return a matching log event and the
-- channel in which the event has occured.
fromClientEvent :: Event -> Maybe LogEvent
fromClientEvent event = case event of
    Join chan nick                       -> Just $ Enter nick chan
    Part chan nick _reason               -> Just $ Leave nick chan
    Quit nick _reason                    -> Just $ LeaveAll nick
    ChannelMessage chan nick msg _notice -> Just $ Message nick chan msg
    NickChange old new                   -> Just $ Rename old new
    _                                    -> Nothing