#!/usr/bin/perl

# DrakFloppy
# $Id$
# 
# Copyright (C) 2001-2003 MandrakeSoft
# Yves Duret
# Thierry Vignaud
#
# 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, 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.


use strict;
use diagnostics;
use lib qw(/usr/lib/libDrakX);

use standalone; #- warning, standalone must be loaded very first, for 'explanations'
use common;
use ugtk2 qw(:helpers :create :wrappers);
use detect_devices;

#- languages that can't be displayed with gtk1, so we unset translations
#- for them until this tool is ported to gtk2
$ENV{LANGUAGE} = "C" if $ENV{LANGUAGE} =~ /\b(ar|he|hi|ta)/;

require_root_capability();

my $expert_mode = $::expert;

my $list_model = Gtk2::ListStore->new((Gtk2::GType->STRING) x 2);
my $list  = Gtk2::TreeView->new_with_model($list_model);
each_index {
    $list->append_column(my $col = Gtk2::TreeViewColumn->new_with_attributes($_, Gtk2::CellRendererText->new, 'text' => $::i));
    $col->set_min_width((200, 50)[$::i]);
    #    $col->set_alignment(1.0) if $::i == 1;
} (N("Module name"), N("Size"));

my $window = ugtk2->new('drakfloppy');
unless ($::isEmbedded) {
    $window->{rwindow}->signal_connect(delete_event => sub { ugtk2->exit(0) });
    $window->{rwindow}->set_title(N("drakfloppy"));
    $window->{rwindow}->set_border_width(5);
    
    ### menus definition
    # the menus are not shown but they provides shiny shortcut like C-q
    create_factory_menu($window->{rwindow}, ( 
                                             {
                                              path => N("/_File"), type => '<Branch>' },
                                             {
                                              path => N("/File/_Quit"), accelerator => N("<control>Q"), callback => sub { ugtk2->exit(0) } },
                                            )
                       );
}


######## up part

# device part
my $device_combo = new Gtk2::Combo();
$device_combo->entry->set_editable(0);
$device_combo->set_popdown_strings(map { "/dev/" . $_->{device} } detect_devices::floppies());


# kernel part
my $kernel_combo = new Gtk2::Combo();
$kernel_combo->disable_activate();
$kernel_combo->set_popdown_strings(sort grep { !/^\.\.?$/ } all("/lib/modules"));
$kernel_combo->entry->set_text(chomp_(`uname -r`));
$kernel_combo->entry->signal_connect(changed => sub { 
                                         change_tree($kernel_combo->entry->get_text());
                                         $list_model->clear();
                                     });


# Create root tree
my $tree_model = Gtk2::TreeStore->new((Gtk2::GType->STRING) x 2);
my $tree = Gtk2::TreeView->new_with_model($tree_model);
#$tree->get_selection->set_mode('browse');
$tree->set_headers_visible(0);
$tree->append_column(my $textcolumn  = Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0));
#$tree->append_column(my $dummy_textcolumn  = Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0));
$tree->signal_connect('row-expanded',  \&expand_tree);
$tree->get_selection()->signal_connect('changed' => \&selected_tree);


fill_tree($kernel_combo->entry->get_text());


### 
my ($output, @modules);


# Create list box
########################################################## from here my $list


### main window
$window->{window}->add(
                       gtkpack_(Gtk2::VBox->new,
                                if_($::isEmbedded, 0, new Gtk2::Label(N("boot disk creation"))),
                                0, gtkadd(Gtk2::Frame->new(N("General")),
                                          gtkpack__(new Gtk2::VBox(0, 0),
                                                    gtkpack__(new Gtk2::HBox(1, 0),
                                                              Gtk2::Label->new(N("device")),
                                                              $device_combo,
                                                              gtksignal_connect(Gtk2::Button->new(N("default")),
                                                                                clicked => sub { $device_combo->entry->set_text("/dev/fd0") }),
                                                             ),
                                                    gtkpack__(new Gtk2::HBox(1, 0),
                                                              Gtk2::Label->new(N("kernel version")),
                                                              $kernel_combo,
                                                              gtksignal_connect(Gtk2::Button->new("default"),
                                                                                clicked => sub { 
                                                                                    $kernel_combo->entry->set_text(chomp_(`uname -r`));
                                                                                    $list_model->clear();
                                                                                }),
                                                             ),
                                                   ),
                                         ),
                                1, gtkadd(my $expert_mod_frame = new Gtk2::Frame(N("Expert Area")),
                                          gtkpack_(gtkset_border_width(Gtk2::VBox->new(0, 5), 5),
                                                   0, gtkadd(Gtk2::Frame->new(N("mkinitrd optional arguments")),
                                                             gtkpack__(Gtk2::HBox->new(0, 5),
                                                                       my $force_button = new Gtk2::ToggleButton(N("force")),
                                                                       my $raid_button = new Gtk2::ToggleButton(N("omit raid modules")),
                                                                       my $needed_button = new Gtk2::ToggleButton(N("if needed")),
                                                                       my $scsi_button = new Gtk2::ToggleButton(N("omit scsi modules")),
                                                                      ),
                                                            ),
                                                   1, gtkadd(Gtk2::Frame->new(N("Add a module")),
                                                             create_hpaned(
                                                                           gtkset_size_request(
                                                                                               create_scrolled_window($tree),
                                                                                               200, $::isEmbedded ? 0 : 175),
                                                                           gtkpack_(Gtk2::VBox->new(0, 0),
                                                                                    1, gtkadd(Gtk2::ScrolledWindow->new,
                                                                                              $list
                                                                                             ),
                                                                                    0, gtksignal_connect(Gtk2::Button->new(N("Remove a module")),
                                                                                                         clicked => sub {
                                                                                                             my $iter = ($list->get_selection->get_selected)[1];
                                                                                                             return unless $iter;
                                                                                                             $list_model->remove($iter);
                                                                                                         }),
                                                                                   ),
                                                                          ),
                                                            ),
                                                  ),
                                         ),
                                1, gtkadd(Gtk2::Frame->new(N("Output")),
                                          gtkpack_(gtkset_size_request(
                                                                       gtkset_border_width(
                                                                                           Gtk2::HBox->new(0, 0),
                                                                                           5),
                                                                       30, 75),
                                                   1, $output = new Gtk2::Text(undef, undef),
                                                   0, Gtk2::VScrollbar->new($output->vadj),
                                                  ),
                                         ),
                                0, gtkpack__(new Gtk2::HBox(0, 0),
                                             gtksignal_connect(Gtk2::Button->new(N("Cancel")),
                                                               clicked => sub { ugtk2->exit(0) }
                                                              ),
                                             gtksignal_connect(Gtk2::Button->new(N("Build the disk")),
                                                               clicked => \&build_it
                                                              ),
                                             gtksignal_connect(my $expert_button = Gtk2::Button->new(""),
                                                               clicked => sub {
                                                                   $expert_mode = !$expert_mode;
                                                                   toggle_expert_button();
                                                               }),
                                            ),
                               ),
                      );

$window->{rwindow}->show_all();
toggle_expert_button();

$window->main;
ugtk2->exit(0);


sub toggle_expert_button {
    if ($expert_mode) {
        $expert_mod_frame->show();
        $expert_button->child->set(N("Normal Mode"));
    } else {
        $expert_mod_frame->hide();
        $expert_button->child->set(N("Expert Mode"));
    }
}

#-------------------------------------------------------------
# tree functions
#-------------------------------------------------------------
### Subroutines

sub fill_tree {
    my ($root_dir) = @_;
    $root_dir = "/lib/modules/" . $root_dir;
    # Create root tree item widget
    my $parent_iter = $tree_model->append_set(undef, [ 0 => $root_dir, 1 => $root_dir ]);

    # Create the subtree
    expand_tree($tree, $parent_iter, $tree_model->get_path($parent_iter)) if has_sub_trees($root_dir);
}

sub change_tree {
    $tree_model->clear;
    fill_tree(@_);
}

# Called whenever an item is clicked on the tree widget.
sub selected_tree {
    my ($select) = @_;
    my ($model, $iter) = $select->get_selected();
    return unless $model;       # no real selection
    my $file = $model->get($iter, 1);
    $iter->free;
    
    return if -d $file;

    my $size = (lstat($file))[7];

    return if member($file, @modules);
    push @modules, $file;
    $file =~ s|/lib/modules/.*?/||g;
    $list_model->append_set([ 0 => $file, 1 => $size ])->free;
}


# Callback for expanding a tree - find subdirectories, files and add them to tree
sub expand_tree {
    my ($tree, $parent_iter, $path) = @_;

    my $dir   = $tree_model->get($parent_iter, 1);
    my $child = $tree_model->iter_children($parent_iter);

    #- hackish: if first child has '' as name, then we need to expand on the fly
    if ($child && $tree_model->get($child, 0) eq '') {
        $tree_model->remove($child);
    }
    unless ($child && $tree_model->iter_has_child($parent_iter)) {
        foreach my $dir_entry (all($dir)) {
            my $entry_path = $dir . "/" . $dir_entry;
            if (-d $entry_path || $dir_entry =~ /\.o(\.gz)?$/) {
                $entry_path =~ s|//|/|g;
                
                my $iter = $tree_model->append_set($parent_iter, [ 0 => $dir_entry, 1 => $entry_path ]);
                #- hackery for partial displaying of trees, used in rpmdrake:
                #- if leaf is void, we may create the parent and one child (to have the [+] in front of the parent in the ctree)
                #- though we use '' as the label of the child; then rpmdrake will connect on tree_expand, and whenever
                #- the first child has '' as the label, it will remove the child and add all the "right" children
                $tree_model->append_set($iter, [ 0 => '' ])->free if has_sub_trees($entry_path);
            }
        }
    }
    $tree->expand_row($path, 0);
}



#-------------------------------------------------------------
# the function
#-------------------------------------------------------------
sub build_it {
    my $y;
    my $co = "/sbin/mkbootdisk --noprompt --verbose --device " . $device_combo->entry->get_text();
    if ($expert_mode) {
        $co .= " --mkinitrdargs -f" if $force_button->get_active;
        $co .= " --mkinitrdargs --ifneeded" if $needed_button->get_active;
        $co .= " --mkinitrdargs --omit-scsi-modules" if $scsi_button->get_active;
        $co .= " --mkinitrdargs --omit-raid-modules" if $raid_button->get_active;
        my $val;
        $list_model->foreach(sub {
                                 my ($model, $_path, $iter) = @_;
                                 my $module = $model->get($iter, 0);
                                 $module =~ s|.*?/||g;
                                 $co .= " --mkinitrdargs --with=" . $y; #. "/usr/lib/" . $kernel_combo->entry->get_text() . "/" . $y;
                                 return 0;
                             }, $val);
    }
    $co .= " " . $kernel_combo->entry->get_text();
    $co .= " 2>&1 |";
    create_dialog(N("Be sure a media is present for the device %s",  $device_combo->entry->get_text()), 1) or return;
    # we test if the media is present
  test:
    my $a = "dd count=1 if=/dev/null of=" . $device_combo->entry->get_text() . " 2>&1";
    my $b = `$a`;
    if ($b =~ /dd/) {
        create_dialog(N("There is no medium or it is write-protected for device %s.\nPlease insert one.", $device_combo->entry->get_text()), 1) ? goto test : return 0;
    }
  
    local *STATUS;
    open STATUS, $co or do { create_dialog(N("Unable to fork: %s", $!), 0); return };
    local $_;
    while (<STATUS>) {
        $output->insert(undef, undef, undef, $_);
    }
    close STATUS or create_dialog(N("Unable to properly close mkbootdisk: \n %s \n %s", $!, $?), 0);
  
    return (0);
}

####
# This is put at the end of the file because any translatable string
# appearing after this will not be found by xgettext, and so wont end in
# the pot file...
####

# Test whether a directory has subdirectories
sub has_sub_trees {
    my ($dir) = @_;
    
    foreach my $file (glob_("$dir/*")) {
        return 1 if -d $file or $file =~ /\.o(\.gz)?$/;

    }
    
    return 0;
}