import re
import socket
import struct


class IPRange:

    _ipdigit = r"(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])"
    _ip = r"(%s\.){3}%s" % (_ipdigit, _ipdigit)
    _ip_range = r"(?P<lower>%s)-(?P<upper>%s$)" % (_ip, _ip)
    _ip_mask = r"(?P<ip>%s)/(?P<mask>%s)$" % (_ip, _ip)
    _ip_intmask = r"(?P<ip>%s)/(?P<mask>([0-2]?[0-9])|(3[0-2]))$" % (_ip, )

    _ip = re.compile(_ip+'$')
    _ip_range = re.compile(_ip_range)
    _ip_mask = re.compile(_ip_mask)
    _ip_intmask = re.compile(_ip_intmask)
    
    def __init__(self, descriptor):
        self.parse(descriptor)
        self._descriptor = descriptor

    def __cmp__(self, other):
        c = cmp(self.lower_int, other.lower_int)
        if c:
            return c
        return cmp(self.upper_int, other.upper_int)
    
    def __hash__(self):
        return hash(self.lower_int) ^ hash(self.upper_int)

    def __contains__(self, ip):
        ip = self._ip_to_int(ip)
        return self.lower_int <= ip <= self.upper_int

    def __repr__(self):
        return '<IPRange %s(%i)-%s(%i)>' % (self.lower_str, self.lower_int,
            self.upper_str, self.upper_int)

    def __str__(self):
        return self._descriptor

    def parse(self, ip):
        lower, upper = self._get_boundaries(ip)
        self.lower_str = lower
        self.upper_str = upper
        self.lower_int = self._ip_to_int(lower)
        self.upper_int = self._ip_to_int(upper)

    def _get_boundaries(self, ip):
        """return lower, upper"""
        if self._ip.match(ip):
            return ip, ip
        m = self._ip_range.match(ip)
        if m is not None:
            return m.group('lower'), m.group('upper')
        m = self._ip_mask.match(ip)
        if m is not None:
            ip = m.group('ip')
            mask = m.group('mask')
            ip = self._ip_to_int(ip)
            mask = self._ip_to_int(mask)
            return self._get_boundaries_mask(ip, mask)
        m = self._ip_intmask.match(ip)
        if m is not None:
            ip = m.group('ip')
            ip = self._ip_to_int(ip)
            intmask = int(m.group('mask'))
            mask = (2L**intmask - 1) << (32 - intmask)
            return self._get_boundaries_mask(ip, mask)
        raise ValueError, "The descriptor %r is not valid." % ip
            
    def _get_boundaries_mask(self, ip, mask):
        lower = ip & mask
        upper = ip | ~mask & 0xffffffffL
        return self._int_to_ip(lower), self._int_to_ip(upper)
            
    def _ip_to_int(self, ip_str):
        """returns int of ip
            raises ValueError if ip is not valid
        """
        if ip_str == '255.255.255.255':
            return 0xffffffffL
        try:
            ip = socket.inet_aton(ip_str)
        except socket.error:
            raise ValueError, "%s is not a valid IP address" % (ip_str, )
        return struct.unpack('!L',ip)[0]
        
    def _int_to_ip(self, ip_int):
        return socket.inet_ntoa(struct.pack('!L',ip_int))

