#
# Copyright (c) 2011, EPFL (Ecole Politechnique Federale de Lausanne)
# All rights reserved.
#
# Created by Marco Canini, Daniele Venzano, Dejan Kostic, Jennifer Rexford
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   -  Redistributions of source code must retain the above copyright notice,
#      this list of conditions and the following disclaimer.
#   -  Redistributions in binary form must reproduce the above copyright notice,
#      this list of conditions and the following disclaimer in the documentation
#      and/or other materials provided with the distribution.
#   -  Neither the names of the contributors, nor their associated universities or
#      organizations may be used to endorse or promote products derived from this
#      software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

import types

from of_controller.of_controller import Controller

import logging
log = logging.getLogger("nice.mc.strategy")

ctxt = None

class HeuristicMicroFlowIndependence:
	def __init__(self, model_checker):
		global ctxt
		self.model_checker = model_checker
		self.cur_node = None
		self.cur_flow = 0
		self.ctrl = None
		self.flows = {}
		self.next_cur_flow = 0
		ctxt = self

	def getEnabledActions(self):
		# filter by next_cur_flow
		# loop next_cur_flow until len(filtered_actions) > 0
		actions = self.model_checker.model.getAllEnabledActions()
		if len(actions) == 0:
			return actions
		filtered_actions = []
		while len(filtered_actions) == 0:
			self.next_cur_flow = (self.next_cur_flow + 1) % (len(self.flows) + 1)
			for a in actions:
				if a.flow == self.next_cur_flow:
					filtered_actions.append(a)
		return filtered_actions
	
	def onEnableAction(self, node, action):
		action.flow = self.cur_flow
		return action
	
	def chooseAction(self, enabled_actions):
		action = enabled_actions.pop(0)
		self.cur_node = self.model_checker.model.nodes[action.node_name]
		self.cur_flow = action.flow
		return action

	def visitModel(self, model):
		self.ctrl = None
		for n in model.getNodes():
			log.debug("microflow %s" % str(n.__class__))
			n.enqueuePacket = types.MethodType(enqueuePacket, n, n.__class__)
			if isinstance(n, Controller):
				if not self.ctrl is None:
					raise NotImplementedError
				self.ctrl = n

	def trackMicroflow(self, packet):
		# is this enqueue action caused by a previous packet?
		if self.cur_flow != 0:
			# check packet independence to other active flows
			for idx in self.flows:
				f = self.flows[idx]
				d = self.ctrl.isSameMicroflow(packet, f)
				if d == True:
					# if dep reuse the correct cur_flow
					self.cur_flow = idx
					return
		# if a client sending a new packet or indep, assign a new cur_flow, mark all new enabled actions with new microflow
		log.debug("new microflow %s" % str(packet))
		idx = len(self.flows) + 1
		self.flows[idx] = packet
		self.cur_flow = idx

def enqueuePacket(self, packet, inport):
	log.debug("microflow enqueuePacket")
	ctxt.trackMicroflow(packet)
	self.ports[inport].queueIn(packet)
	self.enableAction("process_packet", skip_dup=True)
