#!/usr/bin/env python
import xml.etree.ElementTree as ET
import os
import re
import codecs

class Scope(object):
	"""
	Single parameter group
	"""
	re_deep_lines = re.compile(r'.*\/.*\/')
	def __init__(self, ):
		self.scope = set()


	def __str__(self):
		return self.scope.__str__()

	def Add(self, scope):
		"""
		Add Scope to set
		"""
		self.scope.add(scope)

	def Has(self, scope):
		"""
		Check for existance
		"""
		if len(self.scope) == 0:
			return True
		# Anything in the form xxxxx/yyyyy/zzzzz....
		# is treated as xxxxx/yyyyy
		while (self.re_deep_lines.match(scope)):
			scope = os.path.dirname(scope)
		return scope in self.scope
			


class CMakeParser(object):
	"""
	Parses provided data and stores all found paths in scope.
	"""
	re_split_lines = re.compile(r'[\r\n]+')
	re_comment = re.compile(r'^\#')
	re_start = re.compile(r'set\s*\(\s*config_module_list')
	re_end = re.compile(r'\)\s*')

	def Parse(self, scope, contents):
		"""
		Incrementally parse cmake file contents and append all found path scope
		to scope.
		"""
		# This code is essentially a comment-parsing grammar. "state"
		# represents parser state. It contains human-readable state
		# names.
		state = None
		for line in self.re_split_lines.split(contents):
			line = line.strip()
			# Ignore empty lines
			if line == "":
				continue
			if self.re_comment.match(line):
				continue
			elif self.re_start.match(line):
				state = "gather"
				continue
			elif state is not None and state == "gather":
				if self.re_end.match(line):
					return True
				scope.Add(line)
		return False


if len(os.sys.argv) < 2:
	print("Error in %s" % os.sys.argv[0])
	print("Usage: %s <parameters.xml> [cmake-file-scoping] " % os.sys.argv[0])
	raise SystemExit


scope = Scope()
if len(os.sys.argv) == 3:
	with codecs.open(os.sys.argv[2], 'r', 'utf-8') as f:
		try:
			contents = f.read()
			f.close()
			parser  = CMakeParser()
			parser.Parse(scope, contents)
		except:
			contents = ''
			print('Failed reading file: %s, skipping scoping.' % os.sys.argv[2])
			pass

fp_header = open("px4_parameters.h", "w")
fp_src = open("px4_parameters.c", "w")

tree = ET.parse(os.sys.argv[1])
root = tree.getroot()

# Generate the header file content
header = """
#include <stdint.h>
#include <systemlib/param/param.h>

// DO NOT EDIT
// This file is autogenerated from parameters.xml

__BEGIN_DECLS

struct px4_parameters_t {
"""
start_name = ""
end_name = ""

for group in root:
	if group.tag == "group" and "no_code_generation" not in group.attrib:
		section = """
	/*****************************************************************
	 * %s
	 ****************************************************************/""" % group.attrib["name"]
		for param in group:
			scope_ = param.find('scope').text
			if not scope.Has(scope_):
				continue
			if not start_name:
				start_name = param.attrib["name"]
			end_name = param.attrib["name"]
			header += section
			section =""
			header += """
	const struct param_info_s __param__%s;""" % param.attrib["name"]
header += """
	const unsigned int param_count;
};

extern const struct px4_parameters_t px4_parameters;
"""

# Generate the C file content
src = """
#include <px4_parameters.h>

// DO NOT EDIT
// This file is autogenerated from paramaters.xml

const
#ifndef __PX4_DARWIN
__attribute__((used, section("__param")))
#endif
struct px4_parameters_t px4_parameters = {
"""
i=0
for group in root:
	if group.tag == "group" and "no_code_generation" not in group.attrib:
		section = """
	/*****************************************************************
	 * %s
	 ****************************************************************/""" % group.attrib["name"]
		for param in group:
			scope_ = param.find('scope').text
			if not scope.Has(scope_):
				continue
			if not start_name:
				start_name = param.attrib["name"]
			end_name = param.attrib["name"]
			val_str = "#error UNKNOWN PARAM TYPE, FIX px_generate_params.py"
			if (param.attrib["type"] == "FLOAT"):
				val_str = ".val.f = "
			elif (param.attrib["type"] == "INT32"):
				val_str = ".val.i = "
			i+=1
			src += section
			section =""
			src += """
	{
		"%s",
		PARAM_TYPE_%s,
		%s%s
	},
""" % (param.attrib["name"], param.attrib["type"], val_str, param.attrib["default"])
src += """
	%d
};

//extern const struct px4_parameters_t px4_parameters;

__END_DECLS

""" % i

fp_header.write(header)
fp_src.write(src)
fp_header.close()
fp_src.close()
