from typing import Dict

from DataClasses.IPEntry import IPEntry
from Handlers.OutputManagers.OutputDispatcher import OutputDispatcher


class IPsFileHandler:
    """
    Scans the IPs input file (located in the scanbox/inputs/ips.txt folder and generated by the GUI).
    Every line should be formatted as explained in the README.MD file of this project
    """
    def __init__(self, output_dispatcher: OutputDispatcher):
        self.output_dispatcher = output_dispatcher
        self.ips_input_file = "scanbox/inputs/ips.txt"
        self.ip_entries: Dict[str, IPEntry] = {}

    def parse_file(self):
        try:
            file = open(self.ips_input_file, "r")
            for line in file:
                self.parse_line(line.rstrip())
            return self.ip_entries
        except IOError:
            print("Unable to open IPs file at \"scanbox/inputs/ips.txt\"")

    def parse_line(self, line):
        try:
            ip_range, ports_range, timeout, expected_response = line.split("/")
            timeout = int(timeout)
            ports = IPsFileHandler.get_ports(ports_range=ports_range)
            ips = IPsFileHandler.get_ips(ip_range)
            for ip in ips:
                if ip not in self.ip_entries:
                    self.ip_entries[ip] = IPEntry(
                        ip=ip,
                        ports=ports.copy(),
                        responses={str(port): expected_response for port in ports},
                        timeouts={str(port): timeout for port in ports},
                    )
                else:
                    self.ip_entries[ip].add_ports(
                        ports=ports.copy(),
                        responses={str(port): expected_response for port in ports},
                        timeouts={str(port): timeout for port in ports},
                    )
        except ValueError:
            pass

    @staticmethod
    def get_ports(ports_range):
        result = []
        ranges = ports_range.split(";")
        p_range: str
        for p_range in ranges:
            if "-" in p_range:
                extrema = p_range.split("-")
                for i in range(int(extrema[0]), int(extrema[1]) + 1):
                    if i not in result and 0 <= i <= 65535:
                        result.append(i)
            else:
                if p_range not in result:
                    result.append(int(p_range))
        return result

    @staticmethod
    def get_ips(ip_range):
        result = []
        a, b, c, d = ip_range.split(".")
        a_list = IPsFileHandler.get_class_list(a)
        b_list = IPsFileHandler.get_class_list(b)
        c_list = IPsFileHandler.get_class_list(c)
        d_list = IPsFileHandler.get_class_list(d)

        for i in a_list:
            for j in b_list:
                for k in c_list:
                    for l in d_list:
                        ip = f"{i}.{j}.{k}.{l}"
                        if ip not in result:
                            result.append(ip)

        return result

    @staticmethod
    def get_class_list(class_range):
        result = []
        ranges = class_range.split(";")
        p_range: str
        for p_range in ranges:
            if "-" in p_range:
                extrema = p_range.split("-")
                for i in range(int(extrema[0]), int(extrema[1]) + 1):
                    if i not in result and 0 <= i <= 255:
                        result.append(i)
            else:
                if p_range not in result:
                    result.append(p_range)
        return result
