#!/usr/bin/env python2.4

import py
from lxml import etree

command = 'xmame.SDL'
switches = '-fullscreen'
rompath = '/usr/share/games/xmame/roms'
datapath = '/home/johnny/.xmamerunner'
browserpath = '/usr/bin/mozilla'
http_port = 10001
# if this is filled, only the games that have their name in this list will be
# displayed
list_override = [] 

class XMameRunner(object):
    """A graphical user interface for XMAME

        combined with a small database for roms and images, and a data 
        collector
    """

    def __init__(self):
        self.db = RomDB(rompath, datapath)
    
    def get_items(self):
        """return an list of dictionaries with item data"""
        xml = self.db.get_data()
        dom = etree.fromstring(xml)
        ret = []
        for node in dom.xpath('//game'):
            name = node.attrib['name']
            if list_override and name not in list_override:
                continue
            ret.append({
                'name': name,
                'cloneof': node.attrib.get('cloneof', ''),
                'description': node[0].text,
                'year': node[1].text,
                'manufacturer': node[2].text,
            })
        return ret

    def run(self, name):
        """run a game

            name is the name of the game from the dict (key 'name'), which
            can be fed to xmame as a game name
        """
        py.std.os.system('%s %s %s' % (command, switches, name))

class RomDB(object):
    def __init__(self, rompath, datapath):
        self.rompath = py.path.local(rompath)
        self.datapath = dp = py.path.local(datapath)
        if not dp.check():
            dp.mkdir()
        elif not dp.check(dir=1):
            raise IOError, 'datapath is not a directory'

    def get_data(self):
        cache = self.datapath / '.mamerunner_cache'
        if cache.check():
            # if cache is still valid, use it, else create new
            valid = True
            for p in self.rompath.listdir():
                if p.basename.endswith('.zip'):
                    print 'p:', p
                    print 'mtime:', p.mtime()
                    print 'cache mtime:', cache.mtime()
                    if p.mtime() > cache.mtime():
                        data = self.gather_data()
                        cache.write(data)
                        break
            else:
                data = cache.read()
        else:
            data = self.gather_data()
            cache.write(data)
        return data

    def gather_data(self):
        print 'going to gather data, this can take a long time!'
        print 'first reading XML for all ROMs'
        outfp, infp, errfp = py.std.popen2.popen3('%s -listxml' % command)
        try:
            xml = outfp.read()
        finally:
            outfp.close()
            infp.close()
            errfp.close()
        print 'XML read, finding out which files are installed (slow!!)'
        tree = etree.fromstring(xml)
        outfp, infp, errfp = py.std.popen2.popen3('%s -verifyromsets' % 
                                                    command)
        usedgameels = []
        try:
            for line in outfp.readlines():
                line = line.strip()
                while '  ' in line:
                    line = line.replace('  ', ' ')
                if line and line.split(' ')[1] == 'correct':
                    print 'found:', line.split(' ')[0]
                    usedgameels.append(
                        tree.xpath('//game[@name="%s"]' % 
                                    line.split(' ')[0])[0])
        finally:
            outfp.close()
            infp.close()
            errfp.close()
        data = ['<games>']
        for el in usedgameels:
            data.append(etree.tostring(el))
        print 'done gathering data'
        return '%s\n</games>' % '\t'.join(data)

class CGIXMameRunner(object):
    """show the XMAMERunner menu in a browser

        boot a simple HTTP server and point a browser to it, a page with
        all the games is shown and the user can start one by clicking on
        its entry
    """

    def __init__(self):
        self.runner = XMameRunner()
        self.quit = False

    def run(self):
        self.lock = py.std.thread.allocate_lock()
        this = self # make it available to the handler code
        class XMameHTTPRequestHandler(
                    py.std.BaseHTTPServer.BaseHTTPRequestHandler):
            def do_GET(self):
                """show the listing page, run a game, or quit"""
                path = self.path
                query = ''
                if '?' in path:
                    path, query = path.split('?')
                if path == '/':
                    self.show_list()
                elif path == '/run':
                    self.run_game(query)
                elif path == '/images':
                    self.serve_image(query)
                elif path == '/quit':
                    self.quit()
                else:
                    data = 'no such url, please visit <a href="/">the root</a>'
                    self.serve_data('text/html', data)

            def serve_data(self, content_type, data):
                self.wfile.write(
                    ('HTTP/1.0 200 OK\r\n'
                        'Content-Type: %s; charset=UTF-8\r\n'
                        'Content-Length: %s\r\n'
                        '\r\n') % (content_type, len(data)))
                self.wfile.write(data)

            def show_list(self):
                """show the list of games"""
                from templess.templess import template
                t = template(py.magic.autopath().dirpath() / 
                                'templates/list.html')
                games = this.runner.get_items()
                games.sort(lambda a, b: cmp(a['name'], b['name']))
                for item in games:
                    item['link'] = '/run?%s' % item['name']
                    imgspath = py.path.local(datapath) / 'images'
                    if (imgspath / ('%s.png' % item['name'])).check():
                        item['imgsrc'] = '/images?%s' % item['name']
                        item['showimage'] = True
                        item['showlink'] = False
                    elif (imgspath / ('%s.png' % item['cloneof'])).check():
                        item['imgsrc'] = '/images?%s' % item['cloneof']
                        item['showimage'] = True
                        item['showlink'] = False
                    else:
                        item['showimage'] = False
                        item['showlink'] = True
                html = t.render_to_string({
                            'games': games,
                        })
                self.serve_data('text/html', html)

            def run_game(self, query):
                """run a game"""
                print 'going to run', query
                this.runner.run(query)

            def serve_image(self, query):
                img = py.path.local(datapath) / ('images/%s.png' % query)
                self.serve_data('image/png', img.read())

            def quit(self):
                """quit the server and app"""
                this.quit = 1
                from templess.templess import template
                t = template(py.magic.autopath().dirpath() / 
                                'templates/quit.html')
                self.serve_data('text/html', t.render_to_string({}))
                
        thread = py.std.thread.start_new_thread(self.run_server, 
                                                (XMameHTTPRequestHandler,))
        self.start_browser()
        # wait for the lock to be set
        while not self.lock.locked():
            py.std.time.sleep(0.1)
        # lock until the server has quit
        self.lock.acquire()

    def run_server(self, handler):
        server = py.std.BaseHTTPServer.HTTPServer(('', http_port), handler)
        self.lock.acquire()
        while 1:
            server.handle_request()
            if self.quit:
                print 'quitting'
                break
        self.lock.release()

    def start_browser(self):
        url = 'http://localhost:%s/' % http_port
        py.std.os.system('%s "%s" > /dev/null 2>&1 &' % (browserpath, url))

if __name__ == '__main__':
    r = CGIXMameRunner()
    r.run()

