#!/usr/bin/perl

#- convert in.png -colors 128 -treedepth 6 -dither out.gif

use strict;

use lib '/usr/lib/libDrakX';
use Getopt::Long;
use ugtk2 qw(:all);
use Gtk2::Gdk::Keysyms;
use MDK::Common;
use Image::Magick;


my $kernel_size = 1500; #- in KiB
my $initrd_size = 130; #- in KiB

my $lilo_block_size = 50; #- in KiB
my $isolinux_block_size = 64; #- in KiB

my @modes = (
    { Vesa => 0x101, X =>  640, Y =>  480 },
    { Vesa => 0x103, X =>  800, Y =>  600 },
    { Vesa => 0x105, X => 1024, Y =>  768 },
    { Vesa => 0x107, X => 1280, Y => 1024 },
);

my (%image_size, $kernel_and_initrd_size);
my ($progress_rect, $progress_color) = ([], {});
my ($timer_pos, $timer_bg, $timer_fg) = ({}, {}, {});
my ($entry_rect, $entry_selected_color, $entry_color) = ([], {}, {});
my $isolinux_mode;

my $magick = Image::Magick->new;

my ($current_rect, $current_point);
my ($image_area, $image_pixbuf);

sub move_point {
    my ($up_down, $direction) = @_;
    my $wanted = $current_point->{$direction} + $up_down;
    if (0 <= $wanted && $wanted < $image_size{$direction}) {
	$current_point->{$direction} += $up_down;
    }
    $image_area->queue_draw;
}
sub create_image_area() {
    $image_area = Gtk2::DrawingArea->new;
    $image_area->can_focus(1);
    $image_area->add_events('button-press-mask');
    $image_area->signal_connect(button_press_event => \&image_button_pressed);
    $image_area->signal_connect(expose_event => \&image_expose);
    gtkmodify_font($image_area, 'Monospace 12');

    my $keys = {
	$Gtk2::Gdk::Keysyms{Down}   => sub { move_point( ($_[0] ? 5 : 1), 'Y') },
	$Gtk2::Gdk::Keysyms{Up}     => sub { move_point(-($_[0] ? 5 : 1), 'Y') }, 
	$Gtk2::Gdk::Keysyms{Left}   => sub { move_point(-($_[0] ? 5 : 1), 'X') },
	$Gtk2::Gdk::Keysyms{Right}  => sub { move_point( ($_[0] ? 5 : 1), 'X') },
	$Gtk2::Gdk::Keysyms{q} => sub { Gtk2->main_quit },
	$Gtk2::Gdk::Keysyms{Escape} => sub { Gtk2->main_quit },
    };

    $image_area->signal_connect(key_press_event => sub {
	my (undef, $event) = @_;

	if (my $f = $keys->{$event->keyval}) {
	    $f->(member('control-mask', @{$event->state}));
	}
	1;
    });

    $image_area->grab_focus;
    $image_area->show;
    $image_area;
}
sub image_expose {
    my ($widget) = @_;
    my $window = $widget->window;
    $window->draw_pixbuf($widget->style->white_gc, $image_pixbuf, 0, 0, 0, 0, -1, -1, 'none', 0, 0);

    if (!$isolinux_mode) {
	{
	    my $layout = $widget->create_pango_layout('--:--');
	    my ($width, $height) = $layout->get_pixel_size;
	    $window->draw_rectangle(color_index2gc($window, $timer_bg), 1, $timer_pos->{X}, $timer_pos->{Y}, $width, $height);
	    $window->draw_layout(color_index2gc($window, $timer_fg), $timer_pos->{X}, $timer_pos->{Y}, $layout);
	}
	my ($x, $y, $w, $h) = rectangle2xywh($entry_rect);
	my @std_labels = ('linux', 'failsafe', '2.6.3-7mdk', 'X' x ($w / 8));
	for (my $nb = 0; $nb < int($h / 16); $y += 16, $nb++) {
	    my $label = shift(@std_labels) || 'label_' . ($nb+1);
	    my $gc = color_index2gc($window, !$nb ? $entry_selected_color : $entry_color);
	    my $layout = $widget->create_pango_layout($label);
	    $window->draw_layout($gc, $x, $y, $layout);
	}
    }
    {
	my ($x, $y, $w, $h) = rectangle2xywh($progress_rect);
	my $w2 = $w - $w % nb_steps();
	if ($current_rect) {
	    $window->draw_rectangle(color_index2gc($window, $progress_color), 1, $x, $y, $w2, $h);
	    $window->draw_rectangle($widget->style->black_gc, 0, $x, $y, $w, $h);
	} else {
	    $window->draw_rectangle(color_index2gc($window, $progress_color), 1, $x, $y, $w, $h);
	}
    }
    0;
}
sub image_button_pressed {
    my (undef, $event) = @_;

    if ($event->button eq '3') {
	create_popup()->popup(undef, undef, undef, undef, '3', $event->time);
	return 1;
    }

    my $point = { X => $event->x, Y => $event->y };
    my $chosen_point;

    if ($current_rect) {
	if (!@$current_rect) {
	    my @corners = ({ X => 0, Y => 0 }, 
			   { X => 0, Y => $image_size{Y} - 1 },
			   { X => $image_size{X} - 1, Y => 0 }, 
			   { X => $image_size{X} - 1, Y => $image_size{Y} - 1 });
	    @$current_rect = ($corners[farthest($point, @corners)], nearest($point, @corners));
	}
	$current_point = nearest($point, @$current_rect);
    } elsif ($current_point) {
	%$current_point or %$current_point = %$point;
    } else {
	return;
    }

    %$current_point = %$point;
    $current_point->{Color} = $magick->Get("index[$point->{X},$point->{Y}]");

    $image_area->queue_draw;
    1;
}

sub image_set_file {
    my ($file) = @_;

    $image_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($file);
    %image_size = (X => $image_pixbuf->get_width, Y => $image_pixbuf->get_height);
    $image_area->set_size_request($image_size{X}, $image_size{Y});
    $image_area->queue_draw;
}

sub color_index2gc {
    my ($window, $color) = @_;
    my ($r, $g, $b) = split(',', $magick->Get("colormap[$color->{Color}]"));
    my $gc = Gtk2::Gdk::GC->new($window);

    my $gdk_color = Gtk2::Gdk::Color->new($r, $g, $b) or die "bad color for $r,$g,$b";
    $window->get_colormap->rgb_find_color($gdk_color);
    $gc->set_rgb_fg_color($gdk_color);
    $gc;
}

sub rectangle2xywh {
    my ($rect) = @_;

    my $x = min($rect->[0]{X} , $rect->[1]{X});
    my $y = min($rect->[0]{Y} , $rect->[1]{Y});
    my $w = abs($rect->[0]{X} - $rect->[1]{X});
    my $h = abs($rect->[0]{Y} - $rect->[1]{Y});
    ($x, $y, $w, $h);
}

sub distance {
    my ($p1, $p2) = @_;
    sqr($p1->{X} - $p2->{X}) + sqr($p1->{Y} - $p2->{Y});
}

sub farthest {
    my ($point, @others) = @_;
    my $i = 0;
    my $dist = 0;
    my $farthest;
    foreach (@others) {
	my $d = distance($point, $_);
	if ($d >= $dist) {
	    $dist = $d;
	    $farthest = $_;
	}
    }
    $farthest;
}

sub nearest {
    my ($point, @others) = @_;
    my $i = 0;
    my $dist;
    my $nearest;
    foreach (@others) {
	my $d = distance($point, $_);
	if (! defined $dist || $d < $dist) {
	    $dist = $d;
	    $nearest = $_;
	}
    }
    $nearest;
}

sub create_popup() {
    my %l = my @l = (
	     'Progress bar position' => sub { $current_rect = $progress_rect },
	     'Progress bar color' => sub { $current_point = $progress_color },
	     '' => sub {},
		     $isolinux_mode ? (
	     'switch to lilo mode' => sub { $isolinux_mode = 0 },
				      ) : (
	     'Timer position' => sub { $current_point = $timer_pos },
	     'Timer text color' => sub { $current_point = $timer_fg },
	     'Timer background' => sub { $current_point = $timer_bg },
	     '' => sub {},
	     'Entry position' => sub { $current_rect = $entry_rect },
	     'Entry text selected color' => sub { $current_point = $entry_selected_color },
	     'Entry text color' => sub { $current_point = $entry_color },
	     '' => sub {},
	     'switch to isolinux mode' => sub { $isolinux_mode = 1 },
					  ),
		     
	    );

    my $popup = Gtk2::Menu->new;
    foreach (group_by2(@l)) {
	my ($descr, $f) = @$_;
	gtkappend($popup,
		  gtksignal_connect(Gtk2::MenuItem->new_with_label($descr), 
				    activate => sub {
					$current_rect = $current_point = undef;
					$f->();
				    }));
    }
    $popup
}
sub nb_steps() {
    $kernel_and_initrd_size / ($isolinux_mode ? $isolinux_block_size : $lilo_block_size);
}

sub read_parameters {
    my ($file) = @_;
    my %h = getVarsFromSh($file);

    $entry_rect->[0]{X} = $h{'entry_x'};
    $entry_rect->[0]{Y} = $h{'entry_y'};
    $entry_rect->[1]{X} = $h{'entry_x'} + $h{'entry_w'};
    $entry_rect->[1]{Y} = $h{'entry_y'} + $h{'entry_h'};
    $entry_color->{Color} = $h{'entry_bg'} - 64;
    $entry_selected_color->{Color} = $h{'entry_fg'} - 64;

    $timer_pos->{X} = $h{'timer_x'};
    $timer_pos->{Y} = $h{'timer_y'};
    $timer_bg->{Color} = $h{'timer_bg'} - 64;
    $timer_fg->{Color} = $h{'timer_fg'} - 64;

    $progress_color->{Color} = $h{'progress_c'} - 64;
    $progress_rect->[0]{X} = $h{'progress_x'};
    $progress_rect->[0]{Y} = $h{'progress_y'};
    $progress_rect->[1]{X} = $h{'progress_x'} + $h{'progress_real_w'};
    $progress_rect->[1]{Y} = $h{'progress_y'} + $h{'progress_h'};

    $isolinux_mode = $h{'isolinux_mode'};
}

sub save_parameters {
    my ($file) = @_;
    my %h;
   
    $h{'mode'} = (find { $image_size{X} eq $_->{X} } @modes)->{Vesa};
    $h{'clear_h'} = $image_size{X};
    $h{'clear_w'} = $image_size{Y};

    if (!$isolinux_mode) {
	($h{'entry_x'}, $h{'entry_y'}, $h{'entry_w'}, $h{'entry_h'}) = rectangle2xywh($entry_rect);
	$h{'entry_w_chr'} = int($h{'entry_w'} / 8);
	$h{'entry_h_chr'} = int($h{'entry_h'} / 16);
	$h{'entry_bg'} = $entry_color->{Color} + 64;
	$h{'entry_fg'} = $entry_selected_color->{Color} + 64;

	$h{'timer_x'} = $timer_pos->{X};
	$h{'timer_y'} = $timer_pos->{Y};
	$h{'timer_bg'} = $timer_bg->{Color} + 64;
	$h{'timer_fg'} = $timer_fg->{Color} + 64;
    }

    $h{'progress_c'} = $progress_color->{Color} + 64;
    ($h{'progress_x'}, $h{'progress_y'}, $h{'progress_real_w'}, $h{'progress_h'}) = rectangle2xywh($progress_rect);
    $h{'progress_w'} = int($h{'progress_real_w'} / nb_steps());

    $h{'isolinux_mode'} = $isolinux_mode;

    output($file, map { "$_=$h{$_}\n" } sort keys %h);
}

# MAIN #########################################################################
my $usage = <<EOF;
usage: $0 [--kernel <kernel> --initrd <initrd>] [--size <size in KiB>] <image>
(kernel and initrd are used to compute the size of data to load)
EOF

GetOptions('kernel=s' => \ (my $kernel),
	   'initrd=s' => \ (my $initrd),
	   'size=s' => \$kernel_and_initrd_size,
           'isolinux' => \ (my $force_isolinux_mode),
	  ) or die $usage;

my ($file) = @ARGV;
@ARGV == 1 && -e $file or die $usage;

if ($kernel_and_initrd_size) {
    $kernel and die "give kernel and initrd or size, not both\n";
} else {
    if ($kernel) {
	$initrd or die "give both kernel and initrd\n";
	$kernel_size = (-s $kernel) / 1024 or die "bad file $kernel: $!\n";
	$initrd_size = (-s $initrd) / 1024 or die "bad file $initrd: $!\n";
    }
    $kernel_and_initrd_size = $kernel_size + $initrd_size;
}

my $err;
$err = $magick->Read($file) and die $err;

my $bmp_file_name = $file;
$bmp_file_name =~ s/\.\w\w\w?$//;
$bmp_file_name .= '.bmp';

if ($file ne $bmp_file_name || $magick->Get('colors') > 128) {
    warn "writing $bmp_file_name\n";
    $err = $magick->Quantize(colors => 128, dither => 'True', treedepth => 6) and die $err;
    $err = $magick->Write(filename => "bmp3:$bmp_file_name", compression => 'None') and die $err;
}
read_parameters("$bmp_file_name.parameters");
$isolinux_mode ||= $force_isolinux_mode;

my $window_widget = Gtk2::Window->new('toplevel');
$window_widget->signal_connect(destroy => sub { Gtk2->main_quit });
gtkadd($window_widget, 
       gtkpack(Gtk2::VBox->new(0,0), create_image_area()));
image_set_file($bmp_file_name);

$window_widget->show;
$image_area->window->set_cursor(Gtk2::Gdk::Cursor->new('crosshair'));
Gtk2->main;

save_parameters("$bmp_file_name.parameters");