# (c) Roland van Laar

from exodus.models import Node, Interface, PublicAP
from exodus.settings import MASTERIP_NETMASK

def newSSIDName(node, nic, desc):
	"""Generates a new ssid name for a new wifi NIC"""

	# nic is used instead of nic.iface, because string nic is passed on, 
	# instead of object nic.

	return "%s-%s.%s.%s" % (desc, nic, node.name, node.network.name)

#
# Taken from lvoege@gmail.com's getrange.py
#
def parse_addr(s):
	"""Remember when using an address has a section which is higher than
	255, such as 172.16.0.256, the ip address from show_addr turns out as
	172.16.1.0
	"""
	f = s.split('.')
	return (long(f[0]) << 24L) + \
		(long(f[1]) << 16L) + \
		(long(f[2]) << 8L) + \
		long(f[3])

def show_addr(a):
	return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)

def netmask2subnet(s):
	"""IPv4 netmask to subnet"""
	if s < 0 :
		raise ValueError, 'subnet is too small'
	return (0xffffffff << (32 - s))

def getSubnet(netmask):
	return(show_addr(netmask2subnet(netmask)))

def network(address, netmask):
	"""IPv4 network address when address and netmask are given"""
	return(parse_addr(address) & netmask2subnet(netmask))

def broadcast(address, netmask):
	"""IPv4 network address when address and netmask are given"""
	if netmask > 32:
		raise ValueError, 'netmask too large'
	return(parse_addr(address) | 0xffffffff >> netmask)

def getNetwork(address, netmask):
	return(show_addr(network(address,netmask)))

def getBroadcast(address, netmask):
	return(show_addr(broadcast(address,netmask)))

#XXX: free_masterip/addInterlinkIP should be more general by writing a function
#     which finds a range based on a given range, taken ip/netmask and 
#     requested subnet

def free_masterip(city_network, netmask = None):
	if netmask == None:
		netmask = MASTERIP_NETMASK

	if netmask < 0 or netmask > 32 :
		raise ValueError, 'netmask out of bounds'	

	taken = {}

	for node in Node.objects.all():
		addr = network(node.masterip, netmask)
		taken[addr] = 1
    
	numaddrs = 1 << (32 - netmask)

	#XXX: No out of bond checking done yet
	i = parse_addr(city_network.ipspacestart)
	while taken.has_key(i):
		i = i + numaddrs
    
    # go from network address to a valid ip.
	return show_addr(i+1)

#
# XXX: Needs merging with freeInterlinkIP as interface could have both AP, 
# interlink defined on one link
def freePublicAPIP(masterLink, netmask):
	taken = {}

	for interface in Interface.objects.filter(link=masterLink):
		#Grr, needs all interfaces which are master requires a bit of a hack
		addr = network(interface.ip, interface.netmask)
		addrMax = broadcast(interface.ip, interface.netmask)
		while addr < addrMax:
			taken[addr] = 1
			addr = addr + 1

	for accessPoint in PublicAP.objects.filter(iface=masterLink):
		#Grr, needs all interfaces which are master requires a bit of a hack
		addr = network(accessPoint.ip, accessPoint.netmask)
		addrMax = broadcast(accessPoint.ip, accessPoint.netmask)
		while addr < addrMax:
			taken[addr] = 1
			addr = addr + 1

	#Should be dynamic based on the number of hosts in here
	size = netmask
	numaddrs = 1 << (32 - size)
	i = network(masterLink.ip,masterLink.netmask)
	while taken.has_key(i):
		i = i + numaddrs
	
	return show_addr(i)

def freeInterlinkIP(masterLink):
	taken = {}
	
	for interface in Interface.objects.filter(node=masterLink.node):
		#Grr, needs all interfaces which are master requires a bit of a hack
		addr = network(interface.ip, interface.netmask)
		taken[addr] = 1

	#Should be dynamic based on the number of hosts in here
	size = masterLink.netmask
	numaddrs = 1 << (32 - size)
	i = network(masterLink.node.masterip,24) + 4
	while taken.has_key(i):
		i = i + numaddrs
	
	return show_addr(i)

def addInterlinkIP(masterLink):
	taken = {}
	
	for interface in Interface.objects.filter(link=masterLink):
		#Grr, needs all interfaces which are master requires a bit of a hack
		addr = parse_addr(interface.ip)
		taken[addr] = 1

	size = 32
	numaddrs = 1 << (32 - size)
	i = network(masterLink.ip, masterLink.netmask) + 1
	while taken.has_key(i):
		i = i + 1
	
	return show_addr(i)

def link_is_valid(link1, link2):
	if not link_has_compat_type(link1, link2):
		return False

def link_is_wireless(link1):
	wireless = ('11a', '11b', '11g')
	if link1.type in wireless:
		return True

def link_has_compat_type(link1, link2):
	# if this is a link to self, the link is always valid
	if link1 == link2:
		return True
	# link types must the same
	if link1.type == link2.type:
		return True
	# or link types must be compatible
	compat = ('11b', '11g')
	if link1.type in compat and link2.type in compat:
		return True

def link_is_not_to_itself(link1, link2):
	# check if a link is not going itself
	if link1.node != link2.node:
		return True

