import xml.etree.ElementTree as ET


class XmlBuilder(object):
    '''Build XML document interpolating value from context using str.format() in
       text data. Grammar for schema is:

           schema = tree
           tree = simple | loop
           simple = (tag_name, attribs_dict?, tree*)
           loop = ('?loop', loop_var_name, tree*)

       where tag_name is an ElementTree tag name, or an XML tag name with prefix
       in namespaces, attribs_dict is a list of key values pairs

       The loop construct allow to iterate one variable, the one named by loop_var_name, of the
       context as a subcontext to pass to sub-schemas inside the loop tuple.
    '''
    schema = None
    encoding = 'us-ascii'
    namespaces = None

    def __init__(self, schema=None, encoding=None, namespaces=None):
        self.namespaces = {}
        if schema is not None:
            self.schema = schema
        if encoding is not None:
            self.encoding = encoding
        if namespaces is not None:
            self.namespaces.update(namespaces)

    def resolve_namespace(self, qname):
        if ':' in qname:
            prefix, name = qname.split(':')
            url = self.namespaces[prefix]
            return '{{{0}}}{1}'.format(url, name)
        return qname

    def build(self, context=None):
        tb = ET.TreeBuilder()
        def helper(schema, context):
            tag = schema[0]
            if tag == '?loop':
                var = schema[1]
                for subcontext in context[var]:
                    for subschema in schema[2:]:
                        helper(subschema, subcontext)
                return
            if context:
                tag = tag.format(**context)
            tag = self.resolve_namespace(tag)
            template_attribs = schema[1] if (len(schema) > 0
                                             and isinstance(schema[1], dict)) else None
            attribs = None
            if template_attribs:
                for key, value in template_attribs.items():
                    if context:
                        key = key.format(**context)
                    key = self.resolve_namespace(key)
                    value = value.format(**context)
                    attribs[key] = value
            tb.start(tag, attribs or {})
            if template_attribs is None:
                children = schema[1:]
            else:
                children = schema[2:]
            for child in children:
                if isinstance(child, basestring):
                    if context:
                       child = child.format(**context)
                    tb.data(child)
                else:
                    helper(child, context)
            tb.end(tag)
        helper(self.schema, context)
        return tb.close()

    def string(self, context=None, encoding=encoding):
        return ET.tostring(
            self.build(context=context),
            encoding=encoding or self.encoding)
