# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------
# mame_cache.py - Module for caching MAME rom information for Freevo.
# -----------------------------------------------------------------------
# $Id: mame_cache.py 9961 2007-10-13 16:02:19Z duncan $
#
# Notes: This contains some rominfo code from videogame.py.
# Todo:
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------


import sys
import random
# modifications made 21 Jul 2004 by vputz
#
# xmame version 0.84 made --listinfo obsolete; all rom list information
# is obtained by --listxml, which produces an XML feed of rom information.
# This current file switches based on the xmame version number; for 0.83 and
# above, it parses --listxml using a SAX content handler.  Run time is
# essentially the same and it also picks up an additional game that the
# original version missed (--listinfo version saved a rom when a new rom
# started, so the last rom in the list was dropped, not that anyone noticed).
# included are some small functions I used to test compatibility of the romsets
# generated by either method.  Both appear to be identical save that "Alien3"
# in the XML version tried to encode the "3" as a superscript and so it comes
# out as "Alien\xb3"; otherwise, it's fine.
import time, os, string

# Classes to keep track of our roms
import mame_types

# Configuration file.
import config

# Various utilities
import util

# RegExp
import re

from gui.PopupBox import PopupBox

TRUE = 1
FALSE = 0


#
# Lets get a MameRomList if one is available from disk.  If not
# then we shall return an empty one.
#
def getMameRomList():
    file_ver = None
    mameRomList = None

    if os.path.isfile(config.GAMES_MAME_CACHE):
        mameRomList = util.read_pickle(config.GAMES_MAME_CACHE)

        try:
            file_ver = mameRomList.TYPES_VERSION
        except AttributeError:
            print 'The cache does not have a version and must be recreated.'

        if file_ver != mame_types.TYPES_VERSION:
            print (('MameRomList version number %s is stale (new is %s), must ' +
                    'be reloaded') % (file_ver, mame_types.TYPES_VERSION))
        else:
            _debug_('Got MameRomList (version %s).' % file_ver)

    if mameRomList == None:
        mameRomList = mame_types.MameRomList()

    print "MameRomList has %s items." % len(mameRomList.getMameRoms())
    return mameRomList


#
# function to save the cache to disk
#
def saveMameRomList(mameRomList):

    if not mameRomList or mameRomList == None:
        mameRomList = mame_types.MameRomList()

    util.save_pickle(mameRomList, config.GAMES_MAME_CACHE)


#
# We should keep mameRomList up to date.
# This function takes in a list of files and makes sure
# the cache has any relevant information.
#
def mameRomListFromListinfo(mame_cmd):
    # This method of running xmame --listinfo and parsing the output was
    # borrowed from pyrecade - http://pyrecade.sf.net.

    try:
        listinfo = os.popen(mame_cmd + ' --listinfo', 'r')
    except:
        print 'Unable to get mame listinfo.'
        return FALSE

    newRom = mame_types.MameRom()
    cache = {}

    for line in listinfo.readlines():
        if re.compile("^\tname").match(line):
            newRom.name = re.compile("^\tname (.*)").match(line).group(1)
        elif re.compile("^\tdescription").match(line):
            newRom.description = re.compile("^\tdescription (.*)").match(line).group(1)[1:-1]
        elif re.compile("^\tyear").match(line):
            newRom.year = re.compile("^\tyear (.*)").match(line).group(1)
        elif re.compile("^\tmanufacturer").match(line):
            newRom.manufacturer = re.compile("^\tmanufacturer (.*)").match(line).group(1)[1:-1]
        elif re.compile("^\tcloneof").match(line):
            newRom.cloneof = re.compile("^\tcloneof (.*)").match(line).group(1)
        elif re.compile("^\tromof").match(line):
            newRom.romof = re.compile("^\tromof (.*)").match(line).group(1)
        elif re.compile("^game \(").match(line):
            # We've reached a new game so dump everthing we have so far
            if newRom.name:
                # add the new rom to the cache.
                cache[newRom.name] = newRom
                # make a new mameRom
                newRom = mame_types.MameRom()

    listinfo.close()

    mameRomList = mame_types.MameRomList()
    mameRomList.setMameRoms(cache)
    return mameRomList

from xml.sax.handler import ContentHandler
from xml.sax import make_parser


class romHandler( ContentHandler ):
    def __init__( self ) :
        self.newRom = mame_types.MameRom()
        self.cache = {}
        self.encoding = "ascii"

    def convert_text( self, chrs ) :
        return chrs.encode( self.encoding, 'replace' )

    def startElement( self, name, attrs) :
        self.currentName = name
        if ( name == "game" ) :
            if attrs.has_key( "runnable" ) and self.convert_text( attrs.get("runnable") ) == "no" :
                self.newRom = None
            else :
                self.newRom = mame_types.MameRom()
                self.newRom.name = self.convert_text( attrs.get( "name" ) )
                if attrs.has_key( "cloneof" ) :
                    self.newRom.cloneof = self.convert_text( attrs.get( "cloneof" ) )
                if attrs.has_key( "romof" ) :
                    self.newRom.romof = self.convert_text( attrs.get( "romof" ) )
                self.newRom.description = ''
                self.newRom.manufacturer = ''

    def endElement( self, name ) :
        self.currentName = None
        if ( name == "game" and self.newRom != None ) :
            self.cache[ self.newRom.name ] = self.newRom

    def characters( self, chrs ) :
        if self.newRom != None :
            if ( self.currentName == "description" ) :
                self.newRom.description = self.newRom.description + self.convert_text( chrs )
            elif ( self.currentName == "year" ) :
                if "year" not in dir( self.newRom ) :
                    self.newRom.year = ''
                self.newRom.year = self.newRom.year + self.convert_text( chrs )
            elif ( self.currentName == "manufacturer" ) :
                self.newRom.manufacturer = self.newRom.manufacturer + self.convert_text( chrs )

def mameRomListFromListxml( mame_cmd ) :
    try:
        os.system(mame_cmd + ' -listxml -out /tmp/roms')
        listinfo = os.popen('cat /tmp/roms', 'r')
    except:
        print 'Unable to get mame listinfo.'
        return FALSE

    handler = romHandler()
    parser = make_parser()
    parser.setContentHandler( handler )

    parser.parse( listinfo )

    listinfo.close()
    try:
        os.remove('/tmp/roms')
    except OSError:
        pass

    mameRomList = mame_types.MameRomList()
    mameRomList.setMameRoms(handler.cache)
    return mameRomList

def roms_equal( lhs, rhs ) :
    return ( (lhs.name == rhs.name) and
             (lhs.description == rhs.description) and
             (lhs.year == rhs.year) and
             (lhs.manufacturer == rhs.manufacturer) and
             (lhs.cloneof == rhs.cloneof) and
             (lhs.romof == rhs.romof) )

def roms_compare( lhs, rhs ) :
    if not roms_equal( lhs, rhs ) :
        print "%s not equal to %s" % ( lhs.name, rhs.name )
        if ( lhs.name != rhs.name ) :
            print( "Names: '%s' vs '%s'" % (lhs.name, rhs.name) )
        if ( lhs.manufacturer != rhs.manufacturer ) :
            print( "Manufacturers: '%s' vs '%s'" % (lhs.manufacturer, rhs.manufacturer) )
        if ( lhs.description != rhs.description ) :
            print( "Descriptions: '%s' vs '%s'" % (lhs.description, rhs.description) )
        if ( lhs.year != rhs.year ) :
            print( "Years: '%s' vs '%s'" % (lhs.year, rhs.year) )
        if ( lhs.cloneof != rhs.cloneof ) :
            print( "Cloneofs: '%s' vs '%s'" % (lhs.cloneof, rhs.cloneof) )
        if ( lhs.romof != rhs.romof ) :
            print( "Romofs: '%s' vs '%s'" % (lhs.romof, rhs.romof) )

def romsets_compare( lhs, rhs ) :
    lkeys = lhs.mameRoms.keys()
    rkeys = rhs.mameRoms.keys()
    lkeys_not_in_rkeys = [ x for x in lkeys if x not in rkeys ]
    rkeys_not_in_lkeys = [ x for x in rkeys if x not in lkeys ]
    print "lhs keys not in rhs keys: %s" % lkeys_not_in_rkeys
    print "rhs keys not in lhs keys: %s" % rkeys_not_in_lkeys
    for key in lkeys :
        if key in rkeys :
            roms_compare( lhs.mameRoms[ key ], rhs.mameRoms[ key ] )

def xmame_semimajor_version( mame_cmd ) :
    try:
        verinfo = os.popen(mame_cmd + ' --version', 'r')
    except:
        print 'Unable to get mame version.'
        return FALSE

    this_re = re.compile( ".*version\s+(\d+)\.(\d+)\s+" )

    for line in verinfo.readlines() :
        if this_re.match( line ) :
            result = int( this_re.match( line ).group( 2 ) )

    verinfo.close()
    return result



def updateMameRomList( mame_cmd ) :
    if xmame_semimajor_version( mame_cmd ) == FALSE :
        return FALSE
    if xmame_semimajor_version(mame_cmd) >= 83 :
        _debug_("updating via listxml")
        mameRomList = mameRomListFromListxml( mame_cmd )
    else :
        _debug_("updating via listinfo")
        mameRomList = mameRomListFromListinfo( mame_cmd )
    saveMameRomList(mameRomList)
    return TRUE


#
# This will return a list of things relevant to MameItem based on
# which mame files we have cached.  It ignores files we don't.
# Returns: title, filename, and image file for each mame_file.
#
def getMameItemInfoList(mame_files, mame_cmd):
    items = []
    rm_files = []

    print "Call MAME command : %s" % mame_cmd
    # Only build the cache if it doesn't exis.
    if not os.path.isfile(config.GAMES_MAME_CACHE):
        waitmsg = PopupBox(text=_('Generating MAME cache, please wait.'))
        waitmsg.show()
        mame_ok = updateMameRomList(mame_cmd)
        waitmsg.destroy()

        if not mame_ok:
            return (mame_files, [])

    mameRomList = getMameRomList()
    roms = mameRomList.getMameRoms()

    for romfile in mame_files:
        key = os.path.splitext(os.path.basename(romfile))[0]
        if roms.has_key(key):
            rom = roms[key]
            info = { 'manufacturer': rom.manufacturer,
                     'name': rom.name,
                     'description': rom.description,
                     'year': rom.year,
                     'cloneof': rom.cloneof,
                     'romof': rom.romof }
            items += [(rom.description, romfile, None, info)]
            rm_files.append(romfile)

    return (rm_files, items)


def getMameItemInfo(mame_file):
    if not os.path.isfile(config.GAMES_MAME_CACHE):
        return
    mameRomList = getMameRomList()
    roms = mameRomList.getMameRoms()
    key = os.path.splitext(os.path.basename(mame_file))[0]
    if roms.has_key(key):
        return roms[key]
    else:
        return
