{- This file is part of irc-fun-bot.
 -
 - 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/>.
 -}

module Network.IRC.Fun.Bot.Internal.Event
    ( matchEvent
    , handleEvent
    )
where

import Data.Char (isSpace)
import Data.List (find, isPrefixOf)
import Network.IRC.Fun.Bot.Internal.Chat (pong)
import Network.IRC.Fun.Bot.Internal.Failure (defaultRespondToChan)
import Network.IRC.Fun.Bot.Internal.State (askBehavior, askBehaviorS)
import Network.IRC.Fun.Bot.Internal.Types
import Network.IRC.Fun.Bot.Behavior (findCmd)
import qualified Network.IRC.Fun.Client.Events as C (Event (..))
import Network.IRC.Fun.Client.IO (nick)
import Network.IRC.Fun.Client.Util (mentions)

matchEvent :: C.Event -> Config -> [CommandSet e s] -> Event
matchEvent event conf csets =
    case event of
        C.Ping server1 server2      -> Ping server1 server2
        C.Kick channel nicks reason -> Kick channel nicks reason
        C.Join channel nick         -> Join channel nick
        C.Part channel nick reason  -> Part channel nick reason
        C.Quit nick reason          -> Quit nick reason
        C.Mode                      -> other
        C.ChannelMessage channel nick msg False
            | not (null msg) && head msg `elem` map prefix csets ->
                let w = words $ tail msg
                in  BotCommand channel nick (head msg)
                        (if null w then "" else head w)
                        (if null w then [] else tail w)
            | bnick `isPrefixOf` msg                             ->
                let msg' = drop (length bnick) msg
                    msg'' =
                        if null msg'
                            then []
                            else dropWhile isSpace $ tail msg'
                in  if null msg' || head msg' `elem` ",:"
                        then BotMessage channel nick msg''
                        else Message channel nick msg $ msg `mentions` bnick
            | otherwise                                          ->
                Message channel nick msg $ msg `mentions` bnick
        C.ChannelMessage channel nick msg True ->
            Notice (Just channel) nick msg
        C.PrivateMessage nick msg False        -> PersonalMessage nick msg
        C.PrivateMessage nick msg True         -> Notice Nothing nick msg
        C.Topic channel nick topic             ->
            TopicChange channel nick topic
        C.Invite channel nick                  -> other
        C.Names priv chan pnicks               -> Names chan priv pnicks
        C.OtherEvent s                         -> other
    where
    bnick = nick (connection conf)
    other = OtherEvent $ show event

-- Run the command with the given prefix character, command name and list of
-- parameters. If a command with the given prefix and name isn't found, the bot
-- sends a default friendly response.
runCommand :: Char     -- Command prefix
           -> String   -- Command name
           -> [String] -- List of parameters
           -> String   -- Channel in which the command was triggered
           -> String   -- Nickname of user who triggered the command
           -> Session e s ()
runCommand cpref cname cparams channel sender = do
    csets <- askBehaviorS commandSets
    case findCmd cpref cname csets of
        Nothing          ->
            defaultRespondToChan channel cpref cname Nothing
        Just (Left cset) ->
            defaultRespondToChan channel cpref cname (Just cset)
        Just (Right cmd) ->
            respond cmd channel sender cparams

-- | React to an IRC event.
handleEvent :: Event -> Session e s ()
handleEvent event = do
    b <- askBehavior
    case event of
        Ping s1 s2                               -> pong s1 s2
        Kick chan users why                      -> return ()
        Join chan nick                           -> handleJoin b chan nick
        Part chan nick why                       -> handlePart b chan nick why
        Quit nick why                            -> handleQuit b nick why
        Message chan sender msg mentioned        ->
            handleMsg b chan sender msg mentioned
        Notice mchan sender msg                  -> return ()
        BotMessage chan sender msg               ->
            handleBotMsg b chan sender msg
        BotCommand chan sender cpref cname cargs ->
            runCommand cpref cname cargs chan sender
        PersonalMessage sender msg               ->
            handlePersonalMsg b sender msg
        TopicChange chan nick topic              ->
            handleTopicChange b chan nick topic
        Names chan priv pnicks                   ->
            handleNames b chan priv pnicks
        OtherEvent s                             -> return ()