#!/usr/bin/python3.5

class Interval:
	def __init__(self, low, high):
		self.low = low
		self.high = high
	
	def __str__(self):
		if self.low == self.high:
			return str(self.low)
		else:
			return "{0}-{1}".format(self.low, self.high)
	
	def __repr__(self):
		return str(self)
	
	def __eq__(self, other):
		return self.low == other.low and self.high == other.high
	
	def __hash__(self):
		return hash(self.low) * 37 + hash(self.high)
	
	def __len__(self):
		return self.high - self.low + 1
	
	def __iter__(self):
		return iter(range(self.low, self.high + 1))
		
	def IsSingleton(self):
		return self.low == self.high

class Prefix(Interval):
	def __init__(self, low, wild):
		Interval.__init__(self, low, low + (0xFFFFFFFF >> wild))
		self.wild = wild
	
	def __str__(self):
		return "{0}/{1}".format(AddressString(self.low), self.wild)

class Rule:
	def __init__(self, bounds, color):
		self.bounds = bounds
		self.color = color
	
	def __str__(self):
		return "{0} -> {1}".format(",".join([str(b) for b in self.bounds]), self.color)

	def __repr__(self):
		return str(self)

def ParseIP(address):
	(word, m) = address.split('/')
	(a, b, c, d) = word.split('.')
	parts = [a, b, c, d]
	parts = [int(p) for p in parts]
	l = 0
	for p in parts:
		l = l * 256 + p
	#h = l + (0xFFFFFFFF >> int(m))
	#return Interval(l, h)
	return Prefix(l, int(m))
	
def ParsePort(port):
	(l, h) = port.split(':')
	return Interval(int(l), int(h))

def ParseProto(proto):
	(p, m) = proto.split('/')
	if m == '0xFF':
		h = int(p, 16)
		return Interval(h, h)
	else:
		return Interval(0, 255)
	
def ParseClassBenchLine(line, action):
	parts = line[1:].split('\t')
	sip = ParseIP(parts[0])
	dip = ParseIP(parts[1])
	sp = ParsePort(parts[2])
	dp = ParsePort(parts[3])
	proto = ParseProto(parts[4])
	bounds = [sip, dip, sp, dp, proto]
	return Rule(bounds, action)

def ReadClassBench(filename):
	with open(filename, 'r') as reader:
		return [ParseClassBenchLine(line.strip(), i) for (i, line) in enumerate(reader)]

def AddressString(point):
	bytes = [str((point >> (8 * i)) & 0xFF) for i in (3, 2, 1, 0)]
	return ".".join(bytes)

def IPString(address):
	word = AddressString(address.low)
	diffs = address.low ^ address.high
	w = 0
	while diffs:
		w = w + 1
		diffs = diffs >> 1
	return "{0}/{1}".format(word, w)

def PortString(port):
	return "{0}:{1}".format(port.low, port.high)

def ProtoString(proto):
	if proto.low == proto.high:
		return "{0}/0xFF".format(proto.low)
	else:
		return "0x00/0x00"

def PriorityString(color):
	return str(color)

def ActionString(color,maxColor):
        #color=0
        if color < (maxColor/2):
                return "drop"
        else:
                return "normal"

def WriteClassBench(filename, rulelist):
	with open(filename, 'w') as writer:
		for rule in rulelist:
			sa = IPString(rule.bounds[0])
			da = IPString(rule.bounds[1])
			sp = PortString(rule.bounds[2])
			dp = PortString(rule.bounds[3])
			p = ProtoString(rule.bounds[4])
			writer.write("@{0}\n".format("\t".join([sa, da, sp, dp, p])))

def WriteMSU(filename, rulelist):
	with open(filename, 'w') as writer:
		writer.write("!MSU Format: Autogenerated\n")
		writer.write("SA,DA,SP,DP,Proto\n")
		writer.write("0:4294967295,0:4294967295,0:65535,0:65535,0:255\n")
		for rule in rulelist:
			writer.write("{0},{1}\n".format(",".join([PortString(f) for f in rule.bounds]), rule.color))

def WriteOpenFlow(filename, rulelist):
	with open(filename, 'w') as writer:
		for rule in rulelist:
			pr = PriorityString(rule.color)			
			sa = IPString(rule.bounds[0])
			da = IPString(rule.bounds[1])
			sp = str(rule.bounds[2].high)
			dp = str(rule.bounds[3].high)
			p = ProtoString(rule.bounds[4])
			act = ActionString(rule.color,len(rulelist))
			writer.write("{0}\n".format(" ".join(["flow br0 add priority="+pr,"ip","nw_src="+sa,"nw_dst="+da,"tp_src="+sp,"tp_dst="+dp,"nw_proto="+p,"action="+act])))
	
