# 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 findssh
import logging
import time
import zeroconf

import base_scanner
import formlabs_checker

class ZeroconfDetector(base_scanner.Scanner):

    TIMEOUT = 6
    DEFAULT_MDNS_SERVICE_NAME = "_http._tcp.local."
    FORMLABS_MDNS_SERVICE_NAME = "_formlabs_formule._tcp.local."
    CAN_DETECT_SNR = True
    CAN_DETECT_VID_PID = True
    CAN_DETECT_SNR = True
    SCANNER_NAME_IN_PROFILE = ['zeroconf', 'zeroconf_formlabs']

    CHECKER_FOR_SERVICE = {FORMLABS_MDNS_SERVICE_NAME: formlabs_checker.Checker}

    # CHECKER_FOR_SERVICE = {FORMLABS_MDNS_SERVICE_NAME: formlabs_checker.Checker,
    #                        DEFAULT_MDNS_SERVICE_NAME: formlabs_checker.Checker} 

    def __init__(self, parent, profile=None, conn_id=None):
        super().__init__(parent, profile, conn_id)
        self.service_names = []
        if profile and profile.get("network_detect"):
            service_name = profile.get("network_detect").get('filter')
            if service_name:
                if not service_name.endswith('.local.'):
                    service_name += '.local.'
                if service_name not in self.service_names:
                    self.service_names.append(service_name)
                self.port = int(profile.get("network_detect").get('port', self.PORT))
        elif self.profiles:
            for profile in self.profiles:
                service_name = profile.get("network_detect").get('filter')
                if service_name:
                    if not service_name.endswith('.local.'):
                        service_name += '.local.'
                    if service_name not in self.service_names:
                        self.service_names.append(service_name)
                    self.port = int(profile.get("network_detect").get('port'))
        else:
            # self.service_names = [self.FORMLABS_MDNS_SERVICE_NAME]
            self.service_names = [self.DEFAULT_MDNS_SERVICE_NAME, self.FORMLABS_MDNS_SERVICE_NAME]
        self.logger.info('Zeroconf searching services:' + str(self.service_names))

    def detect(self, already_know_ip=None, non_default_port=None):
        if non_default_port:
            self.port = non_default_port
        self.zeroconf = zeroconf.Zeroconf()
        self.browser = zeroconf.ServiceBrowser(self.zeroconf, self.service_names, self)
        count = 100
        sleep_per_loop = self.TIMEOUT / count
        while count:
            if self.parent and self.parent.stop_flag:
                break
            time.sleep(sleep_per_loop)
            count -= 1
        self.zeroconf.close()
        return self.discovered_printers

    def check_host(self, ip, service_type):
        printer = {'IP': ip}
        for key in self.CHECKER_FOR_SERVICE:
            if key == service_type:
                checker = self.CHECKER_FOR_SERVICE[key](self, self.profile)
                result = checker.check(printer)
                if result:
                    return result
        return printer 

    def add_service(self, zeroconf, service_type, name):
        info = zeroconf.get_service_info(service_type, name)
        print('INFO:' + str(info))
        try:
            ip = info.parsed_addresses()[0]
        except:
            self.logger.error('Failed to determine IP address of detected service')
        else:
            # port is not real port on which zeronconf responded, but port belong to printer's API
            self.logger.info(f"Zeroconf service found on {ip}")
            self.logger.debug(f"Service {name} added:\nIP:{ip}\nInfo:{info}")
            checked_host = self.check_host(ip, service_type)
            if checked_host:
                if checked_host and checked_host not in self.discovered_printers:
                    self.logger.info(f"Adding printer: {checked_host}")
                    self.discovered_printers.append(checked_host)
            else:
                self.logger.debug(f"Skipping host(printer check failed)")

    # NOTE: much be present for zeroconf not to crash
    def remove_service(self, zeroconf, service_type, name):
        self.logger.debug("Service %s %s removed" % (name, service_type))

    # NOTE: much be present for zeroconf not to crash
    def update_service(self, zeroconf, service_type, name):
        self.logger.debug("Service %s %s updated" % (name, service_type))


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