#!/usr/bin/perl -w

################# LINUX UPDATE #################
# Package up-to-date monitor for linux.
#
# Copyright (c) 2004-2005 Patryk Zawadzki
# patrys@pld-linux.org
#
################################################

############### CONFIGURE THESE ################

my $refreshDelay = 2;
my $listCommand = 'poldek -q --noask --shcmd="ls -u -O -l" | sed -n "{s/\(.*\)\-\([^\-\ ]*\-[^\-\ ]*\)/\1 \2/; s/[0-9][0-9][0-9][0-9]\/[0-9][0-9]\/[0-9][0-9] [0-9][0-9]:[0-9][0-9]//; s/\@[^\ ]*//g; s/[\ ][\ ]*/\|/g; N; s/\n/|/; s/\([\ |]\)[\ ]*/\1/g; p}"';
my $listDelay = 2;
my $listFile = "$ENV{'TMPDIR'}/LinuxUpdate.lst";
my $updateCommand = 'poldek --up && poldek --upa';
my $refreshEveryNth = 50;
my $updateEveryNth = 150;
my $updateFile = "$ENV{'TMPDIR'}/LinuxUpdate.upd";
my $upgradeCommand = 'xterm -e poldek --noask --upgrade -G -v --pset';
my $pixmapDir = '/usr/share/pixmaps';

my $version = '0.5';

################################################

use IO::Socket;
use IO::Select;
use IO::Handle;
use Encode qw(decode encode);

socketpair(CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "Unable to open communication channel\n";

PARENT->autoflush(1);
CHILD->autoflush(1);

my $pid = fork();
if ($pid eq 0)
{
	close(CHILD);

	my $count = 0;

	my $select = IO::Select->new();
	$select->add(*PARENT);

	while (1)
	{
		$count++;

		my $line;

		if ($select->can_read(0))
		{
			chomp($line = <PARENT>);
			print "Got: $line\n";
			if ($line eq "upgrade")
			{
				if (open(fileIN,"$updateFile"))
				{
					print PARENT "upgrading\n";
					my @updateList = <fileIN>;
					chop(@updateList);
					if (@updateList eq 0)
					{
						unlink("$updateFile");
					}
					else
					{
						close(fileIN);
						print "Upgrading...\n";
						print "Calling $upgradeCommand $updateFile...\n";
						system "$upgradeCommand $updateFile";
						if ($? eq 0)
						{
							unlink("$updateFile");
						}
					}
					print PARENT "upgraded\n";
					print PARENT "ready\n";
				}
			}
			if ($line eq "refresh")
			{
				print PARENT "refreshing\n";
				print "Requesting fresh list...\n";
				$list = `$listCommand`;
				if ($? eq 0)
				{
					open(fileOUT, ">$listFile");
					flock(fileOUT, 2);
					print fileOUT $list;
					close(fileOUT);
				}
				print "List refreshed.\n";
				print PARENT "refreshed\n";
				print PARENT "ready\n";
			}
		}

		if ($count eq $updateEveryNth)
		{
			print PARENT "refreshing\n";
			$count = 0;
			print "Updating package list...\n";
			system $updateCommand;
			print PARENT "ready\n";
		}

		sleep($listDelay);
	}
	exit;
}

close(PARENT);

CHILD->blocking(0);

my $select = IO::Select->new();
$select->add(*CHILD);


use Gtk2::TrayIcon;
use Gtk2::SimpleList;

Gtk2->init;

my @list = ();

my @updateList = ();

my $quitting = 0;

my $trayIcon = Gtk2::TrayIcon->new("pld-update");

my $toolTip = Gtk2::Tooltips->new;

my $eventBox = Gtk2::EventBox->new;

my $trayImage;

my $iconImage;

my $window = Gtk2::Window->new('toplevel');

my $widgetBox = Gtk2::VBox->new(0, 5);

my $packageList = Gtk2::SimpleList->new('Update' => 'bool', 'Package name' => 'text', 'Installed' => 'text', 'Available' => 'text', 'Size' => 'text', 'Description' => 'text');
$packageList->set_rules_hint(TRUE);

sub in_array
{
	my $needle = shift;
	foreach (@_)
	{
		return 1 if $_ eq $needle;
	}
	return 0;
}

sub updateList
{
	@{$packageList->{data}} = ();
	foreach (@list)
	{
		@data = split(/\|/, $_);
		push @{$packageList->{data}}, [in_array($data[0], @updateList), $data[0], $data[2], $data[1], "$data[3] $data[4]", $data[5] ];
	}
	$packageList->columns_autosize();
}

sub setIcon
{
	my $num = @list;
	my $tooltip = "$num updates available.";

	if (!$window->visible)
	{
		updateList();
	}

	$toolTip->set_tip($eventBox, $tooltip);

	if ($num > 0)
	{
		$trayImage->set_from_pixbuf($iconImage[2]);
	}
	else
	{
		$trayImage->set_from_pixbuf($iconImage[1]);
	}
}

sub iconClick
{
	my ($widget, $event, $data) = @_;

	setIcon();
	if ($window->visible)
	{
		$window->hide;
	}
	else
	{
		$window->set_position('center');
		$window->show;
	}
}

sub exitClick
{
	print CHILD "quit\n";
	print "Quitting...\n";
	$quitting = 1;
	Gtk2->main_quit;
	kill 1, $pid;
}

sub hideClick
{
	$window->hide;
}

sub refreshClick
{
	print CHILD "refresh\n";
	get();
	updateList();
}

sub upgradeClick
{
	open(fileOUT, ">$updateFile");
	flock(fileOUT, 2);
	@updateList = ();
	for($i = 0; $i < @{$packageList->{data}}; $i++)
	{
		if ($packageList->{data}[$i][0] eq 1)
		{
			push @updateList, $packageList->{data}[$i][1];
		}
	}
	if (@updateList > 0)
	{
		print "Writing package list...\n";
		foreach(@updateList)
		{
			print fileOUT $_,"\n";
		}
	}
	print CHILD "upgrade\n";
	$window->hide;
	close(fileOUT);
}

sub windowClose
{
	$window->hide;
	return 1-$quitting;
}

my $count = 0;

sub updateStatus
{
	my $line;

	while ($select->can_read(0))
	{
		chomp($line = <CHILD>);
		print "Got: $line\n";
		if ($line eq "refreshed")
		{
			getPart2();
		}
		if ($line eq "upgraded")
		{
			refreshClick();
			$count = 0;
		}
	}
	$count++;
	if ($count eq $refreshEveryNth)
	{
		refreshClick();
		$count = 0;
	}
	return TRUE;
}

sub get
{
	print "Refreshing...\n";
}

sub getPart2
{
	if (open(fileIN,"$listFile"))
	{
		@list = <fileIN>;
		chop(@list);
		close(fileIN);
	}
	if (open(fileIN,"$updateFile"))
	{
		@updateList = <fileIN>;
		chop(@updateList);
		close(fileIN);
	}
	setIcon();
	print "Refreshing done.\n";
}

# Load necessary resources

$iconImage[1] = Gtk2::Gdk::Pixbuf->new_from_file_at_size("$pixmapDir/LinuxUpdate-no-updates.svg", 16, 16);
$iconImage[2] = Gtk2::Gdk::Pixbuf->new_from_file_at_size("$pixmapDir/LinuxUpdate-updates.svg", 16, 16);
$iconImage[3] = Gtk2::Gdk::Pixbuf->new_from_file_at_size("$pixmapDir/LinuxUpdate-logo.svg", 48, 48);

# Setup the tray icon

$trayImage = Gtk2::Image->new_from_pixbuf($iconImage[1]);

$eventBox->add($trayImage);

$trayIcon->add($eventBox);
$trayIcon->show_all;

$toolTip->set_tip($eventBox, "Wait for updates...");

$eventBox->signal_connect(button_press_event => \&iconClick);

# Setup the window

$window->set_default_icon($iconImage[2]);
$window->set_position('center');
$window->set_default_size(600, 400);
$window->set_border_width(5);

my $label = Gtk2::Label->new();
$label->set_markup("<big><b>LinuxUpdate v$version</b></big>\nSelect packages to update:");

my $titleBox = Gtk2::HBox->new(0, 5);

my $labelIcon = Gtk2::Image->new_from_pixbuf($iconImage[3]);

$titleBox->pack_start($labelIcon, 0, 0, 0);

$titleBox->pack_start($label, 0, 0, 0);

my $scrollBox = Gtk2::ScrolledWindow->new;

$scrollBox->set_policy('automatic','automatic');
$scrollBox->add($packageList);
$widgetBox->pack_start($titleBox, 0, 0, 0);
$widgetBox->pack_start($scrollBox, 1, 1, 0);

my $updateButton = Gtk2::Button->new_from_stock('gtk-execute');
$updateButton->signal_connect(clicked => \&upgradeClick);

my $refreshButton = Gtk2::Button->new_from_stock('gtk-refresh');
$refreshButton->signal_connect(clicked => \&refreshClick);

my $exitButton = Gtk2::Button->new_from_stock('gtk-quit');
$exitButton->signal_connect(clicked => \&exitClick);

my $hideButton = Gtk2::Button->new_from_stock('gtk-close');
$hideButton->signal_connect(clicked => \&hideClick);

my $buttonBox = Gtk2::HBox->new(0, 5);
$buttonBox->pack_start($refreshButton, 1, 1, 0);
$buttonBox->pack_start($updateButton, 1, 1, 0);
$buttonBox->pack_end($hideButton, 1, 1, 0);
$buttonBox->pack_end($exitButton, 1, 1, 0);

$widgetBox->pack_end($buttonBox, 0, 1, 0);
$widgetBox->show_all;
$window->add($widgetBox);
$window->signal_connect(delete_event => \&windowClose);

# Start doing your job

Glib::Timeout->add((1000 * $refreshDelay) => \&updateStatus);

refreshClick();

Gtk2->main;

0;
