import json
import threading
import time

from slackclient import SlackClient

from handlers.handler_factory import *
from handlers import *
from util.loghandler import *
from bottypes.invalid_console_command import *

class BotServer(threading.Thread):

    # global lock for locking global data in bot server
    threadLock = threading.Lock()
    userList = []

    def __init__(self):
        log.debug("Parse config file and initialize threading...")

        threading.Thread.__init__(self)

    def lock(self):
        """Acquire global lock for working with global (not thread-safe) data."""
        BotServer.threadLock.acquire()

    def release(self):
        """Release global lock after accessing global (not thread-safe) data."""
        BotServer.threadLock.release()

    def updateUserList(self, slack_client):
        self.lock()

        log.debug("Retrieving user list")

        api_call = slack_client.api_call("users.list")
        if api_call.get('ok'):
            BotServer.userList = api_call.get('members')

        self.release()

    def getUser(self, userName):
        self.lock()

        foundUser = None

        for user in BotServer.userList:
            if 'name' in user and user.get('name') == userName:
                foundUser = user
                break

        self.release()

        return foundUser

    def quit(self):
        log.info("Shutting down")

        self.running = False

    def sendMessage(self, channelID, msg):
        self.slack_client.api_call(
            "chat.PostMessage", channel=channelID, text=msg, as_user=True)

    def load_config(self):
        self.lock()

        with open("./config.json") as f:
            self.config = json.load(f)

        self.release()

    def get_config_option(self, option):
        self.lock()

        result = None

        if option in self.config:
            result = self.config[option]

        self.release()

        return result

    def set_config_option(self, option, value):
        self.lock()

        try:
            if option in self.config:
                self.config[option] = value

                log.info("Updated configuration: {} => {}".format(option, value))

                with open("./config.json", "w") as f:
                    json.dump(self.config, f)
            else:
                raise InvalidConsoleCommand("The specified configuration option doesn't exist: {}".format(option))
        finally:
            self.release()

    def parseSlackMessage(self, slackMessage):
        """
        The Slack Real Time Messaging API is an events firehose.
        this parsing function returns None unless a message is
        directed at the Bot, based on its ID.
        """
        output_list = slackMessage

        if output_list:
            for output in output_list:
                if output and 'text' in output:
                    if self.botAT in output['text']:
                        # return text after the @ mention, whitespace removed
                        return (output['text'].split(self.botAT)[1].strip().lower(), output['channel'], output['user'])
                    elif output['text'] and output['text'].startswith('!'):
                        return (output['text'][1:].strip().lower(), output['channel'], output['user'])

        return None, None, None

    def searchBotUser(self, botName):
        log.debug("Trying to resolve bot user in slack")

        self.updateUserList(self.slack_client)

        self.botName = botName
        botUser = self.getUser(self.botName)

        if botUser:
            self.botID = botUser['id']
            self.botAT = "<@%s>" % self.botID

            log.debug("Found bot user %s (%s)" % (self.botName, self.botID))

            self.running = True
        else:
            log.error("Could not find bot user. Abort...")

            self.running = False

    def run(self):
        log.info("Starting server thread...")

        self.load_config()

        self.slack_client = SlackClient(self.get_config_option('api_key'))

        self.searchBotUser(self.get_config_option('bot_name'))

        if self.botID:
            # 1 second delay between reading from firehose
            READ_WEBSOCKET_DELAY = 1

            if self.slack_client.rtm_connect():
                log.info("Connection successful...")

                log.info("Initializing handlers...")
                # Might even pass the bot server for handlers?
                HandlerFactory.initialize(
                    self.slack_client, self.botID)

                # Main loop
                while self.running:
                    command, channel, user = self.parseSlackMessage(
                        self.slack_client.rtm_read())

                    if command and channel:
                        log.debug("Received bot command : %s (%s)" %
                                  (command, channel))

                        HandlerFactory.process(
                            self.slack_client, self, command, channel, user)

                    time.sleep(READ_WEBSOCKET_DELAY)
            else:
                log.error("Connection failed. Invalid slack token or bot id?")

        log.info("Shutdown complete...")

@@ -2605,51 +2605,46 @@
     
-this parsing function returns None unless a
+Return (message, channel, user) if the
  mes
@@ -2650,24 +2650,16 @@
 ssage is
-%0A       
  directe
@@ -2671,28 +2671,56 @@
 the 
-Bot, based on its ID
+bot,%0A        otherwise return (None, None, None)
 .%0A  
@@ -2737,22 +2737,23 @@
         
-output
+message
 _list = 
@@ -2778,56 +2778,26 @@
     
-if output_list:%0A            for output in output
+for msg in message
 _lis
@@ -2815,48 +2815,41 @@
     
-    
 if 
-output and 'text' in output:%0A    
+msg.get(%22type%22) == %22message%22:%0A
     
@@ -2881,26 +2881,29 @@
  in 
-output%5B'text'%5D:%0A  
+msg.get(%22text%22, %22%22):%0A
     
@@ -2918,18 +2918,16 @@
         
-  
 # return
@@ -2992,34 +2992,26 @@
         
-    
 return 
-(output
+msg
 %5B'text'%5D
@@ -3041,38 +3041,35 @@
 trip().lower(), 
-output
+msg
 %5B'channel'%5D, out
@@ -3065,30 +3065,27 @@
 nnel'%5D, 
-output
+msg
 %5B'user'%5D
 )%0A      
@@ -3076,21 +3076,17 @@
 %5B'user'%5D
-)%0A   
+%0A
         
@@ -3097,47 +3097,32 @@
     
- 
 elif 
-output%5B'text'%5D and output%5B'text'%5D
+msg.get(%22text%22, %22%22)
 .sta
@@ -3131,32 +3131,74 @@
 swith('!'):%0A    
+                # return text after the !%0A
                 
@@ -3212,15 +3212,11 @@
 urn 
-(output
+msg
 %5B'te
@@ -3241,22 +3241,19 @@
 ower(), 
-output
+msg
 %5B'channe
@@ -3261,22 +3261,19 @@
 '%5D, 
-output
+msg
 %5B'user'%5D
 )%0A%0A 
@@ -3268,17 +3268,16 @@
 %5B'user'%5D
-)
 %0A%0A      
@@ -4737,20 +4737,8 @@
 mand
- and channel
 :%0A  

Improve raw message parsing