#
# 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 nox.lib.openflow as openflow

IN_PORT = 'in_port'
AP_SRC = 'ap_src'
AP_DST = 'ap_dst'
DL_SRC = 'dl_src'
DL_DST = 'dl_dst'
DL_VLAN = 'dl_vlan'
DL_VLAN_PCP = 'dl_vlan_pcp'
DL_TYPE = 'dl_type'

# X-CHANGE
DL_VER_NUM = 'dl_ver_num'
DL_NEXT_ENTRY_ATTR = 'dl_next_entry_attr'
DL_WAIT_BIT = 'dl_wait_bit'
DL_NEXT_ENTRY_VN = 'dl_next_entry_vn'

NW_SRC = 'nw_src'
NW_SRC_N_WILD = 'nw_src_n_wild'
NW_DST = 'nw_dst'
NW_DST_N_WILD = 'nw_dst_n_wild'
NW_PROTO = 'nw_proto'
NW_TOS = 'nw_tos'
TP_SRC = 'tp_src'
TP_DST = 'tp_dst'
GROUP_SRC = 'group_src'
GROUP_DST = 'group_dst'
N_TABLES = 'n_tables'
N_BUFFERS = 'n_bufs'
CAPABILITES = 'caps'
ACTIONS = 'actions'
PORTS = 'ports'
PORT_NO = 'port_no'
SPEED = 'speed'
CONFIG = 'config'
STATE = 'state'
CURR = 'curr'
ADVERTISED = 'advertised'
SUPPORTED = 'supported'
PEER = 'peer'
HW_ADDR = 'hw_addr'
CONTINUE = 0
STOP = 1

class Component:

	def __init__(self, ctxt):
		self.ctxt = ctxt
		self.packet_in_cb = None
		self.datapath_leave_cb = None
		self.datapath_join_cb = None
		self.flow_removed_cb = None
# X-CHANGE
		# Xiaoye: init value of the version number
		self.vn = 1000

	def register_for_packet_in(self, func):
		self.packet_in_cb = func

	def register_for_port_stats_in(self, func):
		self.port_stats_in_cb = func

	def register_for_datapath_leave(self, func):
		self.datapath_leave_cb = func

	def register_for_datapath_join(self, func):
		self.datapath_join_cb = func

	def register_for_flow_removed(self, func):
		self.flow_removed_cb = func

	def post_callback(self, timeout, func):
		if (self.ctxt != None):
			self.ctxt.post_callback(timeout, func)


	# Xiaoye: API to install the path in cmd_argqueue
	def process_whole_path(self, cmd_argqueue):
		assert len(cmd_argqueue) > 0
		# message for the first switch
		cmd = cmd_argqueue.pop(0)

		# not the last command 
		while len(cmd_argqueue) > 0:
			cmd2 = cmd_argqueue.pop(0)

			# Xiaoye: packet-out message
			if cmd['name'] == 'PKTO':
# XXX-CHANGE
				self.send_openflow(cmd['dp_id'], cmd['buffer_id'], cmd['packet'], cmd['actions'], cmd['inport'], wait_bit = True, next_entry_attr = cmd2['attrs'], next_entry_vn = self.vn, called_by_whole_path=True)
				#self.send_openflow(cmd['dp_id'], cmd['buffer_id'], cmd['packet'], cmd['actions'], cmd['inport'], wait_bit = True, next_entry_attr = cmd2['attrs'], next_entry_vn = self.vn + 1)
			# Xiaoye: flow-mod message
			elif cmd['name'] == 'MFTEadd':
# XXX-CHANGE
				#self.vn += 1
				self.install_datapath_flow(cmd['dp_id'], cmd['attrs'], cmd['idle_timeout'], cmd['hard_timeout'], cmd['actions'], cmd['buffer_id'], cmd['priority'], cmd['inport'], cmd['packet'], self.vn, wait_bit = True, next_entry_attr = cmd2['attrs'], next_entry_vn = self.vn, called_by_whole_path=True)
				#self.install_datapath_flow(cmd['dp_id'], cmd['attrs'], cmd['idle_timeout'], cmd['hard_timeout'], cmd['actions'], cmd['buffer_id'], cmd['priority'], cmd['inport'], cmd['packet'], self.vn, wait_bit = True, next_entry_attr = cmd2['attrs'], next_entry_vn = self.vn + 1)
			cmd = cmd2
			
		# the last command
		if cmd['name'] == 'PKTO':
			self.send_openflow(cmd['dp_id'], cmd['buffer_id'], cmd['packet'], cmd['actions'], cmd['inport'], called_by_whole_path=True)
		elif cmd['name'] =='MFTEadd':
			#self.vn += 1
			self.install_datapath_flow(cmd['dp_id'], cmd['attrs'], cmd['idle_timeout'], cmd['hard_timeout'], cmd['actions'], cmd['buffer_id'], cmd['priority'], cmd['inport'], cmd['packet'], self.vn, called_by_whole_path=True)
# XXX-CHANGE
		self.vn += 1
		return


# X-CHANGE
	def send_openflow(self, dp_id, buffer_id, packet, actions, inport=openflow.OFPP_CONTROLLER, wait_bit = False, next_entry_attr = {}, next_entry_vn = -1, called_by_whole_path=False):
		if isinstance(actions, int) or isinstance(actions, long): # NOX API shortcut, does not follow OpenFlow specs, we translate
			actions = [[openflow.OFPAT_OUTPUT, [0, actions]]]
		if not isinstance(actions, list):
			utils.crash("Unknown action type")

		if (self.ctxt != None):
# X-CHANGE
			self.ctxt.send_openflow(dp_id, buffer_id, packet, actions, inport, wait_bit, next_entry_attr, next_entry_vn)
		#if called_by_whole_path == False:
		#	self.vn += 1

	def send_flow_command(self, dp_id, command, attrs, 
						  priority=openflow.OFP_DEFAULT_PRIORITY,
						  add_args=None,
						  hard_timeout=openflow.OFP_FLOW_PERMANENT, called_by_whole_path=False):
		if (self.ctxt != None):
# X-CHANGE
			self.ctxt.send_flow_command(dp_id, command, attrs, priority, add_args, hard_timeout, self.vn)
		#if called_by_whole_path == False:
		#	self.vn += 1

	def install_datapath_flow(self, dp_id, attrs, idle_timeout, hard_timeout, actions, buffer_id=None, priority=openflow.OFP_DEFAULT_PRIORITY, inport=None, packet=None, vn = -1, wait_bit = False, next_entry_attr = {}, next_entry_vn = -1, called_by_whole_path=False):
		if vn == -1:
			vn = self.vn

		if (self.ctxt != None):
# X-CHANGE
			self.ctxt.install_datapath_flow(dp_id, attrs, idle_timeout, hard_timeout, actions, buffer_id, priority, inport, packet, vn, wait_bit, next_entry_attr, next_entry_vn)
		#if called_by_whole_path == False:
		#	self.vn += 1

	def delete_strict_datapath_flow(self, dp_id, attrs, priority=openflow.OFP_DEFAULT_PRIORITY, called_by_whole_path=False):
		if (self.ctxt != None):
# X-CHANGE
			self.ctxt.delete_strict_datapath_flow(dp_id, attrs, priority, self.vn)
		#if called_by_whole_path == False:
		#	self.vn += 1

	def __getstate__(self):
		
# X-CHANGE
		'''
			pickle the vn
		'''
		#di = {"Component": self.vn}
		#return di

		return {}

