"""ElementBuilder.py - construct ElementTrees with friendly syntax

(C) 2005 Oren Tirosh. Released under the terms of the MIT License.
    http://www.opensource.org/licenses/mit-license.php

* Extended Element factory

Backward-compatible with the standard ElementTree Element factory with 
the following extensions:

Sub-elements may be supplied as arguments:
    Element('tag', {'a': '5'}, Element('othertag'))

Attribute dictionary is optional:
    Element('tag', Element('othertag'))

Element text may be supplied as an argument:
    Element('tag', 'some text')

Element text and sub-elements:
    Element('tag', 'some text', Element('othertag'))

Element text, sub-elements and sub-element tails:
    Element('tag', 'some text', Element('othertag'), 'tail')


* Namespaces

A namespace is a factory for QNames.

ns.tag == ns+'tag' == QName('http://namespace/uri', 'tag')
where:
ns = Namespace('http://namespace/uri')

Namespace(None) or LocalNamespace generates LocalName objects instead
of QNames but is otherwise similar.

A second optional argument to Namespace is prefix which will be used 
when generating XML instead of automatically-generated numeric namespace
prefixes unless it collides with another defined prefix or uri.


* Callable names

QName and LocalName objects are callable, taking the same arguments as
the Element factory, except the tag argument which is implicitly set to
the QName/LocalName itself.

ns.tag(a='5') == Element(QName('http://namespace/uri', 'tag', a='5')

"""

#from elementtree import ElementTree 
from edgy.xml.element import (Element as basefactory, iselement,
                              QName, _namespace_map)
from edgy.xml.utils import splitTag

__all__ = 'Element', 'Namespace', 'LocalNamespace'


def Element(tag, attrib={}, *children, **extra):
    """ Element(tag (,attrdict)? (,subelement|string)* ) -> Element """

    if isinstance(attrib, dict):
        attrib = attrib.copy()
    else:
        children = (attrib,) + children 
        attrib = {}
    attrib.update(extra)

    element = basefactory(tag, attrib)

    prevelem = None
    for arg in children:
        if iselement(arg):
            element.append(arg)
            prevelem = arg
        else:
            if isinstance(arg, basestring):
                if prevelem is None:
                    element.text = (element.text or '') + arg
                else:
                    prevelem.tail = (prevelem.tail or '') + arg
            else:
                try:
                    it = iter(arg)
                except:
                    raise TypeError, "argument type to Element"
                for item in it:
                    if not iselement(item):
                        raise TypeError, "invalid argument type to Element"
                    element.append(item)
    return element


class _QName(unicode, QName):
    """ Calling a QName creates an Element with the name as its tag """
    def __new__(cls, *args):
        tmp = QName(*args)
        new = unicode.__new__(cls, tmp.text)
        new.text = new
        return new

    # Use Python's binding of first argument as self
    __call__ = Element


class LocalName(unicode):
    """ Calling LocalName creates an Element with the name as its tag """
    # Use Python's binding of first argument as self
    __call__ = Element
            

class Namespace:
    """ Namespace(uri [, prefix hint]) -> Namespace object """

    def __init__(self, uri=None, prefix=None, location=None):
        self.uri = uri or None

        if prefix is None:
            return
        map = _namespace_map
        if uri in map or prefix in map.values():
            # prefix or URI already used
            return 
        if prefix.startswith("ns") and prefix[2:].isdigit():
            # names in this form may collide with autogenerated prefixes
            return
        map[uri] = prefix

        if location is None:
            location = uri
        self.location = location

    def __contains__(self, qn):
        nsuri, tag = splitTag(str(qn))
        return (nsuri == self.uri)
        
    def __add__(self, tag):
        if self.uri is None:
            return LocalName(tag)
        else:
            return _QName(self.uri, tag)

    def __getattr__(self, tag):
        if tag[0] == '_':
            raise AttributeError(tag)
        qname = self+tag
        self.__dict__[tag] = qname  # cache for faster access next time
        return qname

    def __getitem__(self, tag):
        return self + tag

LocalNamespace = Namespace(None)
