#!/usr/bin/python

'''
 
WORKSTATION CLIENT BACKEND

'''


import sys
try:
    import paho.mqtt.client as mqtt
except ImportError:
    # This part is only required to run the example from within the examples
    # directory when the module itself is not installed.
    #
    # If you have the module installed, just use "import paho.mqtt.client"
    import os
    import inspect
    cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"../src")))
    if cmd_subfolder not in sys.path:
        sys.path.insert(0, cmd_subfolder)
    import paho.mqtt.client as mqtt
# NOTE: This needs to be replaced with a QT, TKinker alternative. 
# from wx.lib.pubsub import pub
from packet_handling import decode_client_list, decode_data_packet
try: 
    from ctype_bindings import SwitchPro6
    windows = True
except AttributeError: 
    print("Windows win32 API missing.\n Button presses will be still be represented in print statements.") 
    global print_debug 
    windows = False

#------------------------------------------------------------------------------
# WORKSTATIONCLIENT CLASS

class WorkstationClientClass():
    def __init__(self, client_id=None, debug=True, mqttdebug=False):
        self.client_id = client_id 
        self.debug = debug
        self.mqttdebug = mqttdebug
        self.mode_color = 'debug'
            
        self.data_topics = '/modules/+/data'
        self.clientlist_topic = '/workstations/modulehealth'
        self.clientlist = None 
        self.subscribed_modules = []
        if windows:
            self.switchpro6 = SwitchPro6()
        self._mqttc = mqtt.Client(self.client_id, clean_session=True, userdata=None, 
                        protocol='MQTTv311')
        self._mqttc.message_callback_add(self.clientlist_topic, self.mqtt_on_client_list) 
        self._mqttc.message_callback_add(self.data_topics, self.mqtt_on_data)
        self._mqttc.on_connect = self.mqtt_on_connect
        self._mqttc.on_disconnect = self.mqtt_on_disconnect        
        if self.mqttdebug: 
            self._mqttc.on_log = self.mqtt_on_log
            
#------------------------------------------------------------------------------
# callback functions

    def mqtt_on_connect(self, mqttc, backend_data, flags, rc):
        # pub.sendMessage('status.connection', status = [mqttc._host, mqttc._port, rc])
        if self.debug: 
            if rc == 0: 
                print('Connected to %s: %s' % (mqttc._host, mqttc._port))
            else: 
                print('rc: ' + str(rc))

    def mqtt_on_client_list(self, mqttc, backend_data, msg):
        self.clientlist = decode_client_list(msg.payload) 
        
        for active_module_id in self.subscribed_modules: 
            for dead_module_id in self.clientlist[self.clientlist['alive']==0]['module_id']: 
                if active_module_id == dead_module_id: 
                    if self.debug: 
                        print("Conflicting ID: " + str(active_module_id)+ \
                        "\nAttempting to kill now.")
                    self.unsubscribe_to_module(active_module_id)
        if self.debug: 
            print(self.clientlist)
        # pub.sendMessage('data.clientlist', data = self.clientlist)
       
        
    def mqtt_on_data(self, mqttc, backend_data, msg):
        #pub.sendMessage('data.module', data=msg.payload)
        data =  decode_data_packet(msg.payload)
        if windows:     
            self.switchpro6.keymap_press(data[0])
        if self.debug: 
            print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload)+" "+str(data))

    def mqtt_on_log(self, mqttc, backend_data, level, string):
        print(string)
    
    def mqtt_on_disconnect(self, mqttc, backend_data, rc):
        # pub.sendMessage('status.connection', status = [None, None, rc])
        if self.debug: 
            if rc != 0: 
                print("Client disconnected and its a mystery why!")
            else: 
                print("Client successfully disconnected.") 
#------------------------------------------------------------------------------
# General API Calls 
        
    def start_workstation_client(self, ip="127.0.0.1", port=1883, keepalive=60):     
        self.ip = ip 
        self.port = port 
        self.keepalive = keepalive 
        self.clientlist = None
        self._mqttc.connect(ip, port, keepalive)      # connect to broker                              
        self._mqttc.subscribe(self.clientlist_topic, 2)    # subscribe to client list 
        self._mqttc.loop_start() # Start a thread handling network traffic   
    
    def subscribe_to_module(self, module_id): 
        module_found = False
        if type(module_id) == int: 
            module_id = str(module_id) 
        if len(module_id) < 8:
            module_id = '0'*(8-len(module_id)) + module_id
	
        for active_module_id in self.clientlist[self.clientlist['alive']==1]['module_id']:
            if int(module_id) == active_module_id: 
                module_found = True
                module_topic = '/modules/'+module_id+'/data'
                result, mid =  self._mqttc.subscribe(module_topic, qos=0)
                # pub.sendMessage('status.subscribe', status=[int(module_id), result, mid])
                if result == 0:
                    if int(module_id) not in self.subscribed_modules: 
                        self.subscribed_modules.append(int(module_id))
                        #pub.sendMessage('data.subscribed_modules', data=self.subscribed_modules)
                        if self.debug: 
                            print("Module %s was successfully added" % module_id)
                    else: 
                        if self.debug: 
                            print("Module is already subscribed")
                return result, mid
        if module_found == False:
            if self.debug: 
                print("Module is not alive or in client list.") 
                print("Module ID: " + str(module_id))
                return 3, None 
        
    def unsubscribe_to_module(self, module_id): 
        if type(module_id) == int: 
            module_id = str(module_id) 
        if len(module_id) < 8:
            module_id = '0'*(8-len(module_id)) + module_id
        
        if int(module_id) in self.subscribed_modules: 
            module_topic = '/modules/'+module_id+'/data'
            result, mid = self._mqttc.unsubscribe(module_topic)
            # pub.sendMessage('status.unsubscribe', status=[int(module_id), result, mid])
            if result == 0: 
                self.subscribed_modules.remove(int(module_id))
                #pub.sendMessage('data.subscribed_modules', data=self.subscribed_modules)    # send all the subs
                if self.debug: 
                    print("Module %s was successfully removed" % module_id)
            return result, mid            
        else:            
            if self.debug: 
                print("Module is not subscribed") 
                print("Subscribed Modules:\n", self.subscribed_modules)
            return 3, None 

#-----------------------------------------------------------------------------
# Puck Connectivity 	
# The Puck will function under the following methods. 
# 1. The puck will make the system aware of it's presence through a health 
#    message to the database client. There needs to be sometype of addition
#    flag or delimiter for the database client to know it is a puck. 
# 2. The backend needs to forward this flag via pubsub. 
# 3. There needs to be function to pair a module to a puck with the following
#    house keeping skills: 
#    a. Automatically unpairs if module or puck dies 
#    b. will not pair if puck or module is already connected to computer or 
#       paired with another computer. 
#    c. sends a message to puck to listen to the topic of a specific module. 
#    d. confirms puck is listening through sometype of verification QoS = 2? 
# 

    def pair_module_to_puck(self, module_id, puck_id, pair=True):
        if type(module_id) == int: 
            module_id = str(module_id)
        if len(module_id)<8: 
            module_id = '0'*(8-len(module_id)) + module_id         
        if type(puck_id) == int: 
            puck_id = str(puck_id)
        if len(puck_id)<8: 
            puck_id = '0'*(8-len(puck_id)) + puck_id 
        
        module_found = False
        # update to insure puck is alive
        # modifiy so pandas only is loooking at modules
        for active_module_id in self.clientlist[self.clientlist['alive']==1]['module_id']:
            if int(module_id) == active_module_id :
                module_found = True 
                puck_topic = '/pucks/'+puck_id+'/command'
                if pair:
                    payload = 'a/modules/'+str(module_id)+'/data'
                else: 
                    payload = 'd/modules/'+str(module_id)+'/data'
                result, mid =  self._mqttc.publish(puck_topic, payload, 0, retain=False)
                if result == 0: 
                    # self.subscribed_modules.remove(int(module_id))
                    # pub.sendMessage('data.paired_modules', data=self.subscribed_modules)    # send all the subs
                    if self.debug: 
                        print("Module %s was successfully paired" % module_id)
                    return result, mid    

        if module_found == False:
            if self.debug: 
                print("Module is not alive or in client list.")
                print("Module ID: " + str(module_id))
                return 3, None 
        
        
                
if __name__ == "__main__": 
    wsc = WorkstationClientClass() 
    wsc.start_workstation_client(ip="server.ideasx.tech")