--
-- | The IRC module processes the IRC protocol and provides a nice API for sending
--   and receiving IRC messages with an IRC server.
--
module Lambdabot.IRC
    ( IrcMessage(..)
    , joinChannel
    , partChannel
    , getTopic
    , setTopic
    , codepage
    , privmsg
    , quit
    , timeReply
    , pass
    , user
    , setNick
    ) where

import Lambdabot.Message
import Lambdabot.Nick

import Data.Char (chr,isSpace)
import Data.List.Split

import Control.Monad (liftM2)

-- | An IRC message is a server, a prefix, a command and a list of parameters.
--
-- Note that the strings here are treated as lists of bytes!
data IrcMessage
  = IrcMessage {
        ircMsgServer   :: !String,
        ircMsgLBName   :: !String,
        ircMsgPrefix   :: !String,
        ircMsgCommand  :: !String,
        ircMsgParams   :: ![String]
  }
  deriving (Show)

instance Message IrcMessage where
    nick                = liftM2 Nick ircMsgServer (takeWhile (/= '!') . ircMsgPrefix)
    server              = ircMsgServer
    fullName            = dropWhile (/= '!') . ircMsgPrefix
    channels msg        =
      let cstr = head $ ircMsgParams msg
        in map (Nick (server msg)) $
           map (\(x:xs) -> if x == ':' then xs else x:xs) (splitOn "," cstr)
               -- solves what seems to be an inconsistency in the parser
    lambdabotName msg   = Nick (server msg) (ircMsgLBName msg)

-- | 'mkMessage' creates a new message from a server, a cmd, and a list of parameters.
mkMessage :: String -- ^ Server
          -> String -- ^ Command
          -> [String] -- ^ Parameters
          -> IrcMessage -- ^ Returns: The created message

mkMessage svr cmd params = IrcMessage
    { ircMsgServer = svr
    , ircMsgPrefix = ""
    , ircMsgCommand = cmd
    , ircMsgParams = params
    , ircMsgLBName = "urk!<outputmessage>"
    }

joinChannel :: Nick -> IrcMessage
joinChannel loc     = mkMessage (nTag loc)  "JOIN"  [nName loc]

partChannel :: Nick -> IrcMessage
partChannel loc     = mkMessage (nTag loc)  "PART"  [nName loc]

getTopic :: Nick -> IrcMessage
getTopic chan       = mkMessage (nTag chan) "TOPIC" [nName chan]

setTopic :: Nick -> String -> IrcMessage
setTopic chan topic = mkMessage (nTag chan) "TOPIC" [nName chan, ':' : topic]

-- | 'privmsg' creates a private message to the person designated.
privmsg :: Nick -- ^ Who should receive the message (nick)
        -> String -- ^ What is the message?
        -> IrcMessage -- ^ Constructed message
privmsg who msg = if action then mk [nName who, ':':(chr 0x1):("ACTION " ++ clean_msg ++ ((chr 0x1):[]))]
                            else mk [nName who, ':' : clean_msg]
    where mk = mkMessage (nTag who) "PRIVMSG"
          cleaned_msg = case filter (/= '\CR') msg of
              str@('@':_) -> ' ':str
              str         -> str
          (clean_msg,action) = case cleaned_msg of
              ('/':'m':'e':r) -> (dropWhile isSpace r,True)
              str             -> (str,False)

-- | 'codepage' creates a server CODEPAGE message. The input string given is the
--   codepage name for current session.
codepage :: String -> String -> IrcMessage
codepage svr codepage = mkMessage svr "CODEPAGE" [' ':codepage]

-- | 'quit' creates a server QUIT message. The input string given is the
--   quit message, given to other parties when leaving the network.
quit :: String -> String -> IrcMessage
quit svr msg = mkMessage svr "QUIT" [':' : msg]

-- | Construct a privmsg from the CTCP TIME notice, to feed up to
-- the @localtime-reply plugin, which then passes the output to
-- the appropriate client.
timeReply :: IrcMessage -> IrcMessage
timeReply msg = msg
    { ircMsgCommand = "PRIVMSG"
    , ircMsgParams  = [head (ircMsgParams msg)
                   ,":@localtime-reply " ++ (nName $ nick msg) ++ ":" ++
                      (init $ drop 7 (last (ircMsgParams msg))) ]
    }

user :: String -> String -> String -> String -> IrcMessage
user svr nick_ server_ ircname = mkMessage svr "USER" [nick_, "localhost", server_, ircname]

pass :: String -> String -> IrcMessage
pass svr pwd = mkMessage svr "PASS" [pwd]

setNick :: Nick -> IrcMessage
setNick nick_ = mkMessage (nTag nick_) "NICK" [nName nick_]