#!/usr/bin/perl

#- Copyright (C) 2005 MandrakeSoft SA
#- Copyright (C) 2005 Mandriva SA

use strict;
use warnings;

BEGIN { #- set up a safe path and environment
    $ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin";
    delete @ENV{qw(ENV BASH_ENV IFS CDPATH)};
}

use gurpmi;
use Gtk2;

#- GUI globals
my ($mainw, $mainbox);

#- Replaces the contents of the main window with the specified box
#- (avoids popup multiplication)
sub change_mainw {
    $mainw->remove($mainbox);
    ($mainbox) = @_;
    $mainw->add($mainbox);
    $mainw->show_all;
}

sub sync () {
    $mainw->show;
    Gtk2->main_iteration while Gtk2->events_pending;
}

#- sets the window to a please-wait message
sub wait_label {
    my $wait_vbox = Gtk2::VBox->new(0, 5);
    my $wait_label = Gtk2::Label->new($_[0] || N("Please wait..."));
    $wait_label->set_alignment(0.5, 0.5);
    $wait_vbox->pack_start($wait_label, 1, 1, 0);
    change_mainw($wait_vbox);
    sync();
}

my @all_rpms = gurpmi::parse_command_line();

$> and fatal(N("Must be root"));

#- Now, the graphical stuff.

Gtk2->init;

#- Create main window

$mainw = Gtk2::Window->new('toplevel');
$mainw->set_border_width(3);
$mainw->set_title(N("RPM installation"));
$mainw->signal_connect(destroy => \&quit);
$mainw->set_position('center');
$mainw->set_modal(0);
$mainbox = Gtk2::VBox->new(0, 5);
$mainw->add($mainbox);

#- Performs installation

my $urpm = configure_urpm();
my $state = {};
my %requested = $urpm->register_rpms(@all_rpms);
if (@gurpmi::names) {
    $urpm->search_packages(\%requested, [ @gurpmi::names ]);
}
$urpm->resolve_dependencies(
    $state,
    \%requested,
    callback_choices => \&ask_choice,
);
my @ask_unselect = $urpm->unselected_packages($state);
@ask_unselect
? ask_continue(N(
	"Some requested packages cannot be installed:\n%s\nContinue installation anyway?",
	join "\n", $urpm->translate_why_unselected($state, sort @ask_unselect)
    ), \&do_install)
: do_install();


$mainw->show_all;
Gtk2->main;

#- Creates and configure an urpm object for this application to use.
sub configure_urpm {
    my $urpm = new urpm;
    $urpm->{fatal} = sub {
	Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'error', 'ok', Locale::gettext::iconv($_[1], undef, 'UTF-8'))->run;
	quit;
	exit $_[0];
    };
    $urpm->{error} = sub {
	my $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'warning', 'ok', Locale::gettext::iconv($_[0], undef, 'UTF-8'));
	$w->run;
	$w->destroy;
    };
    $urpm->exlock_rpm_db;
    $urpm->shlock_urpmi_db;
    $urpm->configure;
    $urpm->{options}{'verify-rpm'} = 0 if $gurpmi::options{'no-verify-rpm'};
    #- default options values
    exists $urpm->{options}{$_} or $urpm->{options}{$_} = 1 foreach qw(post-clean verify-rpm split-length);
    $urpm->{options}{'split-level'} = 20 unless exists $urpm->{options}{'split-level'};
    $urpm;
}

#- Callback for choices
sub ask_choice {
    my (undef, undef, undef, $choices) = @_;
    my $radio;
    my @radios = map {
	$radio = Gtk2::RadioButton->new_with_label(
	    $radio ? $radio->get_group : undef,
	    (scalar $_->fullname) . " : " . $_->summary
	    . ($_->flag_installed ? N(" (to upgrade)") : '')
	    . ($_->flag_upgrade   ? N(" (to install)") : '')
	);
    } @$choices;
    my $d = Gtk2::Dialog->new(N("Package choice"), $mainw, [], N("_Cancel") => 0, N("_Ok") => 1);
    my $label = Gtk2::Label->new(N("One of the following packages is needed:"));
    $label->set_alignment(0.5, 0.5);
    $d->vbox->pack_start($label, 1, 1, 0);
    $d->vbox->pack_start($_, 1, 1, 0) foreach @radios;
    my $n = 0;
    $d->signal_connect(response => sub {
	if ($_[1] == 1) { #- "ok"
	    foreach (@radios) { last if $_->get_active; ++$n }
	}
	$d->destroy;
	exit 0 if $_[1] == 0; #- "cancel"
    });
    $radios[0]->set_active(1);
    $d->show_all;
    $d->run;
    $choices->[$n];
}

sub ask_continue {
    my ($msg, $nextclosure) = @_;
    my $vbox = Gtk2::VBox->new(0, 5);
    $vbox->pack_start(new_label($msg), 1, 1, 0);
    my $continue_button = Gtk2::Button->new(but(N("_Ok")));
    my $quit_button = Gtk2::Button->new(but(N("_Abort")));
    $quit_button->signal_connect(clicked => \&quit);
    $continue_button->signal_connect(clicked => sub { goto &$nextclosure });
    add_button_box($vbox, $quit_button, $continue_button);
    change_mainw($vbox);
}

sub ask_continue_blocking {
    my ($msg) = @_;
    my $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'question', 'yes-no', $msg);
    my $answer = $w->run;
    $w->destroy;
    quit if $answer eq 'no';
}

sub do_install {
    wait_label;
    my @ask_remove = $urpm->removed_packages($state);
    @ask_remove
	? ask_continue(N(
	    "The following packages have to be removed for others to be upgraded:\n%s\nContinue installation anyway?",
	    join "\n", $urpm->translate_why_removed($state, sort @ask_remove)
	), \&do_install_2)
	: goto &do_install_2;
}

sub do_install_2 () {
    my @to_install;
    foreach my $pkg (sort { $a->name cmp $b->name } @{$urpm->{depslist}}[keys %{$state->{selected}}]) {
	$pkg->arch ne 'src' and push @to_install, scalar $pkg->fullname;
    }
    $urpm->{nb_install} = @to_install;
    @to_install > 1
	? ask_continue(N(
	    "To satisfy dependencies, the following %d packages are going to be installed:\n%s\n",
	    scalar(@to_install), join "\n", @to_install
	), \&do_install_3)
	: goto \&do_install_3;
}

sub do_install_3 () {
    wait_label(N("Package installation..."));
    my ($local_sources, $list) = $urpm->get_source_packages($state->{selected});
    $local_sources || $list or $urpm->{fatal}(3, N("unable to get source packages, aborting"));
    my %sources = %$local_sources;
    my %error_sources;
    my $vbox = Gtk2::VBox->new(0, 5);
    my $progress_label = Gtk2::Label->new('-');
    $vbox->pack_start($progress_label, 1, 1, 0);
    my $progressbar = Gtk2::ProgressBar->new;
    $progressbar->set_size_request(300, -1);
    $vbox->pack_start($progressbar, 0, 0, 0);
    change_mainw($vbox);
    $urpm->copy_packages_of_removable_media($list, \%sources,
	force_local => 1,
	ask_for_medium => sub {
	    my $w = Gtk2::MessageDialog->new($mainw, [qw(modal destroy-with-parent)], 'warning', 'ok-cancel',
		N("Please insert the medium named \"%s\" on device [%s]", $_[0], $_[1])
	    );
	    my $response = $w->run;
	    $w->destroy;
	    exit 0 if $response eq 'cancel';
	    1;
	}
    );
    $urpm->create_transaction(
	$state,
	split_level => $urpm->{options}{'split-level'},
	split_length => $urpm->{options}{'split-length'},
    );
    my ($nok, @errors);
    foreach my $set (@{$state->{transaction} || []}) {
	my (@transaction_list, %transaction_sources);
	$urpm->prepare_transaction($set, $list, \%sources, \@transaction_list, \%transaction_sources);
	$urpm->download_packages_of_distant_media(
	    \@transaction_list,
	    \%transaction_sources,
	    \%error_sources,
	    limit_rate => $urpm->{options}{'limit-rate'},
	    compress => $urpm->{options}{compress},
	    resume => $urpm->{options}{resume},
	    force_local => 1,
	    callback => sub {
		my ($mode, $file, $percent) = @_;
		if ($mode eq 'start') {
		    $file =~ s|/*\s*$||; $file =~ s|.*/||;
		    $progress_label->set_label(N("Downloading package `%s'...", $file));
		    select(undef, undef, undef, 0.1);  #- hackish
		} elsif ($mode eq 'progress') {
		    $progressbar->set_fraction($percent / 100);
		} elsif ($mode eq 'end') {
		    $progressbar->set_fraction(1);
		}
		sync();
	    },
	);
	my %transaction_sources_install = %{$urpm->extract_packages_to_install(\%transaction_sources, $state) || {}};
	if (!$urpm->{options}{'verify-rpm'} || grep { $_->{'verify-rpm'} } @{$urpm->{media}}) {
	    my @bad_signatures = $urpm->check_sources_signatures(\%transaction_sources_install, \%transaction_sources, translate => 1);
	    if (@bad_signatures) {
		ask_continue_blocking(N(
		    "The following packages have bad signatures:\n%s\n\nDo you want to continue installation ?",
		    (join "\n", @bad_signatures)
		));
	    }
	}
	#- check for local files.
	if (my @missing = grep { m|^/| && ! -e $_ } values %transaction_sources_install, values %transaction_sources) {
	    $urpm->{error}(N("Installation failed, some files are missing:\n%s\nYou may want to update your urpmi database",
		    join "\n", map { s|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|$1xxxx$2|; "    $_" } @missing));
	    ++$nok;
	    next;
	}
	if (keys(%transaction_sources_install) || keys(%transaction_sources)) {
	    @{$set->{remove} || []} and
		$progress_label->set_label(N("removing %s", join(' ', @{$set->{remove} || []})));
	    my $progress_nb;
	    my $total_nb = scalar grep { m|^/| } values %transaction_sources_install, values %transaction_sources;
	    my $callback_inst = sub {
		my ($urpm, $type, $id, $subtype, $amount, $total) = @_;
		my $pkg = defined $id && $urpm->{depslist}[$id];
		if ($subtype eq 'start') {
		    if ($type eq 'trans') {
			$progress_label->set_label(N("Preparing..."));
		    } else {
			$progress_label->set_label(N("Installing package `%s' (%s/%s)...", $pkg->name, ++$progress_nb, $urpm->{nb_install}));
		    }
		} elsif ($subtype eq 'progress') {
		    $progressbar->set_fraction($amount / $total);
		}
		sync();
	    };
	    my @l = $urpm->install(
		$set->{remove} || [],
		\%transaction_sources_install,
		\%transaction_sources,
		'fork' => @{$state->{transaction} || []} > 1, #- fork if multiple transaction
		translate_message => 1,
		oldpackage => $state->{oldpackage},
		callback_inst => $callback_inst,
		callback_trans => $callback_inst,
	    );
	    if (@l) {
		$progress_label->set_label(N("Installation failed") . ":\n" . join("\n",  map { "\t$_" } @l));
		++$nok;
		push @errors, @l;
	    }
	}
    }
    $vbox = Gtk2::VBox->new(0, 5);
    $progress_label = Gtk2::Label->new('-');
    $vbox->pack_start($progress_label, 1, 1, 0);
    my $quit_button = Gtk2::Button->new(but(N("_Done")));
    $quit_button->signal_connect(clicked => \&quit);
    add_button_box($vbox, $quit_button);
    change_mainw($vbox);
    if (values %error_sources) {
	$progress_label->set_label(N("Installation failed, some files are missing:\n%s\nYou may want to update your urpmi database",
		join "\n", map { s|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|$1xxxx$2|; "    $_" } values %error_sources));
    } elsif (@{$state->{transaction} || []} == 0 && @ask_unselect == 0) {
	$progress_label->set_label(N("The package(s) are already installed"));
    } else {
	$progress_label->set_label(N("Installation finished"));
    }
    $urpm->unlock_urpmi_db;
    $urpm->unlock_rpm_db;
    $urpm->try_umounting_removables;
}