import re
from BaseManager import BaseManager
from cwbot.util.textProcessing import stringToBool

class BaseChatManager(BaseManager):
    """
    Base class for any Manager that handles chats. The parseChat() method
    is overridden to handle any chat that is approved in the _chatApplies()
    method.
    """ 
    
    capabilities = ['chat', 'inventory', 'admin']

    def _chatApplies(self, x, checkNum):
        """ Returns True if the manager should process this chat. This
        function should be overridden in derived classes to impliment
        different behavior. By default, the manager processes every public
        chat, and also accepts PMs if its accept_private_messages option
        is set in modules.ini. """
        return (x['type'] in ['normal', 'listen', 'emote'] or 
                (self._respondToWhisper and x['type'] == 'private'))
        
    
    def __init__(self, parent, name, iData, config):
        "Initialize the BaseChatManager"
        self._respondToWhisper = None
        super(BaseChatManager, self).__init__(parent, name, iData, config)
        
        
    def _configure(self, config):
        """Configuration. In addition to the BaseManager config, the
        accept_private_messages option is added for the BaseChatManager. """
        super(BaseChatManager, self)._configure(config)
        try:
            self._respondToWhisper = stringToBool(
                    config.setdefault('accept_private_messages', 'true'))
        except KeyError:
            raise Exception("Invalid option to accept_private_messages "
                            "for manager {}".format(self.identity))
    
    
    def _processChat(self, msg, checkNum):
        """ Every approved chat is passed to the _processChat method. Here
        the chat is passed to individual modules. """
        replies = []
        txt = msg['text']
        # check if this is a command or help query (starts with ? or !) that
        # is of the form "!COMMAND (ARGS)"
        m1 = re.search(r'^(!|\?)([^\s(]+)\s*\((.*)\)', txt)
        cmdtype = []
        cmd = []
        arg = []
        if m1 is not None:
            cmdtype = m1.group(1)
            cmd = m1.group(2).lower()
            arg = m1.group(3)
        else:
            # check for the form "!COMMAND ARGS", discard -hic- if present
            m2 = re.search(r'^(!|\?)([^\s(]+)\s*(.*?)(\s*-hic-)?$', txt)
            if m2 is not None:
                cmdtype = m2.group(1)
                cmd = m2.group(2).lower()
                arg = m2.group(3)
            else:
                cmdtype = None
                cmd = None
                arg = None
        if arg is None:
            arg = "" 
        if cmd == "help" or cmdtype == "?":
            # show "help" text
            replies.extend(self._showHelp(msg, cmd, arg))
        else:
            # execute command
            with self._syncLock:    
                for m in self._modules:
                    mod = m.module
                    permission = m.permission
                    clanOnly = m.clanOnly
                    # ACTUALLY execute the command
                    txt = self._processCommand(mod, permission, clanOnly, msg, 
                                              cmd, arg)
                    if txt is not None:
                        replies.extend(txt.split("\n"))
                        # chat processed! return the replies.
                        break
        return [r for r in replies if r != ""]

    
    def _processCommand(self, module, permission, clanOnly, msg, cmd, arg):
        """ This function is used to send a chat to a module, while first
        checking permissions and in-clan status. """
        
        # get a list of available chat commands
        uid = msg['userId']
        availableCommands = module.extendedCall('available_commands')
        if availableCommands is None:
            availableCommands = {}
        noPermission = (permission is None or 
                        permission == "" or 
                        permission == "None")
        cmdAvailable = (cmd in availableCommands)
        
        # first, perform in-clan check
        if clanOnly:
            # proceed with clan verification
            if not self.checkClan(uid):
                return None
        
        # now, check permissions
        if noPermission:
            # send all chats (even non-commands) to this module because
            # it has no restrictions
            return module.extendedCall('process_command', msg, cmd, arg)
        elif permission in self.properties.getPermissions(uid):
            # the user has permission to execute the command
            if cmdAvailable:
                # do not log this event if the module does not explicitly
                # have this command -- it will probably be ignored!
                self._log.info("Administrator {} ({}) has used permission "
                               "{} to execute command '!{}({})'."
                               .format(msg['userId'], 
                                       msg.get('userName', ""), 
                                       permission, cmd, arg))
            return module.extendedCall('process_command', msg, cmd, arg)
        else:
            # user does not have required permissions
            if cmdAvailable:
                # return a permissions denied message
                self._log.info("User {} ({}) does not have required "
                               "permission {} to execute command '!{}({})'."
                               .format(uid, msg.get('userName', ""), 
                                       permission, cmd, arg))
                return "You do not have permission to use that command."
            else:
                # just skip this module
                return None

    
    def _showHelp(self, msg, cmd, arg):
        """ Return the help text that shows available commands. This is
        done using the available_commands extended module call, which is
        set up for all classes deriving from BaseChatModule. """
        helpText = []
        availableCommands = dict()
        availableAdmin = dict()
        uid = msg['userId']
        availablePermissions = self.properties.getPermissions(uid)

        # "help" accepts two forms: "?COMMAND" and "!help COMMAND". Here
        # the latter is transformed to the former.
        if cmd == "help" and arg != "":
            cmd = arg
        cmd = cmd.lower()
        generalHelp = (cmd == "help")
        
        # get a list of all matching commands
        for m in self._modules:
            mod = m.module
            permission = m.permission
            clanOnly = m.clanOnly
            newCmds = mod.extendedCall('available_commands')
            if newCmds is not None:
                # remove commands with no description (alternates / hidden)
                newCmds = dict((k.lower(),v) for k,v in newCmds.items() 
                               if v is not None)
                if generalHelp or cmd in newCmds:
                    if not clanOnly or self.checkClan(uid):