#!/usr/bin/perl

use lib qw(/usr/lib/libDrakX);
use standalone;
use interactive;
#- err, yes, we're loaded, aren't we? (actually not used by this wizard in install_interactive)
BEGIN { undef @INC{'install_any.pm', 'install_steps.pm'} }
use install_interactive;
use fs::type;
use common;

push @::textdomains, 'draklive-install';

{
    use interactive;
    package interactive;
    *ask_mntpoint_s = \&main::ask_mntpoint_s_interactive;
}

{
    use diskdrake::interactive;
    package diskdrake::interactive;
    my $old = \&hd_possible_actions;
    undef *hd_possible_actions;
    *hd_possible_actions = sub {
	#- for the partition wizard to show the auto-allocate option
	local $::isInstall = 1;
	&$old;
    };
    undef *Done;
    #- skip the fstab/reboot checks
    *Done = \&diskdrake_interactive_Done;
    #- don't ask whether to Move/Hide old files
    undef *need_migration;
    *need_migration = sub { 'hide' };
}

sub umount_all {
    my ($in) = @_;
    #- make sure nothing is mounted in the new root
    foreach (sort { $b cmp $a } grep { /^$in->{prefix}/ } map { (split)[1] } cat_('/proc/mounts')) {
        system('umount', $_);
    }
    #- make sure selected devices aren't mounted, and swap isn't used
    foreach (grep { isSwap($_) } @{$in->{fstab}}) {
        eval { fs::mount::swapoff($_->{device}) };
    }
    foreach (map { $_->{mntpoint} && !isSwap($_) ? "/dev/$_->{device}" : () } @{$in->{fstab}}) {
        system('umount', $_);
    }
}

my $in = 'interactive'->vnew('su');
$in->{prefix} = $::prefix = '/mnt/install';

getHds($in);

umount_all($in);

require any;
my $has_running_wm = to_bool(any::running_window_manager());


$::isWizard = 1;
$::Wizard_no_previous = 1;
$::Wizard_pix_up = "MandrivaOne-install-icon";
my $live_install_img = "MandrivaOne-install";
my $title = N("Mandriva Live");
{
    local $::isStandalone = $has_running_wm; # center me if run in xsetup.d script
    my $w = ugtk2->new($title);
    ugtk2::gtkadd($w->{window},
                  ugtk2::gtkcreate_img($live_install_img),
                  ugtk2::gtknew('Label', height => 5),
                  N("This wizard will help you to install the live distribution."),
                  ugtk2::create_okcancel($w));
    $w->{ok}->grab_focus;
    $w->main;
}

install_interactive::partitionWizard_ask($in);
mkdir_p($::prefix) or die "unable to create $::prefix";

doPartitionDisksAfter($in);
choosePartitionsToFormat_interactive($in, $in->{fstab});

umount_all($in);
formatMountPartitions($in);

#- copy to disk
my $_w = do {
    local $::isInstall = 1; # quick hack to embed the wait message
    $in->wait_message('', N("Computing total size"));
};
my $total = first(split(/\s+/, `du -sbx / 2>/dev/null`));

#- fork interactive::wait_message_with_progress_bar to allow embedding and images
my $progress_displayed;
my $progress = Gtk2::ProgressBar->new;
$progress->hide;
$progress->signal_connect(expose_event => sub { $progress_displayed = 1; 0 });
undef $_w;
$_w = do {
    local $::isInstall = 1; # quick hack to embed the wait message
    my $w = $in->wait_messageW('', [ ugtk2::gtkcreate_img($live_install_img), N("Copying in progress"), if_($progress, $progress) ]);
    before_leaving { $in->wait_message_endW($w) };
}; #- should be my ($w, $update_progress) = $in->wait_message_with_progress_bar()


sub build_copy_command {
    my ($source, $dest) = @_;
    join(' ',
         'tar', 'c',
         (map { ('--exclude', ".$_/*") } '/mnt/*', '/live', '/proc', '/dev', '/sys'),
         '-C', $source, '.',
         '|',
         'tar', 'xvv', '-C', $dest,
     );
}

open(my $OUTPUT, '-|', build_copy_command('/', $in->{prefix}));
{
    local $_;
    my $current = my $previous = 0;
    while (<$OUTPUT>) {
	(undef, undef, my $size) = split;
	$current += $size;
	if ($current <= $total && $current/$total > $previous/$total + 0.001) {
            $progress->set_fraction($current / $total);
            $progress->show;
            $progress_displayed = 0;
            mygtk2::flush() while !$progress_displayed; #- these 4 lines should $update_progress->('', $current, $total)
	    $previous = $current;
	}
    }
}
undef $_w;

#- FIXME: maybe factorize with draklive, using draklive --clean-chroot ?
#- remove unwanted files and packages
unlink(map { $in->{prefix} . $_ } '/.autofsck',
       '/home/guest/Desktop/draklive-copy-wizard.desktop',
       '/home/guest/Desktop/draklive-install.desktop');
system('chroot', $in->{prefix}, 'rpm', '-e', 'draklive-install');

#- enable first boot wizard at system boot
output($in->{prefix} . '/etc/sysconfig/firstboot', 'FIRSTBOOT=yes');
#- enable drakx-finish-install at system boot
output($in->{prefix} . '/etc/sysconfig/finish-install', qq(
FINISH_INSTALL=yes
LICENSE=no
LANGUAGE=no
KEYBOARD=no
TIMEZONE=no
NETWORK=yes
AUTHENTICATION=yes
USERS=yes
USER_RENAME_FROM=guest
GLX=no
));
#- unselect guest user in kdm
my $kdm_cfg = '/etc/kde/kdm/kdmrc';
update_gnomekderc($in->{prefix} . $kdm_cfg,
                  'X-:0-Greeter' => (PreselectUser => 'None', DefaultUser => '')) if -f $kdm_cfg;
#- allow to install doc in disk install
substInFile { undef $_ if /^\%_excludedocs/ } $in->{prefix} . '/etc/rpm/macros';

#- create required directories and devices
mkdir_p($in->{prefix} . $_) foreach qw(/dev /etc /proc /sys);
run_program::run('makedev', $in->{prefix} . '/dev');
#- write fstab
fs::write_fstab($in->{all_hds}, $in->{prefix});

# enable back some disabled services
require services;
services::start_service_on_boot($_) foreach qw(dkms crond);

#- setup bootloader
#- TODO: factorize with drakboot
use bootloader;
my $bootloader = {};
my $cmdline = cat_('/proc/cmdline');
bootloader::suggest($bootloader, $in->{all_hds},
		    vga_fb => first($cmdline =~ /\bvga=(\S+)/), #- don't use $1 here, otherwise perl will hit you because of the other "=~" below
		    quiet => $cmdline =~ /\bsplash=silent\b/,
		    );
{
    local $::Wizard_no_previous = 0;
    lilo_choice();
}

#- cleanly umount here, it will avoid fs journals to be corrupted after a hackish reboot
umount_all($in);

$::Wizard_finished = 1;
$in->ask_okcancel(N("Congratulations"), N("Please halt your computer, remove your live system, and restart your computer."));

$in->exit(0);


###
### duplicate code
###

#- install::any::getHds
sub getHds {
    my ($o, $o_in) = @_;

  getHds: 
    my $all_hds = fsedit::get_hds($o->{partitioning}, $o_in);
    my $hds = $all_hds->{hds};

    if (is_empty_array_ref($hds) && !$::move) { #- no way
	die N("An error occurred - no valid devices were found on which to create new filesystems. Please check your hardware for the cause of this problem");
    }

    #- try to figure out if the same number of hds is available, use them if ok.
    @{$o->{all_hds}{hds} || []} == @$hds and return 1;

    fs::get_raw_hds('', $all_hds);
    fs::add2all_hds($all_hds, @{$o->{manualFstab}});

    $o->{all_hds} = $all_hds;
    $o->{fstab} = [ fs::get::really_all_fstab($all_hds) ];
    fs::merge_info_from_mtab($o->{fstab}) if !$::local_install;
    # draklive-install
    #suggest_mount_points_always($o->{fstab}) if !$::local_install;

    1;
}

#- install_steps::doPartitionDisksAfter
sub doPartitionDisksAfter {
    my ($o) = @_;
    my $hds = $o->{all_hds}{hds};
    partition_table::write($_) foreach @$hds;
    if (any { $_->{rebootNeeded} } @$hds) {
	#- install_steps_interactive::rebootNeeded
	$o->ask_warn('', N("You need to reboot for the partition table modifications to take place"));
	$o->exit(0);
    }
    fs::set_removable_mntpoints($o->{all_hds});
    fs::mount_options::set_all_default($o->{all_hds}, %$o, lang::fs_options($o->{locale}));
    $o->{fstab} = [ fs::get::fstab($o->{all_hds}) ];
}

#- install_steps::choosePartitionsToFormat
sub choosePartitionsToFormat($$) {
    my ($_o, $fstab) = @_;

    return if $::local_install;

    foreach (@$fstab) {
        $_->{mntpoint} = "swap" if isSwap($_);
        $_->{mntpoint} or next;

        add2hash_($_, { toFormat => $_->{notFormatted} }) if $_->{fs_type}; #- eg: do not set toFormat for isRawRAID (0xfd)
        $_->{toFormatUnsure} ||= member($_->{mntpoint}, '/', '/usr');

        if (!$_->{toFormat}) {
            my $fs_type = fs::type::fs_type_from_magic($_);
            if (!$fs_type || $fs_type ne $_->{fs_type}) {
                log::l("setting toFormatUnsure for $_->{device} because <$_->{fs_type}> ne <$fs_type>");
                $_->{toFormatUnsure} = 1;
            }
        }
    }
}

#- install_steps_interactive::choosePartitionsToFormat
sub choosePartitionsToFormat_interactive {
    my ($o, $fstab) = @_;

    choosePartitionsToFormat($o, $fstab);

    my @l = grep { !$_->{isMounted} && $_->{mntpoint} &&
                   (!isSwap($_) || $::expert) &&
                   (!isFat_or_NTFS($_) || $_->{notFormatted} || $::expert) &&
                   (!isOtherAvailableFS($_) || $::expert || $_->{toFormat});
               } @$fstab;
    $_->{toFormat} = 1 foreach grep { isSwap($_) && !$::expert } @$fstab;

    return if @l == 0 || !$::expert && every { $_->{toFormat} } @l;

    #- keep it temporary until the guy has accepted
    $_->{toFormatTmp} = $_->{toFormat} || $_->{toFormatUnsure} foreach @l;

    $o->ask_from_(
        { messages => N("Choose the partitions you want to format"),
          interactive_help_id => 'formatPartitions',
          advanced_messages => N("Check bad blocks?"),
        },
        [ map {
            my $e = $_;
            ({
              text => partition_table::description($e), type => 'bool',
              val => \$e->{toFormatTmp}
             }, if_(!isLoopback($_) && !member($_->{fs_type}, 'reiserfs', 'xfs', 'jfs'), {
              text => partition_table::description($e), type => 'bool', advanced => 1,
              disabled => sub { !$e->{toFormatTmp} },
              val => \$e->{toFormatCheck}
        })) } @l ]
    ) or die 'already displayed';
    #- ok now we can really set toFormat
    foreach (@l) {
        $_->{toFormat} = delete $_->{toFormatTmp};
        set_isFormatted($_, 0);
    }
}

#- install_steps_interactive::formatMountPartitions
#- FIXME(?): drop $_fstab
sub formatMountPartitions {
    my ($o, $_fstab) = @_;
    my ($w, $wait_message) = $o->wait_message_with_progress_bar;
    catch_cdie {
        fs::format::formatMount_all($o->{all_hds}, $o->{fstab}, $wait_message);
    } sub {
        $@ =~ /fsck failed on (\S+)/ or return;
        $o->ask_yesorno('', N("Failed to check filesystem %s. Do you want to repair the errors? (beware, you can lose data)", $1), 1);
    };
    undef $w; #- help perl (otherwise wait_message stays forever in newt)
    die N("Not enough swap space to fulfill installation, please add some") if availableMemory() < 40 * 1024;
}

#- install_any::guess_mount_point
sub guess_mount_point {
    my ($part, $prefix, $user) = @_;

    my %l = (
             '/'     => 'etc/fstab',
             '/boot' => 'vmlinuz',
             '/tmp'  => '.X11-unix',
             '/usr'  => 'X11R6',
             '/var'  => 'catman',
            );

    require any;
    my $handle = any::inspect($part, $prefix) or return;
    my $d = $handle->{dir};
    my $mnt = find { -e "$d/$l{$_}" } keys %l;
    $mnt ||= (stat("$d/.bashrc"))[4] ? '/root' : '/home/user' . ++$$user if -e "$d/.bashrc";
    $mnt ||= (any { -d $_ && (stat($_))[4] >= 500 && -e "$_/.bashrc" } glob_($d)) ? '/home' : '';
    ($mnt, $handle);
}

#- install_any::suggest_mount_points
sub suggest_mount_points {
    my ($fstab, $prefix, $uniq) = @_;

    my $user;
    foreach my $part (grep { isTrueFS($_) } @$fstab) {
        $part->{mntpoint} && !$part->{unsafeMntpoint} and next; #- if already found via an fstab

        my ($mnt, $handle) = guess_mount_point($part, $prefix, \$user) or next;

        next if $uniq && fs::get::mntpoint2part($mnt, $fstab);
        $part->{mntpoint} = $mnt; delete $part->{unsafeMntpoint};

        #- try to find other mount points via fstab
        fs::merge_info_from_fstab($fstab, $handle->{dir}, $uniq, 'loose') if $mnt eq '/';
    }
    $_->{mntpoint} and log::l("suggest_mount_points: $_->{device} -> $_->{mntpoint}") foreach @$fstab;
}

#- install_steps_interactive::ask_mntpoint_s
sub ask_mntpoint_s_interactive { #- }{}
    my ($o, $fstab) = @_;

    my @fstab = grep { isTrueFS($_) } @$fstab;
    @fstab = grep { isSwap($_) } @$fstab if @fstab == 0;
    @fstab = @$fstab if @fstab == 0;
    die N("No partition available") if @fstab == 0;

    {
        my $_w = $o->wait_message('', N("Scanning partitions to find mount points"));
        suggest_mount_points($fstab, $o->{prefix}, 'uniq');
        log::l("default mntpoint $_->{mntpoint} $_->{device}") foreach @fstab;
    }
    if (@fstab == 1) {
        $fstab[0]{mntpoint} = '/';
    } else {
        $o->ask_from_({ messages => N("Choose the mount points"),
                        title => N("Partitioning"),
                        icon => 'banner-part',
                        interactive_help_id => 'ask_mntpoint_s',
                        callbacks => {
                            complete => sub {
                                require diskdrake::interactive;
                                eval { 1, find_index {
                                    !diskdrake::interactive::check_mntpoint($o, $_->{mntpoint}, $_, $o->{all_hds});
                                } @fstab };
                            },
                        },
                      },
                      [ map {
                          {
                              label => partition_table::description($_),
                              val => \$_->{mntpoint},
                              not_edit => 0,
                              list => [ '', fsedit::suggestions_mntpoint(fs::get::empty_all_hds()) ],
                          };
                        } @fstab ]) or return;
    }
    ask_mntpoint_s($o, $fstab);
}

#- install_steps::ask_mntpoint_s
sub ask_mntpoint_s {#-}}}
    my ($_o, $fstab) = @_;

    #- TODO: set the mntpoints

    my %m; foreach (@$fstab) {
        my $m = $_->{mntpoint};

        $m && $m =~ m!^/! or next; #- there may be a lot of swaps or "none"

        $m{$m} and die N("Duplicate mount point %s", $m);
        $m{$m} = 1;

        #- in case the type does not correspond, force it to ext3
        fs::type::set_fs_type($_, 'ext3') if !isTrueFS($_) && !isOtherAvailableFS($_);
    }
    1;
}

#- adapted from drakboot
sub lilo_choice() {
    do {
        my $before = fs::fstab_to_string($in->{all_hds});
        any::setupBootloader($in, $bootloader, $in->{all_hds}, $in->{fstab}, $ENV{SECURE_LEVEL}) or $in->exit;
	fs::write_fstab($in->{all_hds});
    } while !any::installBootloader($in, $bootloader, $in->{all_hds});
}

#- from disdrake::interactive
{
    package diskdrake::interactive;
  sub diskdrake_interactive_Done {
    my ($in, $all_hds) = @_;
    eval { raid::verify($all_hds->{raids}) };
    if (my $err = $@) {
	$::expert or die;
	$in->ask_okcancel('', [ formatError($err), N("Continue anyway?") ]) or return;
    }
    foreach (@{$all_hds->{hds}}) {
	if (!write_partitions($in, $_, 'skip_check_rebootNeeded')) {
	    return if !$::isStandalone;
	    $in->ask_yesorno(N("Quit without saving"), N("Quit without writing the partition table?"), 1) or return;
	}
    }
    #- skip that fstab/reboot steps
    if (!$::isInstall && 0) { 
	my $new = fs::fstab_to_string($all_hds);
	if ($new ne $all_hds->{current_fstab} && $in->ask_yesorno('', N("Do you want to save /etc/fstab modifications"), 1)) {
	    $all_hds->{current_fstab} = $new;
	    fs::write_fstab($all_hds);
	}
	update_bootloader_for_renumbered_partitions($in, $all_hds);

	if (any { $_->{rebootNeeded} } @{$all_hds->{hds}}) {
	    $in->ask_warn('', N("You need to reboot for the partition table modifications to take place"));
	    tell_wm_and_reboot();
	}
    }
    if (my $part = find { $_->{mntpoint} && !maybeFormatted($_) } fs::get::fstab($all_hds)) {
	$in->ask_okcancel('', N("You should format partition %s.
Otherwise no entry for mount point %s will be written in fstab.
Quit anyway?", $part->{device}, $part->{mntpoint})) or return if $::isStandalone && 0; #- no, please
    }
    1;
  }
}
