# IrcAdmin - handle normal IRC functions one would expect
# Copyright (C) 2010  Brian S. Stephan
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from ConfigParser import NoOptionError
import signal
import sys

from extlib import irclib

from Module import Module

# All kinds of miscellaneous irc stuff

class IrcAdmin(Module):

    def register_handlers(self, server):
        server.add_global_handler('welcome', self.on_connect)
        server.add_global_handler('pubmsg', self.on_pubmsg)
        server.add_global_handler('privmsg', self.on_privmsg)

        # we define save, so we'll bite the bullet and take SIGINT
        signal.signal(signal.SIGINT, self.sigint_handler)

    def unregister_handlers(self):
        self.server.remove_global_handler('welcome', self.on_connect)
        self.server.remove_global_handler('pubmsg', self.on_pubmsg)
        self.server.remove_global_handler('privmsg', self.on_privmsg)

    def on_connect(self, connection, event):
        """handler for when the bot has connected to IRC
        """

        # user modes
        try:
            nick = self.config.get('dr.botzo', 'nick')
            usermode = self.config.get('dr.botzo', 'usermode')
            connection.mode(nick, usermode)
        except NoOptionError: pass

        # join the specified channels
        try:
            autojoins = self.config.get(self.__class__.__name__, 'autojoin').split(',')
            for channel in autojoins:
                if irclib.is_channel(channel):
                    connection.join(channel)
        except NoOptionError: pass

    def do(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        """Try all the admin methods."""

        # TODO: sophisticate. also, document all of these

        whats = what.split(' ')

        if whats[0] == 'join' and admin_unlocked and len(whats) >= 2:
            return self.sub_join_channel(connection, event, nick, userhost, replypath, what, admin_unlocked)
        elif whats[0] == 'part' and admin_unlocked and len(whats) >= 2:
            return self.sub_part_channel(connection, event, nick, userhost, replypath, what, admin_unlocked)
        elif whats[0] == 'quit' and admin_unlocked:
            return self.sub_quit_irc(connection, event, nick, userhost, replypath, what, admin_unlocked)
        elif whats[0] == 'autojoin' and admin_unlocked and len(whats) >= 3:
            return self.sub_autojoin_manipulate(connection, event, nick, userhost, replypath, what, admin_unlocked)
        elif whats[0] == 'config' and whats[1] == 'save' and admin_unlocked:
            return self.sub_save_config(connection, event, nick, userhost, replypath, what, admin_unlocked)
        elif whats[0] == 'nick' and admin_unlocked and len(whats) >= 2:
            return self.sub_change_nick(connection, event, nick, userhost, replypath, what, admin_unlocked)
        elif whats[0] == 'load' and admin_unlocked and len(whats) >= 2:
            return self.sub_load_module(connection, event, nick, userhost, replypath, what, admin_unlocked)
        elif whats[0] == 'reload' and admin_unlocked and len(whats) >= 2:
            return self.sub_reload_module(connection, event, nick, userhost, replypath, what, admin_unlocked)
        elif whats[0] == 'unload' and admin_unlocked and len(whats) >= 2:
            return self.sub_unload_module(connection, event, nick, userhost, replypath, what, admin_unlocked)

    def sub_join_channel(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        whats = what.split(' ')

        channel = whats[1]
        if irclib.is_channel(channel):
            connection.join(channel)
            replystr = 'Joined ' + channel + '.'
            return self.reply(connection, replypath, replystr)

    def sub_part_channel(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        whats = what.split(' ')

        channel = whats[1]
        if irclib.is_channel(channel):
            connection.part(channel, ' '.join(whats[2:]))
            replystr = 'Parted ' + channel + '.'
            return self.reply(connection, replypath, replystr)

    def sub_quit_irc(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        whats = what.split(' ')

        if replypath is not None:
            connection.privmsg(replypath, 'Quitting...')
        connection.quit(' '.join(whats[1:]))
        self.save_config()
        sys.exit()

    def sub_autojoin_manipulate(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        whats = what.split(' ')

        if whats[1] == 'add':
            try:
                # get existing list
                channel = whats[2]
                if irclib.is_channel(channel):
                    channelset = set(self.config.get(self.__class__.__name__, 'autojoin').split(','))
                    channelset.add(channel)
                    self.config.set(self.__class__.__name__, 'autojoin', ','.join(channelset))
                    replystr = 'Added ' + channel + ' to autojoin.'
                    return self.reply(connection, replypath, replystr)
            except NoOptionError: pass
        elif whats[1] == 'remove':
            try:
                # get existing list
                channel = whats[2]
                if irclib.is_channel(channel):
                    channelset = set(self.config.get(self.__class__.__name__, 'autojoin').split(','))
                    channelset.discard(channel)
                    self.config.set(self.__class__.__name__, 'autojoin', ','.join(channelset))
                    replystr = 'Removed ' + channel + ' from autojoin.'
                    return self.reply(connection, replypath, replystr)
            except NoOptionError: pass

    def sub_save_config(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        with open('dr.botzo.cfg', 'w') as cfg:
            self.config.write(cfg)
        replystr = 'Saved config file.'
        return self.reply(connection, replypath, replystr)

    def sub_change_nick(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        whats = what.split(' ')

        newnick = whats[1]
        connection.nick(newnick)
        self.config.set('dr.botzo', 'nick', newnick)
        replystr = 'changed nickname'
        return self.reply(connection, replypath, replystr)

    def sub_load_module(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        """
        Load a module (in both the python and dr.botzo sense) if not already loaded.
        """

        whats = what.split(' ')

        modname = whats[1]
        for module in self.modlist:
            if modname == module.__class__.__name__:
                return self.reply(connection, replypath, 'Module ' + modname + ' is already loaded.')

        # not loaded, let's get to work
        try:
            modstr = 'modules.'+modname
            __import__(modstr)
            module = sys.modules[modstr]
            botmod = eval('module.' + modname + '(self.config, self.server, self.modlist)')
            botmod.register_handlers(self.server)

            # might as well add it to the list
            modset = set(self.config.get('dr.botzo', 'module_list').split(','))
            modset.add(modname)
            self.config.set('dr.botzo', 'module_list', ','.join(modset))

            return self.reply(connection, replypath, 'Module ' + modname + ' loaded.')
        except ImportError:
            return self.reply(connection, replypath, 'Module ' + modname + ' not found.')

    def sub_reload_module(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        """
        Attempt to reload a module, by seeing if it's loaded and, if so, calling its reload()
        """

        whats = what.split(' ')

        modname = whats[1]
        for module in self.modlist:
            if modname == module.__class__.__name__:
                module.reload()
                return self.reply(connection, replypath, modname + ' reloaded.')

    def sub_unload_module(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
        """
        Attempt to unload and del a module if it's loaded.
        """

        whats = what.split(' ')

        modname = whats[1]
        for module in self.modlist:
            if modname == module.__class__.__name__:
                # remove module references
                self.modlist.remove(module)
                module.unregister_handlers()

                # del it
                del module
                return self.reply(connection, replypath, modname + ' unloaded.')

        # guess it was never loaded
        return self.reply(connection, replypath, modname + ' is not loaded.')

    # Save the config file.
    def save_config(self):
        with open('dr.botzo.cfg', 'w') as cfg:
            self.config.write(cfg)

    # SIGINT signal handler
    def sigint_handler(self, signal, frame):
        self.save_config()
        print('saved config')
        sys.exit()

# vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on;