import sys
import os
import socket
import json
import time


# fix of broken paths for windows
if not sys.path:
    sys.path = []
path = os.getcwd()
if path not in sys.path:
    sys.path.insert(0, path)

import base_scanner


class ElegooCC2Detector(base_scanner.Scanner):

    CAN_DETECT_SNR = True
    CAN_DETECT_VID_PID = True
    SOCKET_TIMEOUT = 3
    DETECTION_TIMEOUT = 11
    SOCKET_READ_SIZE = 1024
    SCANNER_NAME_IN_PROFILE = ["elegoo_cc2_scanner"]
    DISCOVERY_PORT = 52700

    def detect_printer_profile(self, data, addr):
        result = data.get("result", {})
        if isinstance(result, dict):
            sn = result.get("sn", "")
            machine_model = result.get("machine_model", "")
            #host_name = result.get("host_name", "")
            #token_status = result.get("token_status", 0)
            printer_ip = addr[0]
            if sn and printer_ip:
                for profile in self.profiles:
                    machine_type_id = profile.get('network_detect', {}).get('machine_type_id')
                    if machine_type_id and machine_type_id in machine_model:
                        vid, pid = profile.get('vids_pids')[0]
                        return {
                            'SNR': str(sn),
                            'IP': str(printer_ip),
                            'VID': vid,
                            'PID': pid
                        }
                self.logger.error("Unknown machine type")
            else:
                self.logger.error("Error: empty serial number")

    def detect(self, already_know_ip=None, non_default_port=None):
        self.logger.info("Start Elegoo CC2 detect")
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            sock.settimeout(self.SOCKET_TIMEOUT)
            message = json.dumps({"id": 0, "method": 7000})
            broadcast_addr = ('<broadcast>', self.DISCOVERY_PORT)
        except Exception as e:
            self.logger.exception("Exception in detector: " + str(e))
            return []
        else:
            start_time = time.monotonic()
            printers = {}
            try:
                sock.sendto(message.encode('utf-8'), broadcast_addr)
            except Exception as e:
                self.logger.exception("Exception on broadcast: " + str(e))
            # TODO
            # think about concatenating the received data
            # UDP can split it between messages
            # but that's rare on LAN and not critical
            while time.monotonic() - start_time < self.DETECTION_TIMEOUT:
                try:
                    data, addr = sock.recvfrom(self.SOCKET_READ_SIZE)
                    if data:
                        decoded_data = json.loads(data.decode('utf-8'))
                        if decoded_data:
                            device_info = self.detect_printer_profile(decoded_data, addr)
                            if device_info:
                                printers[device_info["SNR"]] = device_info
                except socket.timeout:
                    pass
                except json.JSONDecodeError:
                    self.logger.error("Invalid JSON received")
                except Exception as e:
                    self.logger.exception("Exception in detector: " + str(e))
            self.discovered_printers = list(printers.values())
            if already_know_ip:
                for printer in self.discovered_printers:
                    if printer.get("IP", "") == already_know_ip:
                        return [printer]
                return []
            return self.discovered_printers


if __name__ == "__main__":
    import pprint
    scr = ElegooCC2Detector(None)
    pprint.pprint(scr.detect())
