# Copyright 3D Control Systems, Inc. All Rights Reserved 2017-2019.

# Built in San Francisco.

# This software is distributed under a commercial license for personal,
# educational, corporate or any other use.
# The software as a whole or any parts of it is prohibited for distribution or
# use without obtaining a license from 3D Control Systems, Inc.

# All software licenses are subject to the 3DPrinterOS terms of use
# (available at https://www.3dprinteros.com/terms-and-conditions/),
# and privacy policy (available at https://www.3dprinteros.com/privacy-policy/)

import socket
import time
import json
import base_scanner

class ElegooDetector(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_scanner"]

    def detect_printer_profile(self, data):
        snr = data.get("Data", {}).get("MainboardID", "")
        printer_ip = data.get("Data", {}).get("MainboardIP", "")
        model_id = f'{data.get("Data", {}).get("BrandName", "")} {data.get("Data", {}).get("MachineName", "")}'
        if snr and printer_ip and model_id:
            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 model_id:
                    vid, pid = profile.get('vids_pids')[0]
                    return {
                        # 'SNR': str(snr),
                        'SNR': str(printer_ip), # just to not duplicate if already added because already_know_ip ignored: TODO: fix me and use `snr`
                        'IP': str(printer_ip),
                        'VID': vid,
                        'PID': pid
                    }

    def detect(self, already_know_ip=None, non_default_port=None):
        self.logger.info("Start SDCP 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 = "M99999"
            port = 3000
            broadcast_addr = ('<broadcast>', port)
        except Exception as e:
            self.logger.exception("Exception in detector: " + str(e))
        else:
            start_time = time.monotonic()
            printers = {}
            sock.sendto(message.encode('utf-8'), broadcast_addr)
            while time.monotonic() - start_time < self.DETECTION_TIMEOUT:
                try:
                    data, addr = sock.recvfrom(self.SOCKET_READ_SIZE)
                    decoded_data = json.loads(data.decode('utf-8'))
                    if decoded_data:
                        device_info = self.detect_printer_profile(decoded_data)
                        if device_info:
                            printers[device_info["SNR"]] = device_info
                except socket.timeout:
                    pass
                except IndexError:
                    self.logger.error(f"Can't detect addr port. addr: {addr}")
                except Exception as e:
                    self.logger.exception("Exception in detector: " + str(e))
            self.discovered_printers = printers.values()
            if already_know_ip:
                for printer in self.discovered_printers:
                    if printer.get("IP", "") == already_know_ip:
                        return [printer]
                    else:
                        return []
            return self.discovered_printers


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