#!/usr/bin/python
#
# Copyright (c) 2017 Mellanox Technologies. All rights reserved.
#
# This Software is licensed under one of the following licenses:
#
# 1) under the terms of the "Common Public License 1.0" a copy of which is
#    available from the Open Source Initiative, see
#    http://www.opensource.org/licenses/cpl.php.
#
# 2) under the terms of the "The BSD License" a copy of which is
#    available from the Open Source Initiative, see
#    http://www.opensource.org/licenses/bsd-license.php.
#
# 3) under the terms of the "GNU General Public License (GPL) Version 2" a
#    copy of which is available from the Open Source Initiative, see
#    http://www.opensource.org/licenses/gpl-license.php.
#
# Licensee has the right to choose one of the above licenses.
#
# Redistributions of source code must retain the above copyright
# notice and one of the license notices.
#
# Redistributions in binary form must reproduce both the above copyright
# notice, one of the license notices in the documentation
# and/or other materials provided with the distribution.
#

import sys
import os
if os.path.exists('/usr/share/pyshared'):
    sys.path.append('/usr/share/pyshared')
if os.path.exists('/usr/lib/python2.7/site-packages'):
    sys.path.append('/usr/lib/python2.7/site-packages')
import ast
from optparse import OptionParser
from dcbnetlink import DcbController
from collections import defaultdict
from subprocess import Popen, PIPE
from tc_wrap import *

DCB_CAP_DCBX_VER_IEEE = 0x8
NUMBER_OF_QCN_PARAMS = 12
NUMBER_OF_QCN_STATS = 10
NUMBER_OF_TCS = 8

list_params = ["rpg_enable", "rppp_max_rps", "rpg_time_reset", "rpg_byte_reset", "rpg_threshold", "rpg_max_rate", "rpg_ai_rate", "rpg_hai_rate", "rpg_gd", "rpg_min_dec_fac", "rpg_min_rate", "cndd_state_machine"]
list_statistics = ["rppp_rp_centiseconds", "rppp_created_rps", "ignored_cnm", "estimated_total_rate", "cnms_handled_successfully", "min_total_limiters_rate", "max_total_limiters_rate"]


class QCN:
	def get(self):
		raise "Not implemented"
	def set(self, qcn):
		raise "Not implemented"
	def getstats(self):
		raise "Not implemented"


class QCNNL(QCN):
	def __init__(self,ctrl):
		self.ctrl = ctrl
	def get(self):
		return ctrl.get_ieee_qcn()

	def set(self, qcn):
		ctrl.set_ieee_qcn(qcn)

	def get_statistics(self):
		return ctrl.get_ieee_qcnstats()

class QCNSysfs(QCN):
	def __init__(self, path):
		self.path = path

	def get(self):
		qcn_params = []
		f = open(self.path, "r")
		for item in f.read().split():
			if (item == "priority" or item == "|priority"):
				lastItem = item
				continue
			elif (item == ":" or item == "|"):
				continue
			elif (lastItem == "priority" or lastItem == "|priority"):
				lastItem = ""
				continue
			qcn_params.append(int(item))
		f.close()
		return qcn_params

	def set(self, qcn):
		f = open(self.path, "w")
		f.write(" ".join(str(r) for r in qcn))
		f.close()

class QCNStatsSysfs(QCN):
	def __init__(self, path):
		self.path = path

	def get_statistics(self):
		qcn_stats = []
		f = open(self.path, "r")
		for item in f.read().split():
			if (item == "priority" or item == "|priority"):
				lastItem = item
				continue
			elif (item == ":" or item == "|"):
				continue
			elif (lastItem == "priority" or lastItem == "|priority"):
				lastItem = ""
				continue
			qcn_stats.append(int(item))
		f.close()
		return qcn_stats

def pretty_print_qcn(qcn, values):
	# Deal with an additions of qcn parameters which were not added to the tool #
	additions = (len(qcn) - (NUMBER_OF_TCS * NUMBER_OF_QCN_PARAMS)) / NUMBER_OF_TCS

	for tc in range(NUMBER_OF_TCS):
		print(("priority %d:" % tc))
		for val in values:
			if isinstance(qcn, list):
				print(("\t%s: %d" % (val,qcn[int((tc * (len(values) + additions)) + values.index(val))])))
			else:
				print(("\t%s: %d" % (val,qcn[values.index(val)][tc])))
		print("")


def update_paremeter(qcn, listPerTc, param):
	# Deal with an additions of qcn parameters which were not added to the tool #
	additions = (len(qcn) - (NUMBER_OF_TCS * NUMBER_OF_QCN_PARAMS)) / NUMBER_OF_TCS

	for tc in range(NUMBER_OF_TCS):
		if (listPerTc[tc] != -1):
			if isinstance(qcn, list):
				qcn[(tc * (NUMBER_OF_QCN_PARAMS + additions)) + list_params.index(param)] = listPerTc[tc]
			else:
				qcn[list_params.index(param)][tc] = listPerTc[tc]
	return qcn



parser = OptionParser(usage="%prog -i <interface> [options]", version="%prog 1.0")

parser.add_option("-i", "--interface", dest="intf",
			help="Interface name")
parser.add_option("-g", "--get_type", dest="type", choices=["parameters", "statistics"],
			help="Type of information to get: \"statistics\" or \"parameters\"")

parser.add_option("--rpg_enable", nargs=8, type="int", dest="rpg_enable_list",
			help="Set value of rpg_enable according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rppp_max_rps", nargs=8, type="int", dest="rppp_max_rps_list",
			help="Set value of rppp_max_rps according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rpg_time_reset", nargs=8, type="int", dest="rpg_time_reset_list",
			help="Set value of rpg_time_reset according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rpg_byte_reset", nargs=8, type="int", dest="rpg_byte_reset_list",
			help="Set value of rpg_byte_reset according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rpg_threshold", nargs=8, type="int", dest="rpg_threshold_list",
			help="Set value of rpg_threshold according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rpg_max_rate", nargs=8, type="int", dest="rpg_max_rate_list",
			help="Set value of rpg_max_rate according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rpg_ai_rate", nargs=8, type="int", dest="rpg_ai_rate_list",
			help="Set value of rpg_ai_rate according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rpg_hai_rate", nargs=8, type="int", dest="rpg_hai_rate_list",
			help="Set value of rpg_hai_rate according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rpg_gd", nargs=8, type="int", dest="rpg_gd_list",
			help="Set value of rpg_gd according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rpg_min_dec_fac", nargs=8, type="int", dest="rpg_min_dec_fac_list",
			help="Set value of rpg_min_dec_fac according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--rpg_min_rate", nargs=8, type="int", dest="rpg_min_rate_list",
			help="Set value of rpg_min_rate according to priority, use spaces between values and -1 for unknown values.")

parser.add_option("--cndd_state_machine", nargs=8, type="int", dest="cndd_state_machine_list",
			help="Set value of cndd_state_machine according to priority, use spaces between values and -1 for unknown values.")

(options, args) = parser.parse_args()

if len(args) > 0:
    print("Bad arguments")
    parser.print_usage()
    sys.exit(1)

if (options.intf == None):
	print("Interface name is required")
	parser.print_usage()
	sys.exit(1)

tmp_list = list(ast.literal_eval(options.__str__()).values())

if (tmp_list.count(None) == len(tmp_list) - 1):
	print("No action was asked for, choose -h to see options")
	parser.print_usage()
	sys.exit(1)

qcn_path = "/sys/class/net/" + options.intf + "/qos/qcn"
qcn_stats_path = "/sys/class/net/" + options.intf + "/qos/qcn_stats"

ctrl = DcbController(options.intf)

try:
	qcn_main = None
	if (not os.path.exists(qcn_path)):
		qcn_main = QCNNL(ctrl)
	else:
		if (options.type == "statistics"):
			qcn_main = QCNStatsSysfs(qcn_stats_path)
		else:
			qcn_main = QCNSysfs(qcn_path)

	if (options.type == "parameters"):
		qcn = qcn_main.get()
		pretty_print_qcn(qcn, list_params)
	elif (options.type == "statistics"):
		qcn = qcn_main.get_statistics()
		pretty_print_qcn(qcn, list_statistics)

	else:

		qcn = qcn_main.get()

		if (options.rpg_enable_list != None):
			qcn = update_paremeter(qcn, options.rpg_enable_list, "rpg_enable")

		if (options.rppp_max_rps_list != None):
			qcn = update_paremeter(qcn, options.rppp_max_rps_list, "rppp_max_rps")

		if (options.rpg_time_reset_list != None):
			qcn = update_paremeter(qcn, options.rpg_time_reset_list, "rpg_time_reset")

		if (options.rpg_byte_reset_list != None):
			qcn = update_paremeter(qcn, options.rpg_byte_reset_list, "rpg_byte_reset")

		if (options.rpg_threshold_list != None):
			qcn = update_paremeter(qcn, options.rpg_threshold_list, "rpg_threshold")

		if (options.rpg_max_rate_list != None):
			qcn = update_paremeter(qcn, options.rpg_max_rate_list, "rpg_max_rate")

		if (options.rpg_ai_rate_list != None):
			qcn = update_paremeter(qcn, options.rpg_ai_rate_list, "rpg_ai_rate")

		if (options.rpg_hai_rate_list != None):
			qcn = update_paremeter(qcn, options.rpg_hai_rate_list, "rpg_hai_rate")

		if (options.rpg_gd_list != None):
			qcn = update_paremeter(qcn, options.rpg_gd_list, "rpg_gd")

		if (options.rpg_min_dec_fac_list != None):
			qcn = update_paremeter(qcn, options.rpg_min_dec_fac_list, "rpg_min_dec_fac")

		if (options.rpg_min_rate_list != None):
			qcn = update_paremeter(qcn, options.rpg_min_rate_list, "rpg_min_rate")

		if (options.cndd_state_machine_list != None):
			qcn = update_paremeter(qcn, options.cndd_state_machine_list, "cndd_state_machine")

		qcn_main.set(qcn)


except:
	print("QCN is not supported on your system!")
	qcn = []


