import json
import logging
import datetime
from base64 import b64decode, b64encode
from binascii import a2b_hex, b2a_hex
from struct import *

from .loxdimmer import LoxDimmer
from .loxswitch import LoxSwitch
from .loxcolorpickerv2 import LoxColorPickerV2
from .loxlightcontrollerv2 import LoxLightControllerV2
from .loxiroomcontrollerv2 import LoxIntelligentRoomControllerV2
from .loxinfoonlyanalog import LoxInfoOnlyAnalog
from .loxinfoonlydigital import LoxInfoOnlyDigital
from .loxclimatecontroller import LoxClimateController
from .loxjalousie import LoxJalousie

_LOGGER = logging.getLogger(__name__)

#https://docs.python.org/3.7/library/struct.html#format-characters

class MessageBody:

    def __init__(self, payload, isBinary, header, config_data):
        message_timestamp = datetime.datetime.now().strftime("%d-%b-%Y %H:%M:%S")

        if header.msg_type == "Text":
            """Text"""
            #_LOGGER.debug('TEXT MESSAGE - {1} bytes - Text'.format(message_timestamp, len(payload)))
            self.raw = payload 
            #_LOGGER.debug("  raw: {0} {1}".format(type(self.raw), self.raw))
            self.msg = json.loads(payload)

            if "Code" in self.raw:
                self.code = self.msg["LL"]["Code"]
                #_LOGGER.debug("    code: " + self.code)

            if "code" in self.raw:
                self.code = self.msg["LL"]["code"]
                #_LOGGER.debug("    code: {0}".format(self.code))
            
            if "control" in self.raw:
                self.control = self.msg["LL"]["control"]
                #_LOGGER.debug("    control: " + self.control)

            if "value" in self.raw:
                self.value = self.msg["LL"]["value"]
                #_LOGGER.debug("    value: {0}".format(self.value))

        elif header.msg_type == "Binary":
            """Binary"""
            #_LOGGER.debug('BINARY MESSAGE - {1} bytes - Binary'.format(message_timestamp, len(payload)))
            self.control = "LoxAPP3.json"
            #_LOGGER.debug("  control: " + self.control)
            self.raw = payload 
            #_LOGGER.debug("  raw: {0} {1}".format(type(self.raw), self.raw))
            self.msg = json.loads(payload)

        elif header.msg_type == "Value-States":
            """Value-States"""
            #_LOGGER.debug('BINARY MESSAGE - {1} bytes - Value-States'.format(message_timestamp, len(payload)))
            x = 0
            while x < len(payload):
                uuid = self.getUuid(payload[x:x+16])
                #_LOGGER.debug("  uuid: {0} {1} {2}".format(type(uuid), len(uuid), uuid))

                e = payload[x+16:x+24]
                #_LOGGER.debug("  e: {0} {1} {2}".format(type(e), len(e), e))

                dval = unpack('<d', e)[0]
                #_LOGGER.debug("  dval: {0} {1}".format(type(dval), dval))

                if uuid in config_data.fieldmap:
                    try:
                        _LOGGER.debug("UUID in fieldmap List {0}".format(uuid))
                        mapentry = config_data.fieldmap[uuid]

                        device = mapentry["device"]
                        stateName = mapentry["stateName"]

                        _LOGGER.debug("SettingValueState {0} {1} device_type: {2} state_name: {3}".format(device.id, device.name, device.device_type, mapentry["stateName"]))

                        device.set_value(stateName, dval)

                    except Exception as ex:
                        _LOGGER.error(ex)

                else:
                    category = 'unknown'
                    controlName = ''
                    controlType = ''
                    stateName = ''
                    roomName = ''
                    for k,v in config_data.data["globalStates"].items():
                        if v == uuid:
                            category = 'globalState'
                    for k,v in config_data.data["caller"].items():
                        if v == uuid:
                            category = 'caller'
                        if 'states' in v:
                            for tk,tv in v["states"].items():
                                if tv == uuid:
                                    category = 'callerState'
                                    stateName = tk
                    for k,v in config_data.data["autopilot"].items():
                        if v == uuid:
                            category = 'autopilot'
                        if 'states' in v:
                            for tk,tv in v["states"].items():
                                if tv == uuid:
                                    category = 'autopilotState'
                                    stateName = tk
                    for k,v in config_data.data["messageCenter"].items():
                        if v == uuid:
                            category = 'messageCenter'
                            stateName = k
                        if 'states' in v:
                            for tk,tv in v["states"].items():
                                if tv == uuid:
                                    category = 'messageCenterState'
                                    stateName = tk

                    if uuid in config_data.data["controls"]:
                        control = config_data.data["controls"][uuid]
                        category = 'control'
                        controlName = control["name"]
                        controlType = control["type"]
                        if 'room' in control:
                            roomName = config_data.get_room_name(control["room"])
                    else:
                        for k,v in config_data.data["controls"].items():
                            if 'states' in v:
                                for tk,tv in v["states"].items():
                                    if tv == uuid:
                                        category = 'controlState'
                                        controlName = v["name"]
                                        controlType = v["type"]
                                        stateName = tk
                                        if 'room' in v:
                                            roomName = config_data.get_room_name(v["room"])

                            if 'subControls' in v:
                                for sk,sv in v["subControls"].items():
                                    if 'states' in sv:
                                        for stk,stv in sv["states"].items():
                                            if stv == uuid:
                                                category = 'subControlState'
                                                controlName = sv["name"]
                                                controlType = sv["type"]
                                                stateName = stk
                                                if 'room' in v:
                                                    roomName = config_data.get_room_name(v["room"])

                    _LOGGER.debug("  uuid: {0} (VS) Category: '{1}' Type: '{2}' Room: '{3}' Name: '{4}' state: '{5}' dval: {6}".format(uuid, category, controlType, roomName, controlName, stateName, dval))

                x = x + 24

        elif header.msg_type == "Text-States":
            """Text-States"""
            #_LOGGER.debug('BINARY MESSAGE - {1} bytes - Text-States'.format(message_timestamp, len(payload)))
            x = 0
            while x < len(payload):
                uuid = self.getUuid(payload[x:x+16])
                #_LOGGER.debug("  uuid: {0} {1} {2}".format(type(uuid), len(uuid), uuid))

                uuidIcon = self.getUuid(payload[x+16:x+32])
                #_LOGGER.debug("  uuidIcon: {0} {1} {2}".format(type(uuidIcon), len(uuidIcon), uuidIcon))

                e = payload[x+32:x+36]
                #_LOGGER.debug("  e: {0} {1} {2}".format(type(e), len(e), e))

                size = unpack('<I', e)[0]
                #_LOGGER.debug("  size: {0} {1}".format(type(size), size))

                rem = size % 4
                #_LOGGER.debug("  rem: {0} {1}".format(type(rem), rem))

                text = str(payload[x+36:x+36+size], 'utf-8')
                #_LOGGER.debug("  text: {0} {1} {2}".format(type(text), len(text), text))

                if uuid in config_data.fieldmap:
                    try:
                        #_LOGGER.debug("UUID in field map")
                        mapentry = config_data.fieldmap[uuid]

                        device = mapentry["device"]
                        stateName = mapentry["stateName"]

                        if device.device_type == "ClimateController":
                            _LOGGER.debug("ClimateController {0} {1} device_type: {2} state_name: {3} text: {4}".format(device.id, device.name, device.device_type, mapentry["stateName"], text))

                            if stateName == "controls":
                                controls = json.loads(text)
                                for con in controls:
                                    con_uuid = con['uuid']
                                    con_demand = con['demand']

                                    _LOGGER.debug("  climate control - uuid: {0} demand: {1}".format(con_uuid, con_demand))

                                    for rck,rcv in config_data.roomcontrollers.items():
                                        #_LOGGER.debug("     available controller: {0}".format(rck))
                                        if rck == con_uuid:
                                            rcv.set_value("demand", con_demand)



                        else:
                            _LOGGER.debug("SettingTextState {0} {1} device_type: {2} state_name: {3}".format(device.id, device.name, device.device_type, mapentry["stateName"]))

                            device.set_value(stateName, text)

                    except Exception as ex:
                        _LOGGER.error(ex)

                else:
                    category = 'unknown'
                    controlName = ''
                    controlType = ''
                    stateName = ''
                    for k,v in config_data.data["globalStates"].items():
                        if v == uuid:
                            category = 'globalState'
                    for k,v in config_data.data["caller"].items():
                        if v == uuid:
                            category = 'caller'
                        if 'states' in v:
                            for tk,tv in v["states"].items():
                                if tv == uuid:
                                    category = 'callerState'
                                    stateName = tk
                    for k,v in config_data.data["autopilot"].items():
                        if v == uuid:
                            category = 'autopilot'
                        if 'states' in v:
                            for tk,tv in v["states"].items():
                                if tv == uuid:
                                    category = 'autopilotState'
                                    stateName = tk
                    for k,v in config_data.data["messageCenter"].items():
                        if v == uuid:
                            category = 'messageCenter'
                            stateName = k
                        if 'states' in v:
                            for tk,tv in v["states"].items():
                                if tv == uuid:
                                    category = 'messageCenterState'
                                    stateName = tk
                    if uuid in config_data.data["controls"]:
                        control = config_data.data["controls"][uuid]
                        category = 'control'
                        controlName = control["name"]
                        controlType = control["type"]
                    else:
                        for k,v in config_data.data["controls"].items():
                            if 'states' in v:
                                for tk,tv in v["states"].items():
                                    if tv == uuid:
                                        category = 'controlState'
                                        controlName = v["name"]
                                        controlType = v["type"]
                                        stateName = tk
                            if 'subControls' in v:
                                for sk,sv in v["subControls"].items():
                                    if 'states' in sv:
                                        for stk,stv in sv["states"].items():
                                            if stv == uuid:
                                                category = 'subControlState'
                                                controlName = sv["name"]
                                                controlType = sv["type"]
                                                stateName = stk

                    _LOGGER.debug("  uuid: {0} (TS) Category: '{1}' Type: '{2}' Name: '{3}' state: '{4}' uuidIcon: {5} text: {6}".format(uuid, category, controlType, controlName, stateName, uuidIcon, text))

                x = x + 36 + size
                if rem > 0:
                    x = x + (4 - rem)

        elif header.msg_type == "Daytime-States":
            """Daytime-States"""
            #_LOGGER.debug('BINARY MESSAGE - {1} bytes - Daytime-States'.format(message_timestamp, len(payload)))
            x = 0
            while x < len(payload):
                #uuid - 128-Bit uuid - 16 bytes
                uuid = self.getUuid(payload[x:x+16])
                #_LOGGER.debug("  uuid: {0} {1} {2}".format(type(uuid), len(uuid), uuid))

                #dDefValue - 64-Bit Float (little endian) default value - 8 bytes     
                dDefValue = unpack('<d', payload[x+16:x+24])[0]
                #_LOGGER.debug("  dDefValue: {0} {1}".format(type(dDefValue), dDefValue))

                #nrEntries - 32-Bit Integer (little endian) - 4 bytes
                nrEntries = unpack('<i', payload[x+24:x+28])[0]
                #_LOGGER.debug("  nrEntries: {0} {1}".format(type(nrEntries), nrEntries))

                y = 0
                while y < nrEntries:
                    ys = 28 + (y*24)
                    y = y + 1

                    #nMode - 32-Bit Integer (little endian) number of mode - 4 bytes
                    nMode = unpack('<i', payload[ys+0:ys+4])[0]
                    #_LOGGER.debug("    nMode: {0} {1}".format(type(nMode), nMode))

                    #nFrom - 32-Bit Integer (little endian) from-time in minutes since midnight - 4 bytes
                    nFrom = unpack('<i', payload[ys+4:ys+8])[0]
                    #_LOGGER.debug("    nFrom: {0} {1}".format(type(nFrom), nFrom))

                    #nTo - 32-Bit Integer (little endian) to-time in minutes since midnight - 4 bytes
                    nTo = unpack('<i', payload[ys+8:ys+12])[0]
                    #_LOGGER.debug("    nTo: {0} {1}".format(type(nTo), nTo))

                    #bNeedActivate - 32-Bit Integer (little endian) need activate (trigger) - 4 bytes
                    bNeedActivate = unpack('<i', payload[ys+12:ys+16])[0]
                    #_LOGGER.debug("    bNeedActivate: {0} {1}".format(type(bNeedActivate), bNeedActivate))

                    #dValue - 64-Bit Float (little endian) value (if analog daytimer) - 8 bytes
                    dValue = unpack('<d', payload[x+16:x+24])[0]
                    #_LOGGER.debug("    dValue: {0} {1}".format(type(dValue), dValue))

                x = x + 28 + (24 * nrEntries)

        elif header.msg_type == "OutOfService":
            """OutOfService"""
            #_LOGGER.debug('BINARY MESSAGE - {1} bytes - OutOfService'.format(message_timestamp, len(payload)))

        elif header.msg_type == "KeepAlive":
            """KeepAlive"""
            #_LOGGER.debug('BINARY MESSAGE - {1} bytes - KeepAlive'.format(message_timestamp, len(payload)))

        elif header.msg_type == "Weather-States":
            """Weather-States"""
            #_LOGGER.debug('BINARY MESSAGE - {1} bytes - Weather-States'.format(message_timestamp, len(payload)))
            x = 0
            while x < len(payload):
                #uuid - 128-Bit uuid - 16 bytes
                uuid = self.getUuid(payload[x:x+16])
                #_LOGGER.debug("  uuid: {0} {1} {2}".format(type(uuid), len(uuid), uuid))

                #lastUpdate - 32-Bit Unsigned Integer (little endian) - 4 bytes
                lastUpdate = unpack('<I', payload[x+16:x+20])[0]
                #_LOGGER.debug("  lastUpdate: {0} {1}".format(type(lastUpdate), lastUpdate))

                #nrEntries - 32-Bit Integer (little endian) - 4 bytes
                nrEntries = unpack('<i', payload[x+20:x+24])[0]
                #_LOGGER.debug("  nrEntries: {0} {1}".format(type(nrEntries), nrEntries))

                y = 0
                while y < nrEntries:
                    ys = 24 + (y*24)
                    y = y + 1

                    #​timestamp - 32-Bit Integer (little endian) - 4 bytes
                    timestamp = unpack('<i', payload[ys+0:ys+4])[0]
                    #_LOGGER.debug("    timestamp: {0} {1}".format(type(timestamp), timestamp))

                    #​weatherType - 32-Bit Integer (little endian) - 4 bytes
                    weatherType = unpack('<i', payload[ys+4:ys+8])[0]
                    #_LOGGER.debug("    ​weatherType: {0} {1}".format(type(weatherType), weatherType))

                    #​windDirection - 32-Bit Integer (little endian) - 4 bytes
                    windDirection = unpack('<i', payload[ys+8:ys+12])[0]
                    #_LOGGER.debug("    ​windDirection: {0} {1}".format(type(windDirection), windDirection))

                    #​solarRadiation - 32-Bit Integer (little endian) - 4 bytes
                    solarRadiation = unpack('<i', payload[ys+12:ys+16])[0]
                    #_LOGGER.debug("    ​solarRadiation: {0} {1}".format(type(solarRadiation), solarRadiation))

                    #​relativeHumidity - 32-Bit Integer (little endian) - 4 bytes
                    relativeHumidity = unpack('<i', payload[ys+16:ys+20])[0]
                    #_LOGGER.debug("    ​relativeHumidity: {0} {1}".format(type(relativeHumidity), relativeHumidity))

                    #​temperature - 64-Bit Float (little endian) - 8 bytes
                    temperature = unpack('<d', payload[ys+20:ys+28])[0]
                    #_LOGGER.debug("    ​temperature: {0} {1}".format(type(temperature), temperature))

                    #​perceivedTemperature - 64-Bit Float (little endian) 8 - bytes
                    perceivedTemperature = unpack('<d', payload[ys+28:ys+36])[0]
                    #_LOGGER.debug("    ​perceivedTemperature: {0} {1}".format(type(perceivedTemperature), perceivedTemperature))

                    #​dewPoint - 64-Bit Float (little endian) - 8 bytes
                    dewPoint = unpack('<d', payload[ys+36:ys+44])[0]
                    #_LOGGER.debug("    ​dewPoint: {0} {1}".format(type(dewPoint), dewPoint))

                    #​precipitation - 64-Bit Float (little endian) - 8 bytes
                    precipitation = unpack('<d', payload[ys+44:ys+52])[0]
                    #_LOGGER.debug("    ​precipitation: {0} {1}".format(type(precipitation), precipitation))

                    #​windSpeed - 64-Bit Float (little endian) - 8 bytes
                    windSpeed = unpack('<d', payload[ys+52:ys+60])[0]
                    #_LOGGER.debug("    ​windSpeed: {0} {1}".format(type(windSpeed), windSpeed))

                    #barometicPressure - 64-Bit Float (little endian) - 8 bytes
                    barometicPressure = unpack('<d', payload[ys+60:ys+68])[0]
                    #_LOGGER.debug("    barometicPressure: {0} {1}".format(type(barometicPressure), barometicPressure))

                x = x + 24 + (68 * nrEntries)

    def getUuid(self, data):
        """UUID"""
        hexva = b2a_hex(data)
        #_LOGGER.debug("    hexva: {0} {1} {2}".format(type(hexva), len(hexva), hexva))

        a = hexva[0:8]
        #_LOGGER.debug("    a: {0} {1} {2}".format(type(a), len(a), a))
        a1 = "{:08x}".format(int.from_bytes(a2b_hex(a), byteorder='little'))
        #_LOGGER.debug("    a1: {0} {1} {2}".format(type(a1), len(a1), a1))

        b = hexva[8:12]
        #_LOGGER.debug("    b: {0} {1} {2}".format(type(b), len(b), b))
        b1 = "{:04x}".format(int.from_bytes(a2b_hex(b), byteorder='little'))
        #_LOGGER.debug("    b1: {0} {1} {2}".format(type(b1), len(b1), b1))

        c = hexva[12:16]
        #_LOGGER.debug("    c: {0} {1} {2}".format(type(c), len(c), c))
        c1 = "{:04x}".format(int.from_bytes(a2b_hex(c), byteorder='little'))
        #_LOGGER.debug("    c1: {0} {1} {2}".format(type(c1), len(c1), c1))

        d1 = str(hexva[16:32],'utf8')
        #_LOGGER.debug("    d1: {0} {1} {2}".format(type(d1), len(d1), d1))

        uuid = "{0}-{1}-{2}-{3}".format(a1, b1, c1, d1)
        #_LOGGER.debug("    uuid: {0} {1} {2}".format(type(uuid), len(uuid), uuid))

        return uuid