#!/usr/bin/python -tt
#
# Copyright 2005-2007  Red Hat, Inc.
# Copyright 2007       Debarshi Ray
#
# Jeremy Katz <katzj@redhat.com>
# Debarshi Ray <rishi@fedoraproject.org>
#
# 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; version 2 only
#
# 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 Library 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 os, sys
import optparse
import gettext
import shutil
import tarfile

try:
    import gtk
    import gtk.glade
    import gtk.gdk as gdk
    import gobject
except Exception, e:
    print >> sys.stderr, "Unable to import modules.  Maybe you're not running under X?"
    sys.exit(1)

from rhpl.exception import installExceptionHandler

from yum.constants import *
from yum.packages import comparePoEVR

from pirut import *
from pirut.constants import *
from pirut.Errors import *

I18N_DOMAIN = "opyum"
if os.path.exists("data/Opyum.glade"):
    gladefn = "data/Opyum.glade"
else:
    gladefn = "/usr/share/opyum/ui/Opyum.glade"

searchgladefn = "/usr/share/pirut/ui/PackageSearch.glade"

if os.path.exists("data/opyum.png"):
    iconfn = "data/opyum.png"
else:
    iconfn = "/usr/share/icons/hicolor/48x48/apps/opyum.png"

t = gettext.translation(I18N_DOMAIN, "/usr/share/locale", fallback = True)
_ = t.lgettext

# lame main loop runner... this should probably just be where we need it
def _runGtkMain(*args):
    while gtk.events_pending():
        gtk.main_iteration()

def _fileChooserLocationPopup(widget, path):
    """
    """

    widget.select_filename(path)


BROWSE_PAGE = 0
SEARCH_PAGE = 1
LIST_PAGE = 2
    

class Profile:
    """
    The class to represent the profile of a system.
    """

    def __init__(self, id, name, path):
        """
        Initialize the profile.
        """

        self.id = id
        self.name = name
        self.path = path
        self.rpmdb = path + "/var/lib/rpm"
    
    def delete(self):
        """
        Delete the profile.
        """

        if not os.access(self.path, os.W_OK):
            return 0

        shutil.rmtree(self.path)

class PackageManager(GraphicalYumBase):
    """
    The basic package manager class to do the back-end work.
    """
    
    def __init__(self, rum_dir, conf_file):
        """
        Initialize the package manager.
        """

        self.rumDir = rum_dir
        if not os.access(self.rumDir, os.F_OK):
            os.makedirs(self.rumDir)
        
        self.cacheDir = self.rumDir + "/cache"
        if not os.access(self.cacheDir, os.F_OK):
            os.makedirs(self.cacheDir)

        self.tmpDir = self.rumDir + "/.tmp"

        self.confFile = conf_file
        self.installPkgs = []
        self.missingDeps = []
        self.currentProfile = None
        self.currentProfileIter = None
        self.profiles = self.findProfiles()
        # self.profileStore = None

        # Setting up main window.
        self.xml = gtk.glade.XML(gladefn, domain=I18N_DOMAIN)
        self.mainwin = self.xml.get_widget("PackageManagerWindow")
        # self.mainwin.set_icon_from_file(iconfn)
        self._connectSignals()
        self.mainwin.connect("delete_event", self.quit)
        self._showPackageType = SHOW_ALL

        # Setting up progress bar and status bar.
        self.progressbar = self.xml.get_widget("ProgressBar")
        self.statusbar = self.xml.get_widget ("StatusBar")

        # Setting up profile dialog.
        self.profileDialog = self.xml.get_widget("profileDialog")
        self.profileView = self.xml.get_widget("profileView")

        column = gtk.TreeViewColumn(None, None)
        cbr = gtk.CellRendererToggle()
        column.pack_start(cbr, False)
        column.add_attribute(cbr, "active", 1)
        cbr.set_radio(True)
        cbr.connect("toggled", self._selectProfile)
        self.profileView.append_column(column)

        column = gtk.TreeViewColumn(None, None)
        txtr = gtk.CellRendererText()
        column.pack_start(txtr, False)
        column.add_attribute(txtr, 'markup', 2)
        self.profileView.append_column(column)
        self.profileView.connect("row-activated", self._selectProfile)

        self.profileStore = gtk.ListStore(gobject.TYPE_PYOBJECT,
                                          gobject.TYPE_BOOLEAN,
                                          gobject.TYPE_STRING)
        for each_profile in self.profiles:
            desc = gobject.markup_escape_text(each_profile.name)
            desc = "<b>%s</b>\n<i>Other details: </i>" % desc
            self.profileStore.append([each_profile, each_profile == self.currentProfile, desc])

        self.profileView.set_model(self.profileStore)

    def findProfiles (self):
        """
        Find all available profiles.
        """

        self.profiles = []
        for each_dir in os.listdir(self.rumDir):
            rpmdb_dir = self.rumDir + "/" + each_dir + "/var/lib/rpm"
            if os.access(rpmdb_dir, os.R_OK):
                profile_name = self.getProfileName(each_dir)
                profile = Profile(each_dir, profile_name, self.rumDir + "/" + each_dir)
                self.profiles.append(profile)

        return self.profiles

    def getProfileName(self, id):
        """
        Gets the name of a profile from its ID.
        """

        profile_data = self.rumDir + "/" + id + "/.profile"
        if os.access(profile_data, os.R_OK):
            f = open(profile_data, "r")
            profile_name = f.readlines()[0].rstrip()
            f.close()
            if not profile_name:
                profile_name = id
        else:
            profile_name = id

        return profile_name

    def setupProfile(self):
        """
        Set-up profile specific information.
        """
        
        self.rootDir = self.rumDir + "/" + self.currentProfile.id
        GraphicalYumBase.__init__(self, run_long=False, configfn=self.confFile,
                                  rootdir=self.rootDir)

        self.installPkgs = []
        self.missingDeps = []

        # FIXME: Dirty hack to create the intermediate directories on the way
        #        to self.conf.cachedir
        if not os.access(self.conf.cachedir, os.F_OK):
            os.makedirs(self.conf.cachedir)
            os.rmdir(self.conf.cachedir)
            os.symlink(self.cacheDir, self.conf.cachedir)

    def setupRepos(self, repos):
        """
        Set-up repository specific information.
        """

        for each_repo in repos:
            if each_repo[0] == "--enablerepo":
                self.repos.enableRepo(each_repo[1])
            else:
                self.repos.disableRepo(each_repo[1])

        # FIXME: Should this be self.reposSetup() from GraphicalYumBase?
        self.doRepoSetup()

    def _busyCursor(self, desensitize = True):
        self.mainwin.window.set_cursor(gdk.Cursor(gdk.WATCH))
        if desensitize:
            self.mainwin.set_sensitive(False)
        _runGtkMain()

    def _normalCursor(self):
        self.mainwin.window.set_cursor(None)
        self.mainwin.set_sensitive(True)        
        _runGtkMain()        

    def _connectSignals(self):
        sigs = { "on_PackageManagerWindow_destroy": self.quit,
                 "on_profileDialog_destroy": self._closeProfileDialog,
                 "on_quit_activate": self.quit,
                 "on_profiles_activate": self._profiles,
                 "on_repositories_activate": self._repoConfig,
                 "on_about_activate": self._about,
                 "on_search_activate": self._setSearch,
                 "on_browse_activate": self._setBrowse,
                 "on_list_activate": self._setList,
                 "on_export_activate": self._exportProfile,
                 "on_import_activate": self._importProfile,
                 "on_apply_activate": self._apply,
                 "on_mainBook_switch_page": self._pageSwitch,
                 "on_browseButton_clicked": self._showBrowse,
                 "on_searchButton_clicked": self._showSearch,
                 "on_listButton_clicked": self._showList,
                 "on_applyButton_clicked": self._apply,
                 "on_profileOkButton_clicked": self._closeProfileDialog,
                 "on_profileQuitButton_clicked": self.quit,
                 "on_deleteButton_clicked": self._deleteProfile,
                 "on_exportButton_clicked": self._exportProfile,
                 "on_importButton_clicked": self._importProfile,
                 "on_profileView_row_activated": self._selectProfile }
        self.xml.signal_autoconnect(sigs)

        self.pageMap = { BROWSE_PAGE: self._showBrowse,
                         SEARCH_PAGE: self._showSearch,
                         LIST_PAGE: self._showList }

    def _setList(self, *args):
        self.xml.get_widget("mainBook").set_current_page(LIST_PAGE)
    def _setSearch(self, *args):
        self.xml.get_widget("mainBook").set_current_page(SEARCH_PAGE)        
    def _setBrowse(self, *args):
        self.xml.get_widget("mainBook").set_current_page(BROWSE_PAGE)        

    def _pageSwitch(self, widget, data, num):
        if self.pageMap.has_key(num):
            self.pageMap[num]()

    def __destroyCurrent(self):
        mb = self.xml.get_widget("mainBook")
        pg = mb.get_data("thepage")
        if pg is not None:
            pg.destroy()
        mb.set_data("thepage", None)

    def _showSearch(self, *args):
        self.__destroyCurrent()
        
        x = gtk.glade.XML(searchgladefn, root="searchBox", domain=I18N_DOMAIN)
        w = x.get_widget("searchBox")
        plist = PirutPackageList(self)
        plist.connect("changed", self._searchPackageSelected,
                      x.get_widget("detailsTextView").get_buffer())
        plist.connect("toggled", self._setApply)
        plist.doneAdding(sort=False) # won't be populating with enough for time to matter
        w.pack_start(plist, True, True)

        b = x.get_widget("searchButton")
        e = x.get_widget("searchEntry")
        sb = x.get_widget("stopSearchButton")
        b.connect("clicked", self._searchClicked, x, plist)
        e.connect("activate", self._searchClicked, x, plist)
        sb.connect("clicked", self._stopSearch, x)
        self.searching = False

        self.xml.get_widget("searchFrame").add(w)
        self.xml.get_widget("mainBook").set_data("thepage", w)        
        gobject.idle_add(e.grab_focus)

    def _searchPackageSelected(self, plist, po, buffer):
        if po:
            desc = po.returnSimple('description') or ""
        else:
            desc = ""

        buffer.set_text(sanitizeString(desc))

    def _stopSearch(self, widget, xml):
        search = xml.get_widget("searchButton")
        stop = xml.get_widget("stopSearchButton")
        
        self.searching = False
        self.progressbar.set_fraction(0)
        self.progressbar.hide()
        self.statusbar.pop(0)
        search.show()
        stop.hide()
        self._normalCursor()
        _runGtkMain()

    def _searchClicked(self, widget, xml, plist):
        search = xml.get_widget("searchButton")
        entry = xml.get_widget("searchEntry")
        stop = xml.get_widget("stopSearchButton")
        
        t = entry.get_text()
        if t:
            t = t.strip()
        else:
            t = ""
        if len(t) == 0:
            return
        t_list = t.split(" ")
        fields = ['name', 'summary', 'description']

        plist.clear()
        self.searching = True
        search.hide()
        stop.show()
        self.progressbar.show()
        self.statusbar.push(0, _("Searching for '%s'") % (t))
        self._busyCursor(False)
        _runGtkMain()

        found = False
        for (po, res) in self.searchGenerator(fields, t_list):
            _runGtkMain()
            if not self.searching:
                found = True # well, not found... but we asked to stop
                break

            self.progressbar.pulse()
            
            if po.repoid == "installed":
                type = SHOW_INSTALLED
            else:
                type = SHOW_AVAIL
            if type != SHOW_INSTALLED and self.simpleDBInstalled(po.name, po.arch):
                continue
            plist.addPackage(po, type)
            found = True

        self._stopSearch(stop, xml) # it's done

        if not found:
            d = gtk.MessageDialog(self.mainwin, gtk.DIALOG_MODAL,
                                  gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
                                  _("No software matching search found."))
            d.run()
            d.destroy()

    def _showListProf(self, *args):
        fn = "prof.0"
        import hotshot, hotshot.stats
        prof = hotshot.Profile(fn)
        rc = prof.runcall(self._showListReal)
        prof.close()
        stats = hotshot.stats.load(fn)
        stats.strip_dirs()
        stats.sort_stats('time', 'calls')
        stats.print_stats(20)
        return rc

    def _showListReal(self, *args):
        global last
        last = None
        def cmppo(po1, po2):
            # XXX: a bit ugly, but keeps things a little more responsive
            global last
            if time.time() - last > 0.05:
                last = time.time()
                _runGtkMain()
                
            if po1.name.lower() < po2.name.lower():
                return -1
            elif po1.name.lower() > po2.name.lower():
                return 1
            return comparePoEVR(po1, po2)
        
        self.__destroyCurrent()

        x = gtk.glade.XML(searchgladefn, root="searchBox", domain=I18N_DOMAIN)
        w = x.get_widget("searchBox")
        x.get_widget("searchHbox").hide()
        plist = PirutPackageList(self)
        plist.connect("changed", self._searchPackageSelected,
                      x.get_widget("detailsTextView").get_buffer())
        plist.connect("toggled", self._setApply)
        w.pack_start(plist, True, True)
        
        self.xml.get_widget("listFrame").add(w)
        self.xml.get_widget("mainBook").set_data("thepage", w)

        self._busyCursor()
        pbar = PirutCancellableProgress(_("Reading software information"),
                                        self.mainwin)
        pbar.show()
        _runGtkMain()

        try:
            # look through the available packages...
            s = self.pkgSack.returnNewestByNameArch()
            _runGtkMain()

            # and the rpmdb ones
            r = self.rpmdb.returnPackages()
            _runGtkMain()
            pos = s + r
            num = 0
            tot = float(len(pos))

            # hey, if I sort myself, I can shave the time by a lot!
            last = time.time()
            pos.sort(cmppo)
            _runGtkMain()

            for po in pos:
                num += 1
                if po.repoid == "installed":
                    t = SHOW_INSTALLED
                elif self.simpleDBInstalled(po.name, po.arch):
                    continue
                else:
                    t = SHOW_AVAIL
                plist.addPackage(po, t)

                if (num/tot) > pbar.get_fraction() + 0.01:
                    pbar.set_fraction(num / tot)
                    _runGtkMain()

            plist.doneAdding(sort = False)
        except PirutCancelledError:
            # FIXME: could we do something better here?
            pass
        pbar.destroy()
        self._normalCursor()
        gobject.idle_add(plist.grab_focus)
        
    _showList = _showListReal

    def _showBrowse(self, *args):
        grpsel = PirutGroupSelector(self)
        self.__destroyCurrent()
        self.xml.get_widget("browseFrame").add(grpsel.vbox)
        self.xml.get_widget("mainBook").set_data("thepage", grpsel.vbox)
        grpsel.doRefresh()
    
    def _apply(self, *args):
        if len(self.tsInfo) == 0:
            d = gtk.MessageDialog(self.mainwin, gtk.DIALOG_MODAL,
                                  gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
                                  _("No package has been selected for download."))
            d.run()
            d.destroy()
            return

        self.tsInfo.makelists()
        d = PirutDetailsDialog(self.mainwin, gtk.MESSAGE_QUESTION,
                               [('gtk-cancel', gtk.RESPONSE_CANCEL),
                                (_("Continue"), gtk.RESPONSE_OK, 'gtk-ok')],
                               _("Package selections"),
                               _("You have selected the following software "
                                 "installations and removals."))

        b = gtk.TextBuffer()
        tag = b.create_tag('bold')
        tag.set_property('weight', pango.WEIGHT_BOLD)
        tag = b.create_tag('indented')
        tag.set_property('left-margin', 10)
        types=[(self.tsInfo.installed,_("Installing:")),
               (self.tsInfo.updated, _("Updating:")),
               (self.tsInfo.removed, _("Removing:"))]
        for (lst, strng) in types:
            if len(lst) > 0:
                i = b.get_end_iter()
                b.insert_with_tags_by_name(i, "%s\n" %(strng,), "bold")
                for txmbr in lst:
                    i = b.get_end_iter()
                    (n,a,e,v,r) = txmbr.pkgtup
                    b.insert_with_tags_by_name(i, "%s-%s-%s\n" % (n,v,r),
                                               "indented")
        d.set_details(buffer = b)
        d.expand_details()

        rc = d.run()
        d.destroy()
        if rc == gtk.RESPONSE_CANCEL:
            return

        create_yumpack_dialog = gtk.FileChooserDialog(_("Save As..."),
                                                      self.mainwin,
                                                      gtk.FILE_CHOOSER_ACTION_SAVE,
                                                      (gtk.STOCK_CANCEL,
                                                       gtk.RESPONSE_CANCEL,
                                                       gtk.STOCK_SAVE,
                                                       gtk.RESPONSE_OK))
        filter = gtk.FileFilter()
        filter.set_name(_("All files"))
        filter.add_pattern("*")
        create_yumpack_dialog.add_filter(filter)
        
        filter = gtk.FileFilter()
        filter.set_name(_("Supported files"))
        filter.add_pattern("*.tar")
        create_yumpack_dialog.add_filter(filter)
        create_yumpack_dialog.set_filter(filter)

        create_yumpack_dialog.set_current_name("Unsaved YumPack 1")
        response = create_yumpack_dialog.run()

        if response == gtk.RESPONSE_OK:
            file_name = create_yumpack_dialog.get_filename()
            if file_name[-4:] != ".tar":
                file_name = file_name + ".tar"

        create_yumpack_dialog.destroy()
        if response != gtk.RESPONSE_OK:
            return

        try:
            output = self.applyChanges(self.mainwin, downloadonly=True)
        except PirutError:
            self._undoDepInstalls()
            return

        self.createYumPack(file_name)

        d = PirutDetailsDialog(self.mainwin, gtk.MESSAGE_INFO,
                               [("gtk-ok", 0)],
                               "YumPack successfully created.")
        if output and len(string.join(output.values(), "")) > 0:
            d.format_secondary_text("Some warnings were given.")
            d.set_details(buffer = outputDictAsTextBuffer(output))
        d.run()
        d.destroy()

        self.reset()
        self.xml.get_widget("mainBook").set_current_page(BROWSE_PAGE)

    def _setApply(self, *args):
        # FIXME: it would be better if we just got a callback when
        # package was selected but that's a little ugly given
        # the way we import things
        try:
            w = self.xml.get_widget("applyButton")
            if len(self.tsInfo) == 0:
                w and w.set_sensitive(False)
            else:
                w and w.set_sensitive(True)
        except:
            pass
        return True

    def run(self):
        _runGtkMain()
        self._profiles(None)
        gobject.timeout_add(1000, self._setApply)

        self.mainwin.show()
        gtk.main()

    def doRefresh(self):
        enabled_repos = self.repos.listEnabled()
        for each_repo in self.repos.listEnabled():
            self.repos.disableRepo(each_repo.id)
            self.repos.enableRepo(each_repo.id)
        self.doRefreshRepos()

    def _profiles(self, widget, event=None):
        """
        Callback function to display the 'Profiles' dialog.
        """

        if self.currentProfile:
            self.xml.get_widget("profileOkButton").set_label("gtk-close")
            self.xml.get_widget("profileQuitButton").hide()
            self.xml.get_widget("hbuttonbox3").set_layout(gtk.BUTTONBOX_START)
        
        self.profileDialog.show()

    def _closeProfileDialog(self, widget, event=None):
        """
        Callback function to close the 'Profiles' dialog.
        It also updates the list of packages according to the selected profile.
        """

        if not self.currentProfileIter:
            d = gtk.MessageDialog(self.profileDialog, gtk.DIALOG_MODAL,
                                  gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
                                  _("No profile selected for use. Either "
                                    "select an existing one or import one for "
                                    "use."))
            d.run()
            d.destroy()
            return False

        self.profileDialog.hide()
        
        if self.currentProfile == self.profileStore.get_value(
                                  self.currentProfileIter, 0):
            return True

        self.currentProfile = self.profileStore.get_value(
                              self.currentProfileIter, 0)
        self.setupProfile()
        self.doRefresh()

        cur_page = self.xml.get_widget("mainBook").get_current_page()
        if cur_page == LIST_PAGE:
            self._showList()
        elif cur_page == SEARCH_PAGE:
            self._showSearch()
        elif len(self.repos.listGroupsEnabled()) == 0:
            self.xml.get_widget("mainBook").set_current_page(LIST_PAGE)
        else:
            # We are not switching pages to BROWSE_PAGE.
            # self.xml.get_widget("mainBook").set_current_page(BROWSE_PAGE)
            self._showBrowse()

        return True

    def _exportProfile(self, widget, event=None):
        """
        Export the profile of the current system as a tar ball.
        """

        export_dialog = gtk.FileChooserDialog(_("Export As..."),
                                              self.mainwin,
                                              gtk.FILE_CHOOSER_ACTION_SAVE,
                                              (gtk.STOCK_CANCEL,
                                               gtk.RESPONSE_CANCEL,
                                               gtk.STOCK_SAVE,
                                               gtk.RESPONSE_OK))
        filter = gtk.FileFilter()
        filter.set_name(_("All files"))
        filter.add_pattern("*")
        export_dialog.add_filter(filter)
        
        filter = gtk.FileFilter()
        filter.set_name(_("Supported files"))
        filter.add_pattern("*.tar")
        export_dialog.add_filter(filter)
        export_dialog.set_filter(filter)

        id = os.uname()[1]
        export_dialog.set_current_name(id)
        response = export_dialog.run()

        if response == gtk.RESPONSE_OK:
            file_name = export_dialog.get_filename()
            if file_name[-4:] != ".tar":
                file_name = file_name + ".tar"

        export_dialog.destroy()
        if response != gtk.RESPONSE_OK:
            return

        # FIXME: We need to figure out whether self.profileDialog is shown or
        #        or hidden.
        pbar = PirutCancellableProgress(_("Exporting profile. Please wait..."),
                                        self.mainwin)
        pbar.show()
        _runGtkMain()

        pbar.set_markup("<i>Preparing...</i>")
        pbar.set_fraction(0.050)
        _runGtkMain()
        if os.access(self.tmpDir, os.F_OK):
            shutil.rmtree(self.tmpDir)
        tmp_dir = self.tmpDir + "/" + id + "/var/lib"
        os.makedirs(tmp_dir)

        pbar.set_markup("<i>Reading system information...</i>")
        pbar.set_fraction(0.150)
        _runGtkMain()
        shutil.copytree("/var/lib/rpm", tmp_dir + "/rpm")
        
        pbar.set_markup("<i>Saving profile data...</i>")
        pbar.set_fraction(0.250)
        _runGtkMain()
        f = open(self.tmpDir + "/" + id + "/.profile", "w")
        f.write(id.capitalize())
        f.write("\n")
        f.close()
        
        pbar.set_markup("<i>Opening %s...</i>" % file_name)
        pbar.set_fraction(0.300)
        _runGtkMain()
        p = tarfile.TarFile(name=file_name, mode="w")

        pbar.set_markup("<i>Creating archive %s...</i>" % file_name)
        pbar.set_fraction(0.900)
        _runGtkMain()
        p.add(self.tmpDir + "/" + id, arcname=id)
        p.close()

        pbar.set_markup("<i>Cleaning up...</i>")
        pbar.set_fraction(0.950)
        _runGtkMain()
        shutil.rmtree(self.tmpDir)

        pbar.set_markup("<i>Finished.</i>")
        pbar.set_fraction(1.000)
        _runGtkMain()

        pbar.destroy()

    def _importProfile(self, widget, event=None):
        """
        Import the profile of any system provided as a tar ball.
        """

        import_dialog = gtk.FileChooserDialog(_("Import As..."),
                                              self.mainwin,
                                              gtk.FILE_CHOOSER_ACTION_OPEN,
                                              (gtk.STOCK_CANCEL,
                                               gtk.RESPONSE_CANCEL,
                                               gtk.STOCK_ADD,
                                               gtk.RESPONSE_OK))
        filter = gtk.FileFilter()
        filter.set_name(_("All files"))
        filter.add_pattern("*")
        import_dialog.add_filter(filter)
        
        filter = gtk.FileFilter()
        filter.set_name(_("Supported files"))
        filter.add_pattern("*.tar")
        import_dialog.add_filter(filter)
        import_dialog.set_filter(filter)

        response = import_dialog.run()

        if response == gtk.RESPONSE_OK:
            file_name = import_dialog.get_filename()

        import_dialog.destroy()
        if response != gtk.RESPONSE_OK:
            return
        
        # FIXME: We need to figure out whether self.profileDialog is shown or
        #        or hidden.
        pbar = PirutCancellableProgress(_("Importing profile. Please wait..."),
                                        self.mainwin)
        pbar.show()
        _runGtkMain()

        pbar.set_markup("<i>Opening archive %s...</i>" % os.path.basename(file_name))
        pbar.set_fraction(0.100)
        _runGtkMain()
        p = tarfile.TarFile(name=file_name, mode="r")

        pbar.set_markup("<i>Reading archive %s...</i>" % os.path.basename(file_name))
        pbar.set_fraction(0.200)
        _runGtkMain()
        members = p.getnames()
        num = len(members)

        for i in xrange(num):
            pbar.set_markup("<i>Extracting from %s: %s...</i>" % (os.path.basename(file_name), members[i]))
            pbar.set_fraction(0.200 + (i+1)/num*0.600)
            _runGtkMain()
            p.extract(members[i], path=self.rumDir)

        pbar.set_markup("<i>Closing archive %s...</i>" % os.path.basename(file_name))
        pbar.set_fraction(0.900)
        _runGtkMain()
        p.close()
            
        id = members[0].split("/")[0]
        profile_name = self.getProfileName(id)

        pbar.set_markup("<i>Setting up profile %s...</i>" % profile_name)
        pbar.set_fraction(0.950)
        _runGtkMain()
        profile = Profile(id, profile_name, self.rumDir + "/" + id)
        self.profiles.append(profile)

        desc = gobject.markup_escape_text(profile.name)
        desc = "<b>%s</b>\n<i>Other details: </i>" % desc
        self.profileStore.append([profile, False, desc])

        pbar.set_markup("<i>Finished.</i>")
        pbar.set_fraction(1.000)
        _runGtkMain()

        pbar.destroy()
        
    def _selectProfile(self, widget, path, column=None):
        """
        Callback function to select a profile.
        """
        
        i = self.profileStore.get_iter(path)
        cb = self.profileStore.get_value(i, 1)

        # Hack to make sure only one profile is selected.
        if cb:
            return True
        
        iter = self.profileStore.get_iter_first()
        while iter is not None:
            if iter == i:
                continue
            self.profileStore.set_value(iter, 1, False)
            iter = self.profileStore.iter_next(iter)
    
        self.profileStore.set_value(i, 1, not cb)
        self.currentProfileIter = i

    def _deleteProfile(self, widget, event=None):
        """
        Callback function to delete a profile.
        """
        
        if not self.currentProfileIter:
            d = gtk.MessageDialog(self.profileDialog, gtk.DIALOG_MODAL,
                                  gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
                                  _("No profile selected for deletion."))
            d.run()
            d.destroy()
            return

        profile = self.profileStore.get_value(self.currentProfileIter, 0)
        if profile == self.currentProfile:
            d = gtk.MessageDialog(self.profileDialog, gtk.DIALOG_MODAL,
                                  gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
                                  _("Not allowed to delete current profile, "
                                    "since this will cause undefined "
                                    "behaviour."))
            d.run()
            d.destroy()
            return
           
        profile.delete()
        self.profiles.remove(profile)
        self.profileStore.remove(self.currentProfileIter)

        iter = self.profileStore.get_iter_first()
        while iter is not None:
            profile = self.profileStore.get_value(iter, 0)
            if profile == self.currentProfile:
                self.profileStore.set_value(iter, 1, True)
                self.currentProfileIter = iter
                return
            iter = self.profileStore.iter_next(iter)

        
    def _repoConfig(self, *args):
        # FIXME: should we check for pending changes first?
        d = PirutRepoSelector(self)
        d.set_transient_for(self.mainwin)
        rc = d.run()
        d.destroy()
        # if things have changed, then we need to redo repo setup
        if rc:
            self.reset(True)
            self.xml.get_widget("mainBook").set_current_page(BROWSE_PAGE)

    def createYumPack(self, yumpack):
        """
        Export the profile of the current system as a tar ball.
        """

        pbar = PirutCancellableProgress(_("Creating YumPack. Please wait..."),
                                        self.mainwin)
        pbar.show()
        _runGtkMain()

        pbar.set_markup("<i>Preparing...</i>")
        pbar.set_fraction(0.050)
        _runGtkMain()
        if os.access(self.tmpDir, os.F_OK):
            shutil.rmtree(self.tmpDir)
        tmp_dir = self.tmpDir + "/yumpack"
        os.makedirs(tmp_dir)

        pbar.set_markup("<i>Reading YumPack information...</i>")
        pbar.set_fraction(0.100)
        _runGtkMain()
        members = self.tsInfo.getMembers()
        num = len(members)

        pbar.set_markup("<i>Creating archive %s...</i>" % os.path.basename(yumpack))
        pbar.set_fraction(0.200)
        _runGtkMain()
        p = tarfile.TarFile(name=yumpack, mode="w")
        p.add(name=tmp_dir, arcname="yumpack", recursive=False)
        
        for i in xrange(num):
            pbar.set_markup("<i> Inserting into %s: %s...</i>" % (os.path.basename(yumpack), members[i].po))
            pbar.set_fraction(0.200 + (i+1)/num*0.600)
            _runGtkMain()
            local_path = str(members[i].po.localPkg())
            p.add(name=local_path, arcname="yumpack/"+os.path.basename(local_path), recursive=False)

        pbar.set_markup("<i>Closing archive %s...</i>" % os.path.basename(yumpack))
        pbar.set_fraction(0.850)
        _runGtkMain()
        p.close()

        pbar.set_markup("<i>Cleaning up...</i>")
        pbar.set_fraction(0.950)
        _runGtkMain()
        shutil.rmtree(self.tmpDir)

        pbar.set_markup("<i>Finished.</i>")
        pbar.set_fraction(1.000)
        _runGtkMain()

        pbar.destroy()

    def _about(self, *args):
        d = self.xml.get_widget("aboutDialog")
        d.show_all()
        d.run()
        d.hide()

#     def _fileChooserLocationPopup(widget, path):
#         """
#         """

#         widget.select_filename(path)

    def quit(self, *args, **kwargs):
        try:
            if hasattr(self, "tsInfo") and len(self.tsInfo) > 0:
                d = gtk.MessageDialog(self.mainwin, gtk.DIALOG_MODAL,
                                      gtk.MESSAGE_QUESTION,
                                      message_format =
                                      _("Software selected and not installed.  "
                                        "Would you like to finish installing or "
                                        "quit anyway, losing all package "
                                        "selections?"))
                d.add_button(_("_Don't quit"), gtk.RESPONSE_CANCEL)
                d.add_button(_("_Quit anyway"), gtk.RESPONSE_OK)
                rc = d.run()
                d.destroy()
                if rc == gtk.RESPONSE_OK:
                    GraphicalYumBase.quit(self)
                else:
                    return
        except:
            pass
        GraphicalYumBase.quit(self, args, kwargs)

def repoOptCb(opt_obj, opt, value, parser):
    """
    Create a list of tuples representing the repositories that are to be
    enabled or disabled, as specified in the command line options. 
    """

    dest = eval('parser.values.%s' % opt_obj.dest)
    dest.append((opt, value))

def main():
    parser = optparse.OptionParser(usage="opyum [options] <create, export, "
                                         "import, use>")
    parser.add_option("-c", "", type="string",
                      dest="conf", default="/etc/yum/yum.conf",
                      help="config file location",
                      metavar="[config file]")
    parser.add_option("-d", "", type="int",
                      dest="debug", default=None,
                      help="debugging output level",
                      metavar="[debug level]")
    parser.add_option("-e", "", type="int",
                      dest="error", default=None,
                      help="error output level",
                      metavar="[error level]")
    parser.add_option("-f", "", type="string",
                      dest="file", default=os.uname()[1]+".tar",
                      help="specify the name of the input or output file "
                           "based on the context",
                      metavar="[file]")
    parser.add_option("-p", "", type="string",
                      dest="profile", default="sunflower",
                      help="specify the name of the current working profile",
                      metavar="[profile]")
    parser.add_option("", "--enablerepo", action="callback", type="string",
                      callback=repoOptCb, dest="repos", default=[],
                      help="enable one or more repositories (wildcards "
                           "allowed)",
                      metavar="[repo]")
    parser.add_option("", "--disablerepo", action="callback", type="string",
                      callback=repoOptCb, dest="repos", default=[],
                      help="disable one or more repositories (wildcards "
                           "allowed)",
                      metavar="[repo]")
    parser.add_option("", "--opyumdir", type="string",
                      dest="opyum", default=os.getenv("HOME")+"/.opyum",
                      help="set the Opyum directory",
                      metavar="[path]")

    (options,args) = parser.parse_args()

    gtk.glade.bindtextdomain(I18N_DOMAIN, "/usr/share/locale")

    try:
        pm = PackageManager(options.opyum, options.conf)
    except PirutError, e:
        startupError(e)

    pm.run()

if __name__ == "__main__":
    installExceptionHandler("pirut", "")
    main()
