#!/usr/bin/python

# Copyright (C) 2004-2005 Ross Burton <ross@burtonini.com>
#               2005-2007 Canonical
#
# Authors:
#  Ross Burton
#  Michael Vogt
#  Sebastian Heinlein
#  Ian Jackson
#  Niran Babalola
#
# 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 MERCHANTABILITY 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 os
import gdbm
import gconf
import errno
from gettext import gettext as _

class ActivationStyle:
    # Ideally this base class would be in AppInstall or Menu or some
    # such, but that would involve doing `import AppInstall' before we've
    # decided to actually run it, which is too slow for the time between a
    # user clicking on a file and us saying we can't open it.  So we have
    # a full one here and another smaller dummy version in Menu.py too.

    def __init__(self):
        self.selectFilter = None
    def isSpecific(self):
        " return True if we are we in a not-normal mode "
        return False
    def menuCacheName(self):
        """ Each activation-mode can have a different cached menu
            This is useful for e.g. codec activation style because
            there are only very few codecs so it makes sense to read
            only the subset of the menu with codec information
        """
        return "menu.p"
    def searchString(self):
        return None
    def isApproved(self, component, package):
        return True
    def userApprovedNotify(self):
        pass
    def changesSuccessfulNotify(self):
        pass
    def quitHook(self):
        pass

class SearchActivationStyle(ActivationStyle):
    def __init__(self,dictname,datadir,cachedir):
	self._dn = dictname
	self._cachedir = cachedir
	self._userapproved = False
	self._changessuccessful = False

        # read the packages whitelist (from the files)
        dict = {}
        for d in (datadir, '/etc/gnome-app-install'):
            try: f = open(d+'/packages-whitelist')
            except IOError, e:
                if e.errno == errno.ENOENT: continue
                raise
            for l in f:
                v = l.strip()
                if v.startswith('#'): continue
                dict[v] = True
        self._wl_packages = dict

        # read the component whitelist (from gconf)
        client = gconf.client_get_default()
        l = client.get_list("/apps/gnome-app-install"+
                            "/mime-whitelist-components",
                            gconf.VALUE_STRING)
        dict = {}
        for v in l: dict[v] = True
        self._wl_components = dict

	db_name = self._cachedir+'/gai-'+self._dn+'-map.gdbm'
        self._db = gdbm.open(db_name, 'rfu')

    def menuCacheName(self):
        return "mime_menu.p"

    def lookup(self,string):
        # look up our entry and bomb if not found
        try: value = self._db[string]
        except KeyError: value = ''

        unapproved = False
        for e in value.split():
            (component,package) = e.split('/',1)
            if self.isApproved(component,package): return (True,True,None,None)
            unapproved = True

	if unapproved: (abbrev,msg) = (
	    _("no suitable application"),
	    _("No application suitable for automatic installation is"
	      " available for handling this kind of file."))
        else: (abbrev,msg) = (
	    _("no application found"),
	    _("No application is known for this kind of file."))

	return (False,unapproved,abbrev,msg)

    def isApproved(self, component, package):
        return (self._wl_components.has_key(component) or
            self._wl_packages.has_key(package))
    def isSpecific(self): return True
    def userApprovedNotify(self): self._userapproved = True
    def changesSuccessfulNotify(self): self._changessuccessful = True

class CodecSearchActivationStyle(SearchActivationStyle):
    def __init__(self, datadir, cachedir):
       SearchActivationStyle.__init__(self,'codec',datadir,cachedir)
       self._codecs = []

    def menuCacheName(self):
        return "codec_menu.p"

    def addSearchTerm(self, string):
       self._codecs.append(string)
       (ok,unapproved,abbrev,msg) = self.lookup(string)
       if ok: return
       print >>sys.stderr, abbrev
       #sys.exit(9 - unapproved)
       sys.exit(1)
        
    def searchString(self): return " ".join(self._codecs)
    def selectFilter(self, menu): return menu._codecMatch

    def quitHook(self):
       if not self._userapproved:
               print >>sys.stderr, _("additional codec installation declined")
               #sys.exit(3)
               sys.exit(4)
       if not self._changessuccessful:
               print >>sys.stderr, _("additional codec installation failed")
               sys.exit(2)

class MimeSearchActivationStyle(SearchActivationStyle):
    def __init__(self, string, datadir, cachedir, uri, duri=None):
	SearchActivationStyle.__init__(self,'mime',datadir,cachedir)
	self._uri = uri
	self._string = string
	(ok,unapproved,abbrev,msg) = self.lookup(string)
	if ok: return

        if uri:
            import gtk
            import AppInstall

	    abbrev = "Error: %s" % abbrev
	    msg = "Cannot open %s: %s" % (duri, msg)
            dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
                gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
            dlg.set_title(abbrev)
            dlg.run()
            dlg.destroy()
            sys.exit(6)
        else:
            print >>sys.stderr, "not offering packages for "+string
            if unapproved:
                print >>sys.stderr, "only unapproved: "+string
                sys.exit(5)
            else:
                print >>sys.stderr, "no entry in mime map"
                sys.exit(4)

    def searchString(self): return self._string
    def selectFilter(self, menu): return menu._mimeMatch

    def quitHook(self):
        if self._uri and self._changessuccessful:
            import gnomevfs
            gnomevfs.url_show(self._uri)

def askConfirmation(summary, msg, transient_for=None):
    import gtk
    dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
                            gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
                            summary)
    dlg.format_secondary_text(msg)
    if transient_for:
        parent = gtk.gdk.window_foreign_new(transient_for)
        dlg.realize()
        dlg.window.set_transient_for(parent)
    res = dlg.run()
    dlg.destroy()
    while gtk.events_pending():
        gtk.main_iteration()
    if res == gtk.RESPONSE_YES:
        return True
    return False

def startGnomeCodecInstall(argv, datadir, cachedir, transient_for):
    as = CodecSearchActivationStyle(datadir, cachedir)
    for arg in argv:
        # we get a string like this:
        #gstreamer.net|0.10|totem|DivX MPEG-4 Version 5 decoder|decoder-video/x-divx, divxversion=(int)5 (DivX MPEG-4 Version 5 decoder)
        try:
            (origin,version,app,descr,search_token) = arg.split("|")
        except ValueError, e:
            sys.stderr.write("invalid commandline '%s' (%s)\n" % (arg, e))
            import gtk
            abbrev = _("Invalid commandline")
            msg = _("'%s' does not understand the commandline argument '%s'" % (sys.argv[0], arg))
            dlg = gtk.MessageDialog(None,
                                    gtk.DIALOG_MODAL,
                                    gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
                                    abbrev)
            dlg.format_secondary_text(msg)
            dlg.run()
            dlg.destroy()
            sys.exit(1)
        if "," in search_token:
            search_token = search_token.split(",")[0]
        as.addSearchTerm("%s:%s" % (version, search_token))
        if not askConfirmation(_("Search for suitable codec?"),
			_("The required software to play this "
                          "file is not installed. You need to install "
                          "suitable codecs to play "
                          "media files. Do you want to search for a codec "
                          "that supports the selected file?\n\n"
                          "The search will also include software which is not "
                          "officially supported by Ubuntu."),
                          transient_for):
            sys.exit(4)
        # ok, lets do it!
        from AppInstall.AppInstall import AppInstall
        from AppInstall.Menu import SHOW_ALL
        app = AppInstall(datadir, desktopdir, cachedir,
                         sys.argv, as, transient_for, addon_cd)
        # show all codecs (we warned the user earlier)
        app.hbox_search_show.hide()
        app.menu.filter = SHOW_ALL
        app.menu._refilter()
        app.run()
        sys.exit(0)

if __name__ == "__main__":

    #datadir = "/tmp/xxx/share/gnome-app-install"	
    datadir = "/usr/share/gnome-app-install"
    desktopdir = "/usr/share/app-install"
    cachedir = "/var/cache/app-install"

    # This is a very poor option parser.
    # I tried using optparse here but:
    #	$ time python -c 'import optparse'
    #	real    0m0.134s
    #	user    0m0.120s
    #	sys     0m0.012s
    def badusage():
        print >>sys.stderr, "gnome-app-install bad usage"
        sys.exit(127)

    transient_for = None
    if len(sys.argv)>1 and sys.argv[1].startswith('--transient-for='):
	try: transient_for = int(sys.argv[1][16:],0)
	except ValueError: badusage()
        # clean out the handled argument
	del(sys.argv[1])

    if len(sys.argv)>1 and sys.argv[1].startswith('--test-me-harder'):
        from AppInstall.AppInstall import AppInstall
        app = AppInstall(datadir, desktopdir, cachedir,
                         sys.argv, ActivationStyle(), transient_for, None)
        while True:
            app.on_install_toggle(None, (0,) )
            app.applyChanges()

    # addon-cd
    addon_cd = None
    if len(sys.argv)>1 and sys.argv[1].startswith('--addon-cd='):
	addon_cd = sys.argv[1][len('--addon-cd='):]
	del(sys.argv[1])

    # activation style
    if os.path.basename(sys.argv[0]) == "gnome-codec-install":
        # special startup mode, we will not return from this
        startGnomeCodecInstall(sys.argv[1:], datadir, cachedir, transient_for)
    elif len(sys.argv)==1:
        as = ActivationStyle()
    elif sys.argv[1].startswith('--mime-type='):
        if len(sys.argv)==2: (uri, duri) = (None, None)
        elif len(sys.argv)==3: (uri, duri) = sys.argv[2], sys.argv[2];
        elif len(sys.argv)==4: (uri,duri) = sys.argv[2:]
        else: badusage()
        mime_type = sys.argv[1][12:]
        as = MimeSearchActivationStyle(mime_type, datadir, cachedir,
                                       uri, duri)
    else: badusage()
    # /Finish parsing the options

    # We have already bombed out if the quick test fails.  We do this
    #  import only now so that quick tests are really quick.
    from AppInstall.AppInstall import AppInstall
    app = AppInstall(datadir, desktopdir, cachedir,
		     sys.argv, as, transient_for, addon_cd)
    app.run()
