# TODO: test *2

import time
import socket
import netaddr
import re

from JumpScale import j

IPBLOCKS = re.compile("(^|\n)(?P<block>\d+:.*?)(?=(\n\d+)|$)", re.S)
IPMAC = re.compile("^\s+link/\w+\s+(?P<mac>(\w+:){5}\w{2})", re.M)
IPIP = re.compile("^\s+inet\s(?P<ip>(\d+\.){3}\d+)/(?P<cidr>\d+)", re.M)
IPNAME = re.compile("^\d+: (?P<name>.*?)(?=:)", re.M)

def parseBlock(block):
    result = {'ip': [], 'cidr': [], 'mac': '', 'name': ''}
    for rec in (IPMAC, IPNAME):
        match = rec.search(block)
        if match:
            result.update(match.groupdict())
    for mrec in (IPIP, ):
        for m in mrec.finditer(block):
            for key, value in list(m.groupdict().items()):
                result[key].append(value)
    return result


def getNetworkInfo():
    exitcode, output = j.sal.process.execute("ip a", outputToStdout=False)
    for m in IPBLOCKS.finditer(output):
        block = m.group('block')
        yield parseBlock(block)


class NetTools:

    def __init__(self):
        self.__jslocation__ = "j.sal.nettools"
        self.logger = j.logger.get('j.sal.nettools')
        self._windowsNetworkInfo = None

    def tcpPortConnectionTest(self, ipaddr, port, timeout=None):
        conn = None
        try:
            conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            if timeout:
                conn.settimeout(timeout)
            try:
                conn.connect((ipaddr, port))
            except:
                return False
        finally:
            if conn:
                conn.close()
        return True

    def ip_to_num(self, ip="127.0.0.1"):
        """
        Convert an IP string to number
        """
        return int(netaddr.IPAddress(ip))

    def waitConnectionTest(self, ipaddr, port, timeout):
        """
        will return false if not successfull (timeout)
        """
        self.logger.debug("test tcp connection to '%s' on port %s" % (ipaddr, port))
        if ipaddr.strip() == "localhost":
            ipaddr = "127.0.0.1"
        port = int(port)
        start = j.data.time.getTimeEpoch()
        now = start
        remainingtime = (timeout - (now - start)) or 1
        while remainingtime > 0:
            if j.sal.nettools.tcpPortConnectionTest(ipaddr, port, remainingtime):
                return True
            time.sleep(0.1)
            now = j.data.time.getTimeEpoch()
            remainingtime = (timeout - (now - start)) or 1
        return False

    def waitConnectionTestStopped(self, ipaddr, port, timeout):
        """
        will test that port is not active
        will return false if not successfull (timeout)
        """
        self.logger.debug("test tcp connection to '%s' on port %s" % (ipaddr, port))
        if ipaddr.strip() == "localhost":
            ipaddr = "127.0.0.1"
        port = int(port)
        start = j.data.time.getTimeEpoch()
        now = start
        while now < start + timeout:
            if j.sal.nettools.tcpPortConnectionTest(ipaddr, port, 1) is False:
                return True
            now = j.data.time.getTimeEpoch()
        return False

    def checkUrlReachable(self, url, timeout=5):
        """
        raise operational critical if unreachable
        return True if reachable
        """
        # import urllib.request, urllib.parse, urllib.error
        try:
            import urllib.request
            import urllib.parse
            import urllib.error
        except:
            import urllib.parse as urllib

        try:
            code = urllib.request.urlopen(url, timeout=timeout).getcode()
        except Exception:
            j.errorconditionhandler.raiseOperationalCritical("Url %s is unreachable" % url)

        if code != 200:
            j.errorconditionhandler.raiseOperationalCritical("Url %s is unreachable" % url)
        return True

    def checkListenPort(self, port):