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


class bl_ar_table_t:

	def __init__(self, input_file):
		self.input_file = input_file
		self.ar_ver_list = []

	def generate_ar_ver_code(self):
		code = ""
		code += "/* \n"
		code +=	" * This file is auto-generated by ar-tool\n"
		code +=	" * please do not modify this file manually\n"
		code +=	" */\n"
		code +=	"#include <plat/common/platform.h>\n"
		code +=	"const uint32_t bl_ar_ver = %d;\n" % self.ar_ver_list[-1]
		return code

	def generate_ar_conf_code(self):
		code = ""
		code +=	"BL_AR_VER\t:=\t%d\n" % self.ar_ver_list[-1]
		return code

	def check_and_set_ar_ver_list(self, ar_ver):
		if ((ar_ver not in self.ar_ver_list) and (ar_ver <= 64) and (ar_ver >= 0)):
			self.ar_ver_list.append(ar_ver)
			return True
		else:
			return False

	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 xml_debug_show(self, line, column):
		f = open(self.input_file, "r")
		if not f:
			sys.stderr.write("Unable to open file '%s'\n" % self.input_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):
		data = None
		try:
			f = open(self.input_file, "r")
			if not f:
				raise
			f.close()
		except:
			sys.stderr.write("Unable to open file '%s'\n" % self.input_file)
			return 1
		try:
			xmldoc = minidom.parse(self.input_file)
			ar_entry_list = xmldoc.getElementsByTagName('bl_ar_entry')

			for i in range(0, len(ar_entry_list)):
				ar_entry = ar_entry_list[i]
				data = self.get_int_by_name_from_ar_entry(ar_entry, i, "USED", False)
				if not data:
					continue

				data = self.get_int_by_name_from_ar_entry(ar_entry, i, "BL_AR_VER")
				if not data:
					return 1
				if data:
					data = data.strip()
					if self.check_and_set_ar_ver_list(int(data)) is False:
						print("XML parse fail in bl_ar_entry[%d].BL_AR_VER:" % i)
						print("    'BL_AR_VER' value duplicate or exceed range")
						return 1
			print("Get %d record in bl_ar_table" % len(self.ar_ver_list))
		except:
			sys.stderr.write("Unable to parse file '%s'\n" % self.input_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


class fw_ar_table_t:

	def __init__(self, input_file):
		self.input_file = input_file
		self.ar_ver_list = []

	def generate_ar_ver_code(self):
		code = ""
		code += "/* \n"
		code +=	" * This file is auto-generated by ar-tool\n"
		code +=	" * please do not modify this file manually\n"
		code +=	" */\n"
		code +=	"const uint32_t fw_ar_ver = %d;\n" % self.ar_ver_list[-1]
		return code

	def generate_ar_conf_code(self):
		code = ""
		code +=	"FW_AR_VER\t:=\t%d\n" % self.ar_ver_list[-1]
		return code

	def check_and_set_ar_ver_list(self, ar_ver):
		if ((ar_ver not in self.ar_ver_list) and (ar_ver <= 64) and (ar_ver >= 0)):
			self.ar_ver_list.append(ar_ver)
			return True
		else:
			return False

	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 xml_debug_show(self, line, column):
		f = open(self.input_file, "r")
		if not f:
			sys.stderr.write("Unable to open file '%s'\n" % self.input_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):
		data = None
		try:
			f = open(self.input_file, "r")
			if not f:
				raise
			f.close()
		except:
			sys.stderr.write("Unable to open file '%s'\n" % self.input_file)
			return 1
		try:
			xmldoc = minidom.parse(self.input_file)
			ar_entry_list = xmldoc.getElementsByTagName('fw_ar_entry')

			for i in range(0, len(ar_entry_list)):
				ar_entry = ar_entry_list[i]
				data = self.get_int_by_name_from_ar_entry(ar_entry, i, "USED", False)
				if not data:
					continue

				data = self.get_int_by_name_from_ar_entry(ar_entry, i, "FW_AR_VER")
				if not data:
					return 1
				if data:
					data = data.strip()
					if self.check_and_set_ar_ver_list(int(data)) is False:
						print("XML parse fail in fw_ar_entry[%d].FW_AR_VER:" % i)
						print("    'FW_AR_VER' value duplicate or exceed range")
						return 1
			print("Get %d record in fw_ar_table" % len(self.ar_ver_list))
		except:
			sys.stderr.write("Unable to parse file '%s'\n" % self.input_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 != 5:
		sys.stdout.write("ar-tool [bl_ar_table|fw_ar_table] [create_ar_ver|create_ar_conf] $(input_file) $(output_file)\n")
		return 1
	if argv[1] == "bl_ar_table":
		ar_table = bl_ar_table_t(argv[3])
	else:
		ar_table = fw_ar_table_t(argv[3])
	if ar_table.parse() != 0:
		return 1
	if argv[2] == "create_ar_ver":
		code = ar_table.generate_ar_ver_code()
		print("(%s) --> (%s)" % (argv[3], argv[4]))
		#print(code)
		f = open(argv[4], "w")
		f.write(code)
		f.close()
		return 0
	elif argv[2] == "create_ar_conf":
		code = ar_table.generate_ar_conf_code()
		print("(%s) --> (%s)" % (argv[3], argv[4]))
		#print(code)
		f = open(argv[4], "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))

