import logging
import sys
import select
import math
import json
from datetime import datetime, timedelta
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

class AsyncServer:
    def convert_hex2decimal(self, packet, readable_sock):
        """
        In the packet, the hexadecimal value is converted to a decimal value, structured in json format, and returned.
        
        packet           TCP Stream packet from IIot Gateway
        readable_sock       client socket object
        
        packet specification
        stx is the starting code, the hex value matching STX in the ascii code table
        utc time is the time when the sensor value is received from the iiot gate
        equipment id means the id of the equipment and is predefined in the database.
        sensor code is means the sensor's type like as tempeatur, pressure, voltage,...
        precision means the accuracy of sensor value, decimal point.
        The sensor value means the sensor value installed in the facility.
        """
        status = 'ER'
        modbus_dict = {'equipment_id': '', 'meta': {'ip': '',
                                                             'port': '',
                                                             'time':'' ,
                                                             'sensor_cd':'' ,
                                                             'fun_cd':'' ,
                                                             'sensor_value': '',
                                                             'precision':''
                                                             }}
        try:
            stx = packet[0:1]
            etx = packet[26:28]
            if stx == '2' and etx == '3':
                time_stamp = int(packet[1:9], 16)
                utc_time = datetime.utcfromtimestamp(time_stamp) + timedelta(hours=9)
                gmt_time = utc_time.strftime('%Y-%m-%d %H:%M:%S')
                equipment_id = packet[9:15]
                sensor_code = packet[15:19]
                function_code = packet[19:21]
                sensor_value = float(int(packet[21:25], 16))
                precision = float(packet[25:26])
                precision = math.pow(10, precision)
                sensor_value = sensor_value / precision
                etx = packet[26:28]
                host, port = readable_sock.getpeername()
                modbus_dict = {'equipment_id': equipment_id, 'meta': {'ip': host,
                                                                    'port': port,
                                                                    'time': gmt_time,
                                                                    'sensor_cd': sensor_code,
                                                                    'fun_cd': function_code,
                                                                    'sensor_value': sensor_value,
                                                                    'precision': precision
                                                                    }}
    
                status = 'OK'
            else:
                status = 'ER'
        except Exception as e:
            print(str(e))
        logging.debug(status + str(packet))
        return status, str(packet), modbus_dict
    
    
    async def get_client(self, event_manger, server_sock, msg_size, msg_queue):
        """
        It create client socket with server sockt
        event_manger        It has asyncio event loop
        server_socket       Socket corresponding to the client socket
        msg_size            It means the packet size to be acquired at a time from the client socket.
        msg_queue           It means the queue containing the message transmitted from the gateway.
        """
        while True:
            client, _ = await event_manger.sock_accept(server_sock)
            event_manger.create_task(self.manage_client(event_manger,  client, msg_size, msg_queue))
    
    
    async def manage_client(self, event_manger, client, msg_size, msg_queue):
        """
            It receives modbus data from iiot gateway using client socket.
            event_manger        It has asyncio event loop
            client              It is a client socket that works with multiple iiot gateways.
            msg_size            It means the packet size to be acquired at a time from the client socket.
            msg_queue           It means the queue containing the message transmitted from the gateway.
            """
        while True:
            try:
                packet = (await event_manger.sock_recv(client, msg_size))
                if packet:
                    try:
                        packet = packet.decode('utf-8')
                        status, packet, modbus_udp = self.convert_hex2decimal(packet, client)
                        if status == 'OK':
                            msg_queue.put(modbus_udp)
                        acq_message = status + packet + '\r\n'
                        client.sendall(acq_message.encode())
                    except Exception as e:
                        client.sendall(packet.encode())
                        logging.exception('message error:' + str(e))
                else:
                    client.close()
    
            except Exception as e:
                logging.exception('manage client exception:' + str(e))
                client.close()
                break
        
                
    def apply_sensor_name(self, db_con, message):
        sensor_cd = message['meta']['sensor_cd']
        sensor_cd_json = json.loads(db_con.get('sensor_cd'))
        sensor_desc = sensor_cd_json[sensor_cd]
        message['meta']['sensor_cd'] = sensor_desc
        return message
    
    
    def modbus_mqtt_publish(self, msg_queue, redis_con, mq_channel, u_test=False):
       while True:
           if msg_queue.qsize() > 0:
            msg_json = msg_queue.get()
            if u_test == True:
                return msg_json
            else:
                pass
                # msg_json = apply_sensor_name(db_con=redis_con, message=msg_json)
                # routing_key = msg_json['equipment_id']
                # msg_json = json.dumps(msg_json)
                # mq_channel.basic_publish(exchange='', routing_key=routing_key, body=msg_json)



