#!/usr/bin/perl

#use-stuff
use utf8;
use Gtk2 '-init';
use Gtk2::Helper;
use Gtk2::GladeXML;
use locale;
use POSIX qw(locale_h);
use Glib qw /TRUE FALSE/;
setlocale(LC_NUMERIC, "C");
use strict;
use FileHandle;
use Data::Dumper;  # for debugging purposes

# This file must be downloaded from http://www.smokepit.net/~theemperor/Utvikling/Emerge-Progress/ep.glade and placed somewhere, then change the next line accordingly..
my $glade = Gtk2::GladeXML -> new("/home/users/t/theemperor/Projects/project1/project1.glade");


#Definering av ting..

my $logfile = "/var/log/emerge.log";
my @package_list;
my $lastline;
my @logfile;
my $packages = ();
my $updatetag;
my @cols = (
	{ title => 'Name',	type => 'Glib::String',	},
	{ title => 'Started',	type => 'Glib::String',	},
	{ title => 'Status',	type => 'Glib::String',	},
);

my @cols_done = (
	{ title => 'Name',	type => 'Glib::String',	},
	{ title => 'Started',	type => 'Glib::String',	},
	{ title => 'Completed in',	type => 'Glib::String',	},
);

#initsialisere ting..
my $store = Gtk2::ListStore->new( map {$_->{type}} @cols );
my $storedone = Gtk2::ListStore->new( map { $_->{type}} @cols_done );
my $window = $glade -> get_widget("window1");
my $swindow = $glade -> get_widget("scrolledwindow1");
my $swindow2 = $glade -> get_widget("scrolledwindow2");
my $vbox = $glade -> get_widget("vbox1");
my $treeview = $glade -> get_widget("treeview1");
$treeview -> set_model($store);
my $treeviewdone = $glade -> get_widget("treeview2");
$treeviewdone -> set_model($storedone);

$window->signal_connect(destroy => sub { &quit; });

#Start programmet..
&sett_opp;
Gtk2->main;

sub quit
{
	Gtk2 -> main_quit;
}

sub sett_opp
{
	&sett_opp_kolonner;
	&parselog;
	my $timer = Glib::Timeout->add(5000, \&test);
}

sub sett_opp_kolonner
{
	my $renderer;
	my $rendererdone;
	my $i = 0;
	foreach (@cols)
	{
		$renderer = Gtk2::CellRendererText->new;
		$renderer->signal_connect (edited => sub {
			my ($cell, $text_path, $new_text, $store) = @_;
			my $path = Gtk2::TreePath->new_from_string ($text_path);
			my $iter = $store->get_iter ($path);
			$store->set ($iter, 2, $text_path);
		}, $store);
		my $column = Gtk2::TreeViewColumn->new_with_attributes(
			$_->{title}, $renderer, text => $i );
		$treeview->append_column($column);
		$i++;
	}

	$i = 0;
	foreach(@cols_done)
	{
		$rendererdone = Gtk2::CellRendererText->new;
		$rendererdone->signal_connect (edited => sub {
			my ($celldone, $text_pathdone, $new_textdone, $storedone) = @_;
			my $pathdone = Gtk2::TreePath->new_from_string ($text_pathdone);
			my $iterdone = $storedone->get_iter ($pathdone);
			$storedone->set ($iterdone, 2, $text_pathdone);
		}, $storedone);
		my $columndone = Gtk2::TreeViewColumn->new_with_attributes(
			$_->{title}, $rendererdone, text => $i );
		$treeviewdone->append_column($columndone);

		$i++;
	}
	return 1;
}

sub parselog
{
	my $fh = FileHandle -> new;
	my $tag;
	open($fh, "</var/log/emerge.log") or &error("Kunne ikke lese emerge.log");
	$tag = Gtk2::Helper -> add_watch($fh -> fileno, 'in', sub { parselog_callback($fh,$tag); });
	return TRUE;
}

sub parselog_callback
{
	my ($fh,$tag) = @_;
	if(eof($fh))
	{
		Gtk2::Helper -> remove_watch ($tag) or &error("Kunne ikke gi slipp på Gtk2::Helper..");
		close($fh);
 		foreach my $pkg(keys %$packages)
 		{
			if($packages -> {$pkg}) { &add_to_list($pkg); }
 		}
		return TRUE;
	}
	else
	{
		my $line = <$fh>;
		push(@logfile,$line);

		if($line =~ ">>> emerge")
		{
			my $pkg = (split / /, $line)[7];
			my $pkg_cat = (split /\//, $pkg)[0];
			$pkg = (split /\//, $pkg)[1];
			my $time = (split /:/, (split /\ /, $line)[0])[0];

			$packages -> {$pkg} -> {'name'} = $pkg;
			$packages -> {$pkg} -> {'category'} = $pkg_cat;
			$packages -> {$pkg} -> {'start_time'} = $time;
			if(keys %$packages > @package_list)
			{
				unshift(@package_list, $pkg);
				&clean_package_list;
			}
		}
	}
	1;
}

sub clean_package_list
{
	if($#package_list >= 10)
	{
		foreach my $pkg(@package_list[10..($#package_list)])
		{
			delete $packages -> {$pkg};
			pop(@package_list);
		}
	}
	1;
}

sub add_to_list
{
		my $pkg = $_[0];
		my $package_id = $packages -> {$pkg} -> {'id'};
		my $package_name = $packages -> {$pkg} -> {'name'};
		my $package_category = $packages -> {$pkg} -> {'category'};
	
		my $package_start_time = gmtime($packages -> {$pkg} -> {'start_time'});
		my $package_status = &status_package($pkg);
		unless(defined($package_status)) { $package_status = 'Unknown'; }
		my @data=(
			{ Name => "$package_name", Time => "$package_start_time", Status => "$package_status" }
		);
		foreach (@data)
		{
			unless($package_status =~ "Completed" or $package_status =~ "Terminated")
			{
				my $iter = $store -> append;
				$store->set($iter,
					0, $_->{'Name'},
					1, $_->{'Time'},
					2, $_->{'Status'}
				);
			}
			else
			{
				my $iter = $storedone -> append;
				$storedone -> set($iter,
					0, $_->{'Name'},
					1, $_->{'Time'},
					2, $_->{'Status'}
				);
			}
		}
}

sub test
{
	my $testline = `tail -n1 $logfile`;
	my $testline2 = $logfile[$#logfile];
	chomp($testline);
	chomp($testline2);
	if($testline eq $testline2)
	{
		my $testi = 0;
		my $pathi = 0;
		while($testi < $#package_list + 1)
		{
			if(defined($packages -> {$package_list[$testi]}))
			{
				my $pkg = $packages -> {$package_list[$testi]} -> {'name'};
				if(defined($pkg))
				{
					unless($packages -> {$pkg} -> {'status'} =~ "Completed" or $packages -> {$pkg} -> {'status'} =~ "Terminated")
					{
						my $string;
						my $path;
						my $iter;
						my $time = time();
						&eta($pkg);
						my $eta = &gettime($packages -> {$pkg} -> {'eta'});
						my $percent = $packages -> {$pkg} -> {'percent'};
						my $status = $packages -> {$package_list[$testi]} -> {'status'};				
						if(($percent) || ($eta)) { $string = "$percent% ETA: $eta"; }
						else { $string = $status; }
						$path = Gtk2::TreePath->new_from_string($testi);
						if(defined($path)) { $iter = $store->get_iter($path); }
						if(defined($iter)) { $store->set($iter, 2, "$string"); }
					}
				}
			}
			$testi++;
		}
		return 1;
	}
 	else
 	{
 		@package_list = ();
 		$store->clear;
 		$storedone->clear;
 		&parselog;
 	}
}

sub status_package
{
	return 0 unless($_[0]);
	my $pkg = $_[0];
	my $searchfor_pkg = $pkg;
	$searchfor_pkg =~ tr/\+/./;
	for(my $line_i = 0; $line_i < @logfile; $line_i++)
	{
		my $line = $logfile[$line_i];
		if($line =~ m/$searchfor_pkg/)
		{
			if($line =~ m/Cleaning/)
			{
				$packages -> {$pkg} -> {'status'} = "Cleaning";
			}
			if($line =~ m/Compiling\/Merging/)
			{
				$packages -> {$pkg} -> {'status'} = "Compiling/Merging";
			}
			if($line =~ m/Post-Build Cleaning/)
			{
				$packages -> {$pkg} -> {'status'} = "Post-Build Cleaning";
			}
			if($line =~ m/AUTOCLEAN/)
			{
				$packages -> {$pkg} -> {'status'} = "Autocleaning";
			}
			if($line =~ m/completed emerge/)
			{
				my $whendone = (split /:::/, $line)[0];
				$whendone = (split /:/, $whendone)[0];
				$whendone = &gettime($whendone - $packages -> {$pkg} -> {'start_time'});
				$packages -> {$pkg} -> {'status'} = "Completed after $whendone";
			}
			if(defined($logfile[$line_i + 1]))
			{
				if($logfile[$line_i + 1] =~ 'terminating') #if the line after one telling about the status of the package is *** terminating, we can be pretty sure the emerge has been stopped, and that package will NOT be completed...
				{
					$packages -> {$pkg} -> {'status'} = "Terminated";
				}
			}
#this will loop for all logfile entries concerning the package, it may be many :) {'status'} will change a lot... probably there is MANY better ways to do this :)... anyhow, it will only return one value, and that is the last entry.., so the routine will only inform you about the last status of the package...
		}
	}
	unless($packages -> {$pkg} -> {'status'} eq "Compiling/Merging")
	{
		my $testi = 0;
		foreach my $tmp(@package_list)
		{
			if($tmp eq $pkg)
			{
				splice(@package_list,$testi,1);
			}
			$testi++;
		}
	}
	return $packages -> {$pkg} -> {'status'};
}

sub eta
{
	return 0 unless(defined($_[0]));
	my $pkg = $_[0];
	my $start_time = $packages -> {$pkg} -> {'start_time'};
	&percent($pkg);
	my $p = $packages -> {$pkg} -> {'percent'};
	my $t = time();
	if(defined($p))
	{
		my $tt;
		my $ss = ($t - $start_time); # time since start
		my $per_sec = ($p / $ss); # percent per second
		if($per_sec > 0) { $tt = ((100 - $p) / ($per_sec)); } # estimated time until 100%
		else { return 0; }
		$packages -> {$pkg} -> {'eta'} = $tt;
		return TRUE;
	}
}

sub percent
{
	return 0 unless(defined($_[0]));
	my $pkg = $_[0];
	my $progress;
	my $total = `find /var/tmp/portage/$pkg -iname "*.c" -or -iname "*.cpp" -or -iname "*.cc" | wc -l`;
	chomp($total);
	unless($total eq "0")
	{
		$progress = `find /var/tmp/portage/$pkg -iname "*.o" | wc -l`;
		chomp($progress);
	}
	if(defined($progress))
	{
		if(defined($total))
		{
			unless($progress eq "0" || $total eq "0")
			{	
				my $prosent = ($progress / $total * 100);
				$prosent = sprintf("%.2f", $prosent);
				$packages -> {$pkg} -> {'percent'} = $prosent;
				return TRUE;
			}
			else
			{
				return FALSE;
			}
		}
	}
}

sub gettime # gettime(tid i sekunder)
{
	return 0 unless(defined($_[0]));
        my $gettime = $_[0];
        if($gettime > 0)
        {
		my $returnstring;
                my $days = int($gettime/24/60/60);
                my $hours = int($gettime/60/60)%24;
                my $minutes = int($gettime/60)%60;
                my $seconds = int$gettime%60;
		if($seconds > 0) { $returnstring = "$seconds sec"; }
		if($minutes > 0) { $returnstring = "$minutes min, $seconds sec"; }
		if($hours > 0) { $returnstring = "$hours hours, $minutes min, $seconds sec"; }
		if($days > 0) { $returnstring = "$days days, $hours hours, $minutes min, $seconds sec"; }
                return $returnstring;
        } # if
	else { return 0; }
} # sub time

sub error
{
	my $message = shift;
	my $dialog = Gtk2::Dialog->new ('Feil!!', $window,
								'destroy-with-parent',
								'gtk-ok' => 'none');
	my $label = Gtk2::Label->new ($message);
	$dialog->vbox->add ($label);
	$dialog->set('modal' => 1);
	
	$dialog->signal_connect (response => sub { $_[0]->destroy; Gtk2->main_quit; });
	$dialog->show_all;
}
