from xml.dom import pulldom
from io import StringIO
from twisted.python import usage
import nevow

class LineBasedStream(object):
    """
    Allow pulldom to read at most one line at a time, to get accurate
    line number reporting. Otherwise it always reports everything is
    on the last line read in a single chunk.

    Delay reporting newlines to next read, to avoid line numbers
    always being off by one.

    Not the prettiest code I've written :-(
    """
    def __init__(self, stream):
        self.stream = stream
        self.buffer = ''

    def read(self, bufsize):
        if not self.buffer:
            self.buffer = self.stream.readline(bufsize)

        if not self.buffer:
            # eof
            return ''

        data, self.buffer = self.buffer, ''
        while data.endswith('\n'):
            self.buffer = self.buffer + data[-1]
            data = data[:-1]
        if not data:
            # data was nothing but newlines, undo above or it would
            # look like EOF and we'd never make progress
            data, self.buffer = self.buffer, ''
        return data

def getMsgID(node):
    out = StringIO()
    print('msgid ""', file=out)
    for child in node.childNodes:
        s = child.toxml('utf-8')
        s = s.replace('\\', '\\\\')
        s = s.replace('"', '\\"')
        s = s.replace('\n', '\\n')
        print('"%s"' % s, file=out)
    print('msgstr ""', file=out)
    return out.getvalue()

def process(filename, messages):
    f = open(filename, 'rU')
    stream = LineBasedStream(f)
    events = pulldom.parse(stream)

    for (event, node) in events:
        if event == pulldom.START_ELEMENT:
            get = getattr(node, 'getAttributeNS', None)
            if get is not None:
                value = get('http://nevow.com/ns/nevow/0.1', 'render')
                if value == 'i18n':
                    events.expandNode(node)

                    msgid = getMsgID(node)
                    l = messages.setdefault(msgid, [])
                    l.append('#: %s:%d' % (filename, events.parser.getLineNumber()))


def report(messages):
    for msgid, locations in list(messages.items()):
        for line in locations:
            print(line)
        print(msgid)

class GettextOptions(usage.Options):
    def opt_version(self):
        print('Nevow version:', nevow.__version__)
        usage.Options.opt_version(self)

    def parseArgs(self, *files):
        self['files'] = files

def runApp(config):
    messages = {}

    for filename in config['files']:
        process(filename, messages)

    report(messages)

def run():
    from twisted.application import app
    app.run(runApp, GettextOptions)
