#!/usr/bin/python
#
# Copyright (c) 2007 Canonical
#
# AUTHOR:
# Michael Vogt <mvo@ubuntu.com>
#
# This file is part of AptUrl
#
# AptUrl 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.
#
# AptUrl 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 AptUrl; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

import sys
import apt
import apt_pkg
import subprocess

import os
import os.path

import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade

from tempfile import NamedTemporaryFile
from gettext import gettext as _
from optparse import OptionParser

import aptsources.distro
from aptsources.sourceslist import SourcesList, is_mirror

import gettext
import time
import thread

from AptUrl import Parser

# return codes
(RESULT_OK,
 RESULT_CANCELT,
 RESULT_ERROR) = range(3)


def error(summary, msg=""):
    d = gtk.MessageDialog(parent=None,
                          flags=gtk.DIALOG_MODAL,
                          type=gtk.MESSAGE_ERROR,
                          buttons=gtk.BUTTONS_CLOSE)
    d.set_title("")
    d.set_markup("<big><b>%s</b></big>\n\n%s" % (summary, msg))
    d.realize()
    d.window.set_functions(gtk.gdk.FUNC_MOVE)
    d.run()
    d.destroy()

def question(header, body):
    dia = gtk.MessageDialog(None, 0, gtk.MESSAGE_QUESTION,
                            gtk.BUTTONS_YES_NO, "")
    dia.set_markup("<b><big>%s</big></b>" % header)
    dia.set_icon(gtk.icon_theme_get_default().load_icon('deb', 16, False))
    dia.format_secondary_text(body)
    res = dia.run()
    dia.destroy()
    if res != gtk.RESPONSE_YES:
        return False
    return True

def wait_for_p(p, lock):
    " helper for the thread to wait for process p to finish "
    p.wait()
    lock.release()

def get_dist():
    return subprocess.Popen(["lsb_release","-c","-s"],stdout=subprocess.PIPE).communicate()[0].strip()

def enable_section(apturl, cache):
    added = False

    sourceslist = SourcesList()
    distro = aptsources.distro.get_distro()
    distro.get_sources(sourceslist)

    for component in apturl.section:
        if (component in distro.enabled_comps and
            cache.has_key(apturl.package)):
            continue

        if not question(_("Enable additional components"),
                        gettext.ngettext("Do you want to enable the following "
                                         "component: '%s'?",
                                         "Do you want to enable the following "
                                         "components: '%s'?",
                                         len(apturl.section)) % ",".join(apturl.section)):
            return RESULT_CANCELT

        cmd = ["gksu", "--desktop",
               "/usr/share/applications/gnome-app-install.desktop",
               "--",
               "gnome-app-install-helper", "-e", component]
        try:
            output = subprocess.Popen(cmd,
                                      stdout=subprocess.PIPE).communicate()[0]
        except OSError, e:
            print >>sys.stderr, "Execution failed:", e
            return RESULT_ERROR
        #FIXME: Very ugly, but gksu doesn't return the correct exit states
        if output != "Enabled the %s component\n" % component:
            return RESULT_ERROR
        added = True

    if added:
        run_update(dia)
    return RESULT_OK
    

def run_update(dia):
        p = subprocess.Popen(['gksu',
                              '--desktop',
                              '/usr/share/applications/synaptic.desktop',
                              '--',
                              '/usr/sbin/synaptic',
                              '--hide-main-window',
                              '--non-interactive',
                              '--update-at-startup',
                              ])
        wait_for_synaptic(dia, p)

def run_install(dia, apturl):
        # run synaptic
        temp = NamedTemporaryFile()
        temp.write("%s\t install\n" % apturl.package)
        temp.flush()
        #print temp.name
        p = subprocess.Popen(['gksu',
                              '--desktop',
                              '/usr/share/applications/synaptic.desktop',
                              '--',
                              '/usr/sbin/synaptic',
                              '--hide-main-window',
                              '--non-interactive',
                              '--set-selections-file', temp.name
                              ])
        wait_for_synaptic(dia, p)
        temp.close()    

def wait_for_synaptic(dia, p):
        # wait for synaptic
        lock = thread.allocate_lock()
        lock.acquire()
        thread.start_new_thread(wait_for_p, (p, lock))

        dia.set_sensitive(False)
        while lock.locked():
            while gtk.events_pending():
                gtk.main_iteration()
            time.sleep(0.01)
        dia.set_sensitive(True)
        return True

def debline(apturl):
    return "%s %s %s" % (apturl.repo_url, apturl.dist, " ".join(apturl.section))

def aptsources_file(apturl):
    dir = apt_pkg.Config.FindDir("Dir::Etc::sourceparts")
    file = apt_pkg.URItoFileName(debline(apturl))+".list"
    file = file.replace("%20","__")
    return os.path.join(dir, file)

def enable_repo(apturl):
    source = aptsources_file(apturl)
    if os.path.exists(source):
        return True
    temp = NamedTemporaryFile()
    temp.write("# added by apturl\n")
    temp.write("deb %s\n" % debline(apturl))
    temp.flush()
    # copy channel file in place
    cmd = ["gksu",
           "--desktop", "/usr/share/applications/gnome-app-install.desktop",
           "--",
           "install", "--mode=644","--owner=0",temp.name, source
          ]
    subprocess.call(cmd)
    # install the key as well (if needed)
    if apturl.keyfile:
        cmd = ["gksu",
               "--desktop",
               "/usr/share/applications/gnome-app-install.desktop",
               "--",
               "apt-key", "add",
               "/usr/share/app-install/channels/%s" % apturl.keyfile]
        subprocess.call(cmd)
    return True
    
if __name__ == "__main__":
    localesApp="apturl"
    localesDir="/usr/share/locale"
    gettext.bindtextdomain(localesApp, localesDir)
    gettext.textdomain(localesApp)

    parser = OptionParser()
    parser.add_option("-p", "--http-proxy", dest="http_proxy",
                      default=None, help="use http proxy")
    (options, args) = parser.parse_args()

    gtk.init_check()

    # global return code
    ret = RESULT_OK

    # eval and add proxy
    if options.http_proxy is not None:
        proxy = options.http_proxy
        if not ":" in proxy:
            proxy += ":3128"
        os.environ["http_proxy"] = "http://%s" % proxy

    try:
        apturl_list = Parser.parse(args[0])
    except IndexError, e:
        error(_("Need a url to continue, exiting"))
        sys.exit(1)
    except Parser.InvalidUrlException, e:
        error(_("Invalid url: '%s' given, exiting") % sys.argv[1])
        sys.exit(1)
        
    # get cache and check if its ok
    cache = apt.Cache()
    if cache._depcache.BrokenCount > 0:
        err_header = _("Software index is broken")
        err_body = _("This is a major failure of your software " 
                     "management system. Please check for broken packages "
                     "with synaptic, check the file permissions and "
                     "correctness of the file '/etc/apt/sources.list' and "
                     "reload the software information with: "
                     "'sudo apt-get update' and 'sudo apt-get install -f'."
                     )
        error(err_header, err_body)
        sys.exit(RESULT_ERROR)

    # create empty dialog
    dia_xml = gtk.glade.XML('/usr/share/apturl/apturl.glade', 
                            'confirmation_dialog')
    dia = dia_xml.get_widget('confirmation_dialog')
    dia.realize()

    # now go over the url list
    for apturl in apturl_list:
        if not (apturl.schema == "apt" or apturl.schema == "apt+http"):
            error(_("Can not deal with protocol '%s' ") % apturl.schema)
            continue

        # FIXME: ask before adding stuff to the sources.list
        # check if we need to fiddle with the sources.list
        if apturl.section and apturl.repo_url is None:
            ret = enable_section(apturl, cache)
            if ret != RESULT_OK:
                error(_("Enabling '%s' failed") % ",".join(apturl.section))
                continue
            cache = apt.Cache()
        # FIXME2: this has security implications, not enabled
        #elif apturl.repo_url is not None:
        #    if not enable_repo(apturl):
        #        error(_("Enabling '%s' failed") % apturl.repo_url)
        #        continue
        #    run_update(dia)

        if not cache.has_key(apturl.package):
            error(_("Can not find '%s' ") % apturl.package)
            continue
        if cache[apturl.package].isInstalled and apturl.minver is None:
            error(_("Package '%s' is already installed") % apturl.package)
            continue
        
        # populate the dialog
        header = _("Install additional software?")
        body = _("Do you want to install the package '%s' ?") % apturl.package
        dia.set_title('')
        header_label = dia_xml.get_widget('header_label')
        header_label.set_markup("<b><big>%s</big></b>" % header)
        body_label = dia_xml.get_widget('body_label')
        body_label.set_label(body)
        description_text_view = dia_xml.get_widget('description_text_view')
        description = gtk.TextBuffer()
        desc = "%s\n\n%s" % (cache[apturl.package].summary,
                             cache[apturl.package].description)
        description.set_text(desc)
        description_text_view.set_buffer(description)
        dia.set_icon(gtk.icon_theme_get_default().load_icon('deb', 16, False))
        res = dia.run()
        if res != gtk.RESPONSE_YES:
            ret = RESULT_CANCELT
            dia.hide()
            continue

        # try to install it
        try:
            cache[apturl.package].markInstall()
        except SystemError, e:
            error(_("Can not install '%s' (%s) ") % (apturl.package, e))
            continue
        if apturl.minver is not None:
            verStr = cache[apturl.package].candidateVersion
            if apt_pkg.VersionCompare(verStr, apturl.minver) < 1:
                error(_("Package '%s' requests minimal version '%s', but "
                        "only '%s' is available") % (apturl.package,
                                                     apturl.minver,
                                                     verStr))
                continue

        # install it
        run_install(dia, apturl)

        # check if the package got actually installed
        cache = apt.Cache()
        pkg = cache[apturl.package]
        if (not pkg.isInstalled or
            pkg._pkg.CurrentState != apt_pkg.CurStateInstalled or
            cache._depcache.BrokenCount > 0):
            ret = RESULT_ERROR

        if apturl.repo_url is not None:
            header = _("Remove software channel?")
            body = _("For installing '%s' the software channel '%s' was "
                     "added, do you want to remove it again?") % (apturl.package, apturl.repo_url)
            res = question(header, body)
            if res:
                subprocess.call(["gksu","rm",aptsources_file(apturl)])

        # cleanup
        dia.hide()

    
    # return values
    sys.exit(ret)
