#!/usr/bin/python

# xml2lst - convert Discover 2 XML files back to Discover 1 format

import sys
import optparse
import string
import re
import xml.sax
import xml.sax.handler

recognized_data_paths = { "linux/module/name": "%s",
                          "xfree86/server/device/driver": "Server:XFree86(%s)",
                          "xfree86/server/name": "Server:%s"
                        }

class DeviceHandler(xml.sax.handler.ContentHandler):
    def __init__(self, busclass, vendor):
        self.busclasses = busclass
        self.vendors = vendor

        self.devices = {}
        for vendor in self.vendors.keys():
            self.devices[vendor] = {}

        self.clearDevice()

    def clearDevice(self):
        self.current_device = None
        self.current_device_items = {}
        self.data_path = []

    def startElement(self, name, attrs):
        if name == 'data':
            self.data_path.append(attrs["class"])
        elif name == 'device':
            self.current_device = attrs.copy()

    def endElement(self, name):
        if name == 'data':
            self.data_path.pop()
        elif name == 'device':
            self.saveDevice()
            self.clearDevice()

    def characters(self, content):
        if len(self.data_path) < 1 or \
           not re.search(r'\S+', content):
            return

        data_key = string.join(self.data_path, "/")
        self.current_device_items[data_key] = content.strip()

    def saveDevice(self):
        item = {}
        item["busclass"] = self.busclasses[self.current_device["busclass"]]
        item["name"] = self.current_device["model_name"]

        item["driver"] = "ignore"
        for key in self.current_device_items.keys():
            if not recognized_data_paths.has_key(key):
                continue
            item["driver"] = recognized_data_paths[key] \
                             % (self.current_device_items[key],)
            break

        vendor = self.current_device["vendor"]
        model = self.current_device["model"]
        self.devices[vendor][model] = item

def load_id_db(infile):
    class IdHandler(xml.sax.handler.ContentHandler):
        def __init__(self):
            self.idmap = {}

        def startElement(self, name, attrs):
            if len(filter(lambda x: x not in ('id', 'name'),
                          attrs.keys())) == 0:
                self.idmap[attrs['id']] = attrs['name']

        def getResults(self):
            return self.idmap

    handler = IdHandler()
    xml.sax.parse(infile, handler)
    return handler.getResults()

def main():
    usage = "usage: %prog [options] busclass_file vendor_file device_file"
    cmdline = optparse.OptionParser(usage)
    cmdline.add_option("-o", dest="output_fn", metavar="FILE",
                       help="file to write instead of stdout")
    # XXX: add this capability at some point
##     cmdline.add_option("-d", dest="data_versions", action="append",
##                        metavar="VERSIONSPEC",
##                        help="attach a data version for limiting output")
    (options, args) = cmdline.parse_args()
    (busclass_fn, vendor_fn, device_fn) = args

    if options.output_fn:
        output_file = open(options.output_fn, "w")
    else:
        output_file = sys.stdout

    busclasses = load_id_db(busclass_fn)
    vendors = load_id_db(vendor_fn)

    parser = DeviceHandler(busclasses, vendors)
    xml.sax.parse(device_fn, parser)

    vendor_ids = vendors.keys()
    vendor_ids.sort()
    for vendor in vendor_ids:
        output_file.write("%s\t%s\n" % (vendor, vendors[vendor]))

        device_ids = parser.devices[vendor].keys()
        device_ids.sort()
        for device in device_ids:
            output_file.write("\t%s%s\t%s\t%s\t%s\n"
                              % (vendor, device,
                                 parser.devices[vendor][device]["busclass"],
                                 parser.devices[vendor][device]["driver"],
                                 parser.devices[vendor][device]["name"]))

if __name__ == "__main__":
    main()
