
class AutogenException(Exception):
    def __init__(self, msg):
        Exception.__init__(self, msg)

def doFor(content, defines, output, globals, locals):
    # Do a for loop over a list in the definitions.  The mess here is to
    # support a separator character.
    params = eval(content[0].replace('\r\n','\n'), globals, locals)
    if isinstance(params, tuple):
        if len(params) != 2:
            raise AutogenException("for: wrong number of arguments (2 expected).");
        items = params[0]
        sep = params[1]
    else:
        items = params
        sep = ''

    if not isinstance(items, list):
        items = [items]
    for i in xrange(len(items)):
        autogen(content[1:], items[i], output, globals, locals)
        if i < len(items)-1:
            output.write(sep)

def doIf(entry, defines, output, globals, locals):
    # If blocks have a condition, and hide the 'elif' and 'else' clauses
    # in a third element of the tuple.
    content = entry[1]
    test = eval(content[0].replace('\r\n','\n'), globals, locals)
    if test:
        autogen(content[1:], defines, output, globals, locals)
    elif len(entry[2]) != 0:
        entry = entry[2][0]
        if entry[0] == 'else':
            autogen(entry[1], defines, output, globals, locals)
        elif entry[0] == 'elif':
            doIf(entry, defines, output, globals, locals)

def autogen(template, defines, output, globals=None, locals=None):
    if globals is None:
        globals = {}

    # Copy local scope so we can modify things in this template scope.
    if locals is None:
        locals = {}
    else:
        locals = dict(locals)

    # If this part of the definitions is a dictionary add the keys to the
    # local scope, otherwise, set the variable 'value' to the current 
    # definitions.
    if isinstance(defines, dict):
        locals.update(defines)
    else:
        locals['value'] = defines

    #print "template: %r" % template
    #print "globals: %r" % globals.keys()
    #print "locals: %r" % locals.keys()
    
    # Go through this level of the template.
    for entry in template:
        if isinstance(entry, unicode) or isinstance(entry, str):
            output.write(entry)
        else:
            type, content = entry[0:2]
            if type == 'define':
                exec content[0].replace('\r\n','\n') in globals, locals
            elif type == 'eval':
                try:
                    output.write(unicode(eval(content[0].replace('\r\n','\n'),
                                              globals, locals)))
                except KeyError, e:
                    raise AutogenException('In "%s", threw KeyError: %s.' % (content[0], e))
            elif type == 'for':
                doFor(content, defines, output, globals, locals)
            elif type == 'if':
                doIf(entry, defines, output, globals, locals)

if __name__ == '__main__':
    import getopt, sys, codecs, time, os
    import data
    import template

    def usage():
        print >> sys.stderr, """Usage:
    %s [-t template] [-o output] defines
""" % sys.argv[0]
        sys.exit(1)

    # Parse command line arguments, this is uglier than it has to be.
    try:
        opts, args = getopt.getopt(sys.argv[1:], "t:o:D:", ["template=", "output="])
    except getopt.GetoptError:
        usage()

    templateFile = None
    outputFile = None
    defineFile = None
    globals = {}

    for o, a in opts:
        if o in ('-t', "--template"):
            templateFile = a
        elif o in ('-o', "--output"):
            outputFile = a
        elif o == '-D':
            i = a.find('=')
            globals[a[0:i]] = a[i+1:]
    
    if len(args) > 1:
        print >> sys.stderr, "Too many options!"
        usage()

    if len(args) == 1:
        defineFile = args[0]
        defines = open(defineFile)
    else:
        defineFile = 'stdin'
        defines = sys.stdin

    if templateFile is None:
        print >> sys.stderr, "No template given!"
        usage()

    tmpl = codecs.open(templateFile, "r", "utf-8")
    
    # Read in definition file.
    try:
        dataParser = data.Parser()
        inputData = dataParser.parse(defines)
    except data.ParserException, e:
        print str(e)
        sys.exit(1)

    # Read in template file.
    try:
        dataTemplate = tmpl.read()
        dataTemplate = template.parse(dataTemplate)
    except template.ParserException, e:
        print str(e)
        sys.exit(1)

    # Generate the "do not edit this file" message.
    def dne(prefix=''):
        eol = ''
        if 'COMSPEC' in os.environ:
            eol = '\r\n'
        else:
            eol = '\n'
        s = eol + prefix + " DO NOT EDIT THIS FILE    " + outputFile + eol
        s += eol + prefix + " It has been AutoGen-ed   " + time.ctime() + eol
        s += eol + prefix + " and the template file    " + templateFile + eol
        return s

    globals['dne'] = dne

    if outputFile is None:
        outputFile = 'stdout'
        output = sys.stdout
    else:
        output = codecs.open(outputFile, "w", "utf-8")

    # Go!
    autogen(dataTemplate, inputData, output, globals)

