from nevow.flat.flatstan import serialize, TagSerializer
from nevow.flat import registerFlattener, flatten
from nevow.context import WovenContext, WebContext
from nevow import inevow, stan, tags
from zope.interface import implements, Interface

import sys

attr = stan.Proto("nevow:attr")
slot = stan.Proto("nevow:slot")

class MyRendererFactory(object):
    implements(inevow.IRendererFactory)
    def renderer(self, context, name):
        def r(context, data):
            if context.tag is None:
                attrname = context.locate(IAttrName)
                context.locate(INoOutputAttr)[attrname] = True
                attrelem = attr(**{'nevow:render':name, 'name':attrname})
                context.locate(IChildren).insert(0, attrelem)
                return ''
            else:
                return context.tag.clone()(**{'nevow:render':name})
        return r

class MyNotQuiteDict(object):
    def get(self, name, default):
        return slot(name=name)

class MyContext(WebContext):
    def __init__(self, *args):
        WebContext.__init__(self, *args)
        self._slotData = MyNotQuiteDict()

allowSingleton = ('img', 'br', 'hr', 'base', 'meta', 'link', 'param', 'area',
                  'input', 'col', 'basefont', 'isindex', 'frame', 'nevow:attr', 'nevow:slot')

class IAttrName(Interface): pass
class INoOutputAttr(Interface): pass
class IChildren(Interface): pass
class IStan2XML(Interface): pass

def MyTagSerializer(original, context):
    """
    Original is the tag.
    Context is either:
      - the context of someone up the chain (if contextIsMine is False)
      - this tag's context (if contextIsMine is True)
    """
    try:
        context.locate(IStan2XML)
    except KeyError:
        yield TagSerializer(original, context, contextIsMine)

    visible = bool(original.tagName)

    if visible and context.isAttrib:
        raise RuntimeError, "Tried to render tag '%s' in an tag attribute context." % (original.tagName)

    attributes = original.attributes.copy()
    if original.tagName == 'html':
        attributes['xmlns:nevow'] = "http://nevow.com/ns/nevow/0.1"

    if original.pattern is not stan.Unset and original.pattern is not None:
        attributes['nevow:pattern'] = original.pattern

    context = WovenContext(context, original)

    if original.data is not stan.Unset:
        attributes['nevow:data'] = original.data

    if original.render:
        toBeRenderedBy = original.render
        original._clearSpecials()
        yield serialize(toBeRenderedBy, context)
        return

    if not visible:
        for child in original.children:
            yield serialize(child, context)
        return

    children = original.children[:]

    yield '<%s' % original.tagName
    if attributes:
        attribContext = WovenContext(parent=context, precompile=context.precompile, isAttrib=True)
        noattroutput = {}
        attribContext.remember(noattroutput, INoOutputAttr)
        attribContext.remember(children, IChildren)
        for (k, v) in attributes.iteritems():
            attribContext.remember(k, IAttrName)
            if v is None:
                continue
            r = []
            r.append(' %s="' % k)
            r.append(serialize(v, attribContext))
            r.append('"')
            if k not in noattroutput:
                for l in r:
                    yield l
    if not children:
        if original.tagName in allowSingleton:
            yield ' />'
        else:
            yield '></%s>' % original.tagName
    else:
        yield '>'
        for child in children:
            yield serialize(child, context)
        yield '</%s>' % original.tagName

registerFlattener(MyTagSerializer, stan.Tag)

header = '''\
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
          "DTD/xhtml1-strict.dtd">
'''

def stan2xml(s, header=header):
    c = MyContext()
    f = MyRendererFactory()
    c.remember(f, inevow.IRendererFactory)
    c.remember(f, inevow.IData)
    c.remember(f, IStan2XML)
    return header + flatten(s, c)

if __name__ == '__main__':
    d = tags.__dict__.copy()
    d['tags'] = tags
    e = {}
    execfile(sys.argv[1], d, e)
    s, = e.values()
    print stan2xml(s)
