#!/usr/bin/python
import os
import sys
from xml.dom import minidom
import pdb
import traceback
import re

class ar_record_t:
	def __init__(self, release_idx, bl2_ar_ver, fip_ar_ver, fit_ar_ver):
		self.idx = release_idx
		self.bl2 = bl2_ar_ver
		self.fip = fip_ar_ver
		self.fit = fit_ar_ver

	def to_header_data(self, ident=""):
		code = "%sAR_ENTRY(%d, %d, %d, %d)" % (ident, self.idx, self.bl2, self.fip, self.fit)
		return code

class ar_table_t:

	def __init__(self, conf_file):
		self.conf_file = conf_file
		self.parse_result = ""
		self.release_index_list = []
		self.ar_record_list = []

	def generate_ar_table_code(self):
		code = ""
		code += "/* \n"
		code +=	" * This file is auto-generated by ar-table tool\n"
		code +=	" * please do not modify this file manually\n"
		code +=	" */\n"
		code +=	"#include <ar_table.h>\n"
		code +=	"const uint32_t mtk_ar_table_num_entry = %d;\n" % len(self.ar_record_list)
		code += "const struct ar_table_entry mtk_ar_table[] = {\n"
		code += self.to_header_data(ident="\t")
		code += "};\n"
		return code

	def generate_ar_conf_code(self):
		code = ""
		code +=	"TFW_NVCTR_VAL\t:=\t%d\n" % self.ar_record_list[-1].fip
		code +=	"NTFW_NVCTR_VAL\t:=\t%d\n" % self.ar_record_list[-1].fip
		code +=	"BL2_AR_VER\t:=\t%d\n" % self.ar_record_list[-1].bl2
		code += "FIT_AR_VER\t:=\t%d\n" % self.ar_record_list[-1].fit
		return code

	def to_header_data(self, ident=""):
		code = ""
		for i in range(0, len(self.ar_record_list)):
			line = self.ar_record_list[i].to_header_data()
			code += (ident+line+","+"\n")
		return code

	def check_and_set_release_index(self, release_index):
		if release_index not in self.release_index_list:
			self.release_index_list.append(release_index)
			return True
		else:
			return False

	def check_data_exist_in_ar_entry(self, xml_node, entry_id, name):
		i = entry_id
		datalist = xml_node.getElementsByTagName(name)
		if not datalist:
			return False
		return True

	def get_data_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True):
		i = entry_id
		datalist = xml_node.getElementsByTagName(name)
		if not datalist:
			if print_err is True:
				print("XML parse fail in ar_entry[%d]:" % i)
				print("    Chilld node '%s' not exist" % name)
			return None
		data = None
		if len(datalist) != 1:
			if print_err is True:
				print("XML parse fail in ar_entry[%d]:" % i)
				print("    Duplicate '%s' node exist" % name)
			return None
		datanode = datalist[0].firstChild
		if not datanode:
			if print_err is True:
				print("XML parse fail in ar_entry[%d].%s:" % (i, name))
				print("    '%s' data not exist" % name)
			return None
		if datanode.nodeType != datanode.TEXT_NODE:
			if print_err is True:
				print("XML parse fail in ar_entry[%d].%s:" % (i, name))
				print("    '%s' data not exist" % name)
			return None
		return str(datanode.data)

	def get_int_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True):
		data = self.get_data_by_name_from_ar_entry(xml_node, entry_id, name, print_err)
		if data:
			data = data.strip()
			if not data.isdigit():
				if print_err is True:
					print("XML parse fail in ar_entry[%d].%s:" % (i, name))
					print("    '%s' must be an integer" % name)
				return None
			return data
		return None

	def show(self):
		code = self.to_header_data()
		print(code)

	def xml_debug_show(self, line, column):
		f = open(self.conf_file, "r")
		if not f:
			sys.stderr.write("Unable to open antirollback config file '%s'\n" % self.conf_file)
			raise
		xml_data = f.read()
		xml_lines = xml_data.split("\n")
		f.close()
		print("input xml fail at line %d, column %d" % (line, column))
		if line < 2:
			show_lines = [xml_lines[line]]
		elif line+2 >= len(xml_lines):
			show_lines = [xml_lines[line]]
		else:
			show_lines = xml_lines[line-1:line+1]
		for line in show_lines:
			print(line)

	def parse(self):
		line_num = 0
		data = None
		try:
			f = open(self.conf_file, "r")
			if not f:
				raise
			f.close()
		except:
			sys.stderr.write("Unable to open antirollback config file '%s'\n" % self.conf_file)
			return 1
		try:
			xmldoc = minidom.parse(self.conf_file)
			entrylist = xmldoc.getElementsByTagName('ar_entry')

			for i in range(0, len(entrylist)):
				entrynode = entrylist[i]
				data = self.get_int_by_name_from_ar_entry(entrynode, i, "RELEASE_INDEX")
				if not data:
					return 1
				if data:
					data = data.strip()
					if self.check_and_set_release_index(int(data)) is False:
						print("XML parse fail in ar_entry[%d].RELEASE_INDEX:" % i)
						print("    'RELEASE_INDEX' value have been used by others entry")
						return 1

				release_idx = int(data)
				bl2_ar_ver = 0
				fip_ar_ver = 0
				fit_ar_ver = 0

				bl2_exist = self.check_data_exist_in_ar_entry(entrynode, i, "BL2_AR_VER")
				fip_exist = self.check_data_exist_in_ar_entry(entrynode, i, "FIP_AR_VER")
				fit_exist = self.check_data_exist_in_ar_entry(entrynode, i, "FIT_AR_VER")

				if (not bl2_exist) and (not fip_exist) and (not fit_exist):
					print("NULL...")
					break
				else:
					if not bl2_exist:
						print("XML parse fail in ar_entry[%d]:" % i)
						print("    Chilld node '%s' not exist" % "BL2_AR_VER")
						return 1
					if not fip_exist:
						print("XML parse fail in ar_entry[%d]:" % i)
						print("    Chilld node '%s' not exist" % "FIP_AR_VER")
						return 1
					if not fit_exist:
						print("XML parse fail in ar_entry[%d]:" % i)
						print("    Chilld node '%s' not exist" % "FIT_AR_VER")
						return 1

				data = self.get_data_by_name_from_ar_entry(entrynode, i, "BL2_AR_VER")
				if data:
					bl2_ar_ver = int(data)
				data = self.get_data_by_name_from_ar_entry(entrynode, i, "FIP_AR_VER")
				if data:
					fip_ar_ver = int(data)
				data = self.get_data_by_name_from_ar_entry(entrynode, i, "FIT_AR_VER")
				if data:
					fit_ar_ver = int(data)

				ar_record = ar_record_t(release_idx, bl2_ar_ver, fip_ar_ver, fit_ar_ver)
				self.ar_record_list.append(ar_record)

			print("Get %d record in anti-rollback release table" % len(self.ar_record_list))

		except:
			sys.stderr.write("Unable to parse xml '%s'\n" % self.conf_file)
			crash_info = traceback.format_exc()
			m = re.search("ExpatError: mismatched tag: line (.+), column (.+)", crash_info)
			if m:
				line = int(m.group(1))
				column = int(m.group(2))
				self.xml_debug_show(line, column)
				print(m.group(0))
			else:
				print(crash_info)
			return 1


		return 0

def main(argc, argv):
	if argc != 4:
		sys.stdout.write("ar-table [create_ar_table|create_ar_conf] $(input_config) $(output_file)\n")
		return 1
	ar_table = ar_table_t(argv[2])
	if ar_table.parse() != 0:
		return 1

	if argv[1] == "create_ar_table":
		code = ar_table.generate_ar_table_code()
		print("(%s) --> (%s)" % (argv[2], argv[3]))
		#print(code)
		f = open(argv[3], "w")
		f.write(code)
		f.close()
		return 0
	elif argv[1] == "create_ar_conf":
		code = ar_table.generate_ar_conf_code()
		print("(%s) --> (%s)" % (argv[2], argv[3]))
		#print(code)
		f = open(argv[3], "w")
		f.write(code)
		f.close()
		return 0
	else:
		print("Unknow option '%s'" % argv[1])
		return 1


if __name__ == '__main__':
    sys.exit(main(len(sys.argv), sys.argv))

