#!/usr/bin/env python
#
# Copyright (c) 2008-2010 Wind River Systems, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the Lesser GNU General Public License version 2.1 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# version 2.1 along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
#
"""convert tables.in files to enums, tables, and support code.

Inputs are a type name, prefix, and a list of columns, followed by a list of
names with optional "= value" suffixes, plus optional additional columns.
The names are used to create enums and a table of strings, as well as
to/from lookups between the ids and names.  If additional columns are
defined, each column (separated by ", ") is used to create an additional
table of the given name, and a lookup function from ids.  Example:
        foo: FFF; bar = GOZINTA
        hello, yah
        world, nope
produces:
        typedef enum {
                FFF_UNKNOWN = -1,
                FFF_MIN = 0,
                FFF_NONE = 0,
                FFF_HELLO,
                FFF_WORLD,
                FFF_MAX
        } foo_id_t;
        extern const char *foo_name(foo_id_t id);
        extern foo_id_t foo_id(const char *name);
        extern const char *foo_bar(foo_id_t id);

such that foo_name(1) => "hello" and foo_bar(1) => "yah".  If there
is an assigned value for a column description, missing column values
yield that value, otherwise they yield "unknown".

Values out of range yield "unknown", and unrecognized names yield the
value -1.  Note that the "MAX" value is one more than the highest defined
value.  (This is for consistency with C array bounds.)
"""

import glob
import sys
import string
from templatefile import TemplateFile

class DataType:
    """a set of related DataItem objects"""

    def __init__(self, path):
        """read the first line of path, then make tuples of the rest"""
        source = file(path)
        definition = source.readline().rstrip()
        self.name, qualifiers = string.split(definition, ': ', 2)
        if '; ' in qualifiers:
            self.prefix, columns = string.split(qualifiers, '; ')
        else:
            self.prefix = qualifiers
            columns = []
        if len(columns):
            self.columns = []
            columns = string.split(columns, ', ')
            for col in columns:
                if "=" in col:
                    name, default = string.split(col, ' = ')
                else:
                    name, default = col, ""
                self.columns.append({"name":name, "value":default})
        else:
            self.columns = []
        self.data = []
        self.comments = []
        for line in source.readlines():
            item = {}
            if line.startswith('#'):
                self.comments.append(line.rstrip().replace('#', ''))
                continue
            # first entry on the line is the "real" name/id, following hunks
            # are additional columns
            cols = string.split(line.rstrip(), ', ')
            item["name"] = cols.pop(0)
            item["upper"] = item["name"].replace('-', '_').upper()
            column_list = []
            for col in self.columns:
                if len(cols) > 0:
                    column_list.append({"name":col["name"], "value":cols.pop(0)})
                else:
                    column_list.append({"name":col["name"], "value":col["default"]})
            item["cols"] = column_list
            self.data.append(item)

    def __getitem__(self, key):
        """Make this object look like a dict for Templates to use"""
        attr = getattr(self, key)

        if callable(attr):
            return attr()
        else:
            return attr

    def __repr__(self):
        column = 0
        out = ""
        out += "type: %s_t" % self.name
        out += " (prefix '%s_ENUM')\n" % self.prefix
        for col in self.columns:
            out += "  extra column: %s (default '%s')\n" % (col["name"], col["value"])
        out += "   "
        for item in self.data:
            column = column + 1
            if column > 4 and column % 4 == 1:
                out += "\n   "
            out += "%-19s" % item["name"]
#            for col in item["cols"]:
#                out += "\t%s(%s)\n" % (col["name"], col["value"])
        return out

    def comment(self):
        if len(self.comments):
            return '/*' + '\n *'.join(self.comments) + ' */\n'
        else:
            return ''

    def names(self):
        return ',\n\t'.join('"%s"' % x["name"] for x in self.data)

    def enums(self):
        return ',\n\t'.join('%s_%s' % (self.prefix, x["upper"]) for x in self.data)

    def column_names(self):
        decl_lines = []
        column = 0
        for col in self.columns:
            decl_lines.append("static const char *%s_id_to_%s[] = {" % (self.name, col["name"]))
            decl_lines.append('\t"%s",' % col["value"])
            for item in self.data:
                decl_lines.append('\t"%s",' % item["cols"][column]["value"])
            decl_lines.append('\tNULL')
            decl_lines.append("};")
            column = column + 1
        return '\n'.join(decl_lines)

    def column_funcs(self):
        decl_lines = []
        for col in self.columns:
            decl_lines.append('extern const char *')
            decl_lines.append('pseudo_%s_%s(pseudo_%s_t id) {' %
                (self.name, col["name"], self.name))
            decl_lines.append('\tif (id < 0 || id >= %s_MAX)' % (self.prefix))
            decl_lines.append('\t\treturn "%s";' % col["value"])
            decl_lines.append('\treturn %s_id_to_%s[id];' %
                (self.name, col["name"]))
            decl_lines.append('}')
        return '\n'.join(decl_lines)

    def column_protos(self):
        decl_lines = []
        for col in self.columns:
            decl_lines.append('extern const char *pseudo_%s_%s(pseudo_%s_t id);' %
                (self.name, col["name"], self.name))
        return '\n'.join(decl_lines)

def main():
    """Read in function defintions, write out files based on templates."""
    datatypes = []
    templates = []

    # error checking helpfully provided by the exception handler
    copyright_file = open('guts/COPYRIGHT')
    TemplateFile.copyright = copyright_file.read()
    copyright_file.close()

    for path in glob.glob('table_templates/*'):
        try:
            template_file = TemplateFile(path)
            template_file.emit('copyright')
            template_file.emit('header')
            templates.append(template_file)
        except IOError:
            print "Invalid or malformed template %s.  Aborting." % path
            exit(1)

    for filename in sys.argv[1:]:
        # read in the datatype
        sys.stdout.write("%s: " % filename)
        datatype = DataType(filename)
        datatypes.append(datatype)
        print datatype.__repr__()
        print ""

    print "Writing datatypes...",
    for datatype in datatypes:
        # populate various tables and files with each datatype
        for template_file in templates:
            template_file.emit('body', datatype)
    print "done.  Cleaning up."
    
    for template_file in templates:
        # clean up files
        template_file.emit('footer')
        template_file.close()

if __name__ == '__main__':
    main()
