import inspect, pydoc, urllib
import XmlFormatter

class ExtensionFormatter(XmlFormatter.XmlFormatter):

    def doc_module(self, module):
        """
        Produce documentation for a given module object.
        """
        if not inspect.ismodule(module):
            raise TypeError("doc_module() requires a 'module' object "
                            "but received a '%s'" % type(module).__name__)

        attributes = {'name' : module.__name__}
        try:
            absfile = inspect.getabsfile(module)
            file = urllib.pathname2url(absfile)
        except TypeError:
            file = '<builtin>'
        attributes['file'] = file
        self.start_element('ext-module', attributes)

        desc = self.escape(pydoc.getdoc(module))
        self.write_element('description', content=desc)

        namespaces = getattr(module, 'ExtNamespaces', {})
        functions = getattr(module, 'ExtFunctions', {})
        elements = getattr(module, 'ExtElements', {})

        if namespaces:
            self.start_element('namespaces')
            for namespace_uri, prefix in namespaces.items():
                self.doc_namespace(namespace_uri, prefix)
            self.end_element('namespaces')

        if functions:
            self.start_element('functions')
            for (namespace_uri, name), function in functions.items():
                qname = self.make_qname(namespaces, namespace_uri, name)
                self.doc_function(function, namespace_uri, qname)
            self.end_element('functions')

        if elements:
            self.start_element('elements')
            for (namespace_uri, name), element in elements.items():
                qname = self.make_qname(namespaces, namespace_uri, name)
                self.doc_element(element, namespace_uri, qname)
            self.end_element('elements')

        self.end_element('ext-module')
        return

    def make_qname(self, namespaces, namespace_uri, name):
        if namespace_uri:
            prefix = 'extns'  # default (if not given)
            if not namespaces.has_key(namespace_uri):
                # No namespace/prefix mapping
                self.warn("namespace '%s' used, but no prefix defined "
                          "(defaulting to '%s')" % (namespace_uri, prefix))
            elif not namespaces.get(namespace_uri):
                # A namespace/prefix mapping exists, but prefix is empty
                self.warn("namespace '%s' used, but empty prefix defined " %
                          namespace_uri)
            prefix = namespaces.get(namespace_uri, prefix)
            namespaces[namespace_uri] = prefix
            qname = prefix + ':' + name
        else:
            qname = name
        return qname

    def doc_namespace(self, namespace, prefix):
        """
        Document extension namespaces
        """
        attributes = {'namespace-uri' : namespace,
                      'prefix' : prefix,
                      }
        self.write_element('namespace', attributes)
        return
        
    def doc_function(self, function, namespace, qname):
        """
        Document extension functions
        """
        attributes = {'namespace-uri' : namespace or '',
                      'name' : qname,
                      }
        self.start_element('function', attributes)

        desc = self.escape(pydoc.getdoc(function))
        self.write_element('description', content=desc)

        ext_result = getattr(function, 'ext_result', 'unknown')
        self.write_element('result', content=self.escape(ext_result))

        args, varargs, varkw, defaults = inspect.getargspec(function)
        firstdefault = len(args) - len(defaults or ())
        # All extension functions take 'context' as their first argument
        for i in range(1, len(args)):
            attributes = {'name' : str(args[i])}
            if (i < firstdefault):
                attributes['required'] = 'yes'
            self.write_element('argument', attributes)

        self.end_element('function')
        return

    def doc_element(self, element, namespace, qname):
        """
        Document extension elements
        """
        attributes = {'namespace-uri' : namespace or '',
                      'name' : qname,
                      }
        self.start_element('element', attributes)

        desc = self.escape(pydoc.getdoc(element))
        self.write_element('description', content=desc)

        if element.content:
            content  = self.escape(str(element.content))
            self.write_element('content', content=content)

        attributes = element.legalAttrs or {}
        for name, info in attributes.items():
            attrs = {'name' : name,
                     'content' : str(info),
                     'required' : info.required and 'yes' or 'no',
                     }
            if info.default:
                attrs['default'] = info.default
            self.start_element('attribute', attrs)
            desc = info.description or ''
            self.write_element('description', content=desc)
            self.end_element('attribute')

        self.end_element('element')
        return
