import http.client
import ipaddress
import json
import logging
import socket
import pprint


import findssh
import findssh.threadpool
import log


class HTTPDetector:

    PORT = 80
    TIMEOUT = 1
    FINDSSH_SERVICE = "http"
    PATH = "/"
    RESPONSE_FIELD_NAME = "status"
    METHOD = "GET"
    BODY = None
    HEADERS = None

    def __init__(self, parent=None, profile=None):
        self.logger = logging.getLogger(__name__)
        self.ip = None
        if parent:
            self.ip = ipaddress.ip_address(parent.local_ip)
        if not self.ip:
            self.ip = findssh.getLANip()
        self.parent = parent
        self.profile = profile
        self.port = self.PORT
        self.discovered_printers = []

    def process_hosts(self, hosts):
        matched_hosts = {}
        for host in hosts:
            host_ip = str(host[0])
            if self.parent_stop():
                break
            match = self.check_ip(host_ip)
            if match:
                matched_hosts[host_ip] = match
        return matched_hosts

    def check_ip(self, host):
        self.logger.debug("Detecting host: %s", str(host))
        if self.port in (443, 8443):
            connection = http.client.HTTPSConnection(host, self.port, timeout=self.TIMEOUT)
        else:
            connection = http.client.HTTPConnection(host, self.port, timeout=self.TIMEOUT)
        self.logger.debug("Connecting")
        try:
            connection.connect()
            args = []
            if self.BODY or self.HEADERS:
                args.append(self.BODY)
            if self.HEADERS:
                args.append(self.HEADERS)
            self.logger.debug("Requesting")
            connection.request(self.METHOD, self.PATH, *args)
            self.logger.debug("Reading response")
            resp = connection.getresponse()
        except (socket.error, http.client.error) as e:
            self.logger.warning("Error on http detection request: %s", str(e))
        else:
            self.logger.debug("Response status code %s" % resp.status)
            if resp.status == 200:
                self.logger.debug("Reading body")
                try:
                    answer_body = resp.read()
                except (socket.error, http.client.error) as e:
                    self.logger.warning("Error on http detection body read: %s", str(e))
                else:
                    self.logger.debug("Decoding json")
                    try:
                        decoded = json.loads(answer_body)
                        if type(decoded) != dict:
                            raise TypeError("Response is not json dictionary")
                    except (ValueError, TypeError) as e:
                        self.logger.debug("HTTP detector got non json response: %s", str(e))
                    else:
                        self.logger.debug("Checking status dictionary")
                        self.logger.debug('Response: %s', pprint.pformat(decoded))
                        if isinstance(decoded, dict):
                            return decoded.get(self.RESPONSE_FIELD_NAME)


    def parent_stop(self):
        return self.parent and self.parent.stop_flag

    def detect(self, already_know_ip=None, non_default_port=None):
        if non_default_port:
            self.port = non_default_port
        else:
            self.port = self.PORT
        if not already_know_ip:
            if self.ip:
                network = findssh.netfromaddress(self.ip)
                hosts = findssh.threadpool.get_hosts(network, self.port, self.FINDSSH_SERVICE, self.TIMEOUT)
            else:
                hosts = []
        else:
            hosts = [(already_know_ip, self.port)]
        self.logger.debug("All detected hosts: " + str(hosts))
        if self.profile:
            VID, PID = self.profile.get('vids_pids', [("XXXX","XXXX")])[0]
        else:
            VID, PID = "XXXX", "XXXX" #dummy values only for debug mode
        discovered_printers = []
        # TODO refactor all the mess with overload of response of proccess_hosts
        for ip, host_match_data in self.process_hosts(hosts).items():
            discovered_printers.append(self.parse_matched_hosts(ip, VID, PID, host_match_data))
        self.discovered_printers = discovered_printers

    def parse_matched_hosts(self, IP, VID, PID, match_data):
        self.logger.debug("Detected: " + str(match_data))
        printer = {'VID': VID, 'PID': PID}
        if isinstance(match_data, dict):
            printer.update(match_data)
        elif isinstance(match_data, str):
            printer['SNR'] = match_data
            printer['IP'] = IP
        return printer

if __name__ == "__main__":

    detector = HTTPDetector(None)
    detector.detect()
    pprint.pprint(detector.discovered_printers)
