import socket

from . import const, Message

MESSAGE_TYPE = const.MESSAGE_TYPE
SEARCH_RES_OFFSET = const.SEARCH_RES_OFFSET


class OppleDevice(object):
    def __init__(self, ip='', message=None):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.bind(('0.0.0.0', 0))
        self.socket.settimeout(2)

        self.id = 0
        self.port = 0
        self.version = 0
        self.name = ''
        self.ip = ip
        self.ip_raw = ()
        self.mac = ''
        self.mac_raw = ()
        self.server_port = self.socket.getsockname()[1]
        self.is_init = False
        self.is_online = False

        if message:
            self.init(message)
        else:
            self.async_init()

    def init(self, message):
        self.id = message.get(SEARCH_RES_OFFSET['ID_LOW'], 4, value_type=int)
        self.port = message.get(SEARCH_RES_OFFSET['PORT'], 2, value_type=int)
        self.version = message.get(SEARCH_RES_OFFSET['VERSION'], 4, value_type=int)
        self.name = str(message.get(SEARCH_RES_OFFSET['NAME'], 0xE), 'gbk')
        self.ip_raw = message.get(SEARCH_RES_OFFSET['IP'], 4)
        self.mac_raw = message.get(SEARCH_RES_OFFSET['MAC'], 6)

        self.ip = '.'.join(map(lambda x: str(x), self.ip_raw))
        self.mac = ':'.join(map(lambda x: hex(x)[2:], self.mac_raw))

        self.is_init = True
        self.is_online = True

    def async_init(self):
        self.port = const.BROADCAST_PORT
        message = self.send('SEARCH', reply=True)
        if message:
            self.init(message)

    def send(self, message_type, data=None, reply=False):
        message = Message.build_message(MESSAGE_TYPE[message_type], data, self)
        self.socket.sendto(message.data, (self.ip, self.port))

        if reply:
            while True:
                try:
                    data, address = self.socket.recvfrom(1024)
                    incoming_message = Message.parse_message(data, self)

                    if message.get_request_sn() != incoming_message.get_response_sn():
                        continue

                    self.is_online = True
                    return incoming_message

                except socket.timeout:
                    self.is_online = False
                    return None


def search():
    message = Message.build_message(MESSAGE_TYPE['SEARCH'])
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.sendto(message.to_bytes(), ('255.255.255.255', const.BROADCAST_PORT))
    s.settimeout(2)

    from . import OppleLightDevice

    device_list = []

    while True:
        try:
            data, address = s.recvfrom(1024)
            message = Message.parse_message(data)

            if message.get(SEARCH_RES_OFFSET['CLASS_SKU'], 4, value_type=int) == 0x100010E:
                device = OppleLightDevice.OppleLightDevice(message=message)
            else:
                break

            device_list.append(device)
            yield device
        except socket.timeout:
            break

    return device_list
