#!/usr/bin/perl

use strict;
use IO::Socket;
use Gtk2;
use Gtk2::Helper;
use Gtk2::GladeXML;
use Gtk2::TrayIcon;
use FileHandle;
use Glib qw /TRUE FALSE/;

use constant ICON_BUSY => 0;
use constant ICON_OKAY => 1;
use constant ICON_CRITIC => 2;
use constant ICON_DISC => 3;
my $debug = 0;
my $pixmap_dir = '/usr/local/share/dup2date/pixmaps/';
my $sharepath = '/usr/local/share/dup2date/';
my $local_dir = $ENV{HOME}.'/.dup2date';
my $config_file = $local_dir.'/config';
my $textbuffer = Gtk2::TextBuffer -> new;
my ($window_state, $last_window_state, $progress, $window, $menu, $menu_check_upg, $menu_upg, $menu_show, $menu_quit, $tooltip, $statusbar, $treeview, $scrolledwindow1, $scrolledwindow2, $label, $hbuttonbox1, $text, $button_update, $button_upgrade, $eventbox);
my ($upg_pkg, $last_upg_pkg);
my @icon_list;
my @packages;
my @todo;
my ($sock,$tag);

my $current_mode = ICON_BUSY;
my $tip_text = '';
my $busy_tip = "Checking for upgrades";
my $okay_tip = "No upgrades available";
my $disc_tip = "Error checking for upgrades";
my $critic_tip = "Upgrades available";

my @cols = (
	{ title => 'Name', type => 'Glib::String', },
	{ title => 'VersionFrom', type => 'Glib::String', },
	{ title => 'VersionTo', type => 'Glib::String', },
);
my $store = Gtk2::ListStore -> new( map {$_->{type}} @cols );

my $startup = 1;
Gtk2 -> init;
&lagvindu;
&set_window_state(undef,FALSE);
my $timer = Glib::Timeout -> add (2160000, \&check_updates);
my $dotimer = Glib::Timeout -> add(250, \&dostuff);
&check_updates;
$startup = 0;
Gtk2 -> main;

sub dostuff
{
	&debug("DoStuff started.."); # Debugline
	if(defined($todo[0]))
	{
		my $line = $todo[0];
		&debug("todo : $line"); # Debugline
		if($line =~ m/update: 0/)
		{
			$eventbox -> remove ($icon_list[$current_mode]);
			$eventbox -> add ($icon_list[ICON_OKAY]);
			$current_mode = ICON_OKAY;
			$tooltip -> set_tip($eventbox, $okay_tip, undef);
			$statusbar -> push (1,$okay_tip);
			$button_update -> set_sensitive(FALSE);
			@packages = ();
			$store -> clear;
		}
		elsif($line =~ m/packages: /)
		{
			@packages = ();
			$store -> clear;
			&set_window_state('list_upgradeable');
			$line =~ s/packages: //;
			my @pkg_list = (split /,/,$line);
			foreach my $pkg(@pkg_list)
			{
				my $version_from = (split / /,$pkg)[1];
				my $version_to = (split / /,$pkg)[2];
				chomp($pkg);
				chomp($version_from);
				chomp($version_to);
				&putt_inn_i_tabell("$pkg $version_from $version_to");
			}	
			&set_mode('upgrade');
		}
		elsif($line =~ m/upgrade:/)
		{
			my $pakke = (split / /,$line)[2];
			chomp($pakke);
			$tooltip -> set_tip($eventbox, "Upgrading $pakke", undef);
			$statusbar -> push (1,"Upgrading $pakke");
			$label -> set('label',"Upgrading $pakke");
			if($line =~ m/upgrade: installing/)
			{
				&skrivivindu("Installing $pakke..\n");
			}
			elsif($line =~ m/upgrade: done/)
			{
				&skrivivindu("Install of $pakke is done.\n");
			}
			elsif($line =~ m/upgrade: Downloading/)
			{
				&skrivivindu("	Downloading $pakke\n");
			}
			elsif($line =~ m/upgrade: Unpacking/)
			{
				&skrivivindu("	Unpacking $pakke\n");
			}
			elsif($line =~ m/upgrade: Setup/)
			{
				&skrivivindu("	Setting up $pakke\n");
			}
			elsif($line =~ m/upgrade: Removing/)
			{
				&skrivivindu("	Removing old $pakke\n");
			}
		}
		elsif($line =~ m/status: idle/)
		{
			if(scalar(@packages) > 0)
			{
				&set_mode('upgrade');
			}
			else
			{
				&set_mode('up2date');
			}
			&debug("Server said idle, we're disconnecting from server.."); # Debugline
			&disconnect;
		}
		elsif($line =~ m/status: update/)
		{
			&set_mode('update');
		}
		elsif($line =~ m/status: upgrade/)
		{
			&set_mode('upgrading');
		}
		shift(@todo);
	}
	else
	{
		&debug("Nothing to do, closing ToDo-loop..\n"); # Debugline
		undef $dotimer;
		return FALSE;
	}
	1;
}

sub lagvindu
{
	my $glade = Gtk2::GladeXML -> new("$sharepath/dup2date.glade");
#	my $glade = Gtk2::GladeXML -> new("/home/users/t/theemperor/Dokumenter/Utvikling/dup2date/dup2date.glade");

	$window = $glade -> get_widget('window1');
	$text = $glade -> get_widget('textview1');
	$label = $glade -> get_widget('label');
	$scrolledwindow1 = $glade -> get_widget('scrolledwindow1');
	$scrolledwindow2 = $glade -> get_widget('scrolledwindow2');
	$treeview = $glade -> get_widget('treeview');
	$hbuttonbox1 = $glade -> get_widget('hbuttonbox1');
	$button_update = $glade -> get_widget('button_update');
	$button_upgrade = $glade -> get_widget('button_upgrade');
	my $button_close = $glade -> get_widget('button_close');
	$statusbar = $glade -> get_widget('statusbar');

	$menu = Gtk2::Menu -> new;

	my $tray = Gtk2::TrayIcon -> new ('dup2date');
	$eventbox = Gtk2::EventBox -> new;
	$tooltip = Gtk2::Tooltips -> new;
	
	@icon_list = (0..3);
	$icon_list[0] = Gtk2::Image -> new_from_file ($pixmap_dir.'/applet-busy.png');
	$icon_list[1] = Gtk2::Image -> new_from_file ($pixmap_dir.'/applet-okay.png');
	$icon_list[2] = Gtk2::Image -> new_from_file ($pixmap_dir.'/applet-critical.png');
	$icon_list[3] = Gtk2::Image -> new_from_file ($pixmap_dir.'/applet-disconnect.png');

	foreach my $icon (@icon_list)
	{
		$icon -> show;
	}

#	$eventbox -> remove ($icon_list[$current_mode]);
	$eventbox -> add ($icon_list[ICON_BUSY]);
	$current_mode = ICON_BUSY;
	$tooltip -> set_tip ($eventbox, $busy_tip, undef);
	$statusbar -> push (1,$busy_tip);
	
	$eventbox -> signal_connect ('button-release-event', \&handle_button_release);
	$tray -> add ($eventbox);	
	$menu_check_upg = Gtk2::MenuItem -> new ("Check for upgrades");
	$menu_check_upg -> signal_connect ('activate', sub
	{
		unless($startup eq '1')
		{
			\&send_to_server('update');
		}
	});
	$menu_check_upg -> show;
	
	$menu_upg = Gtk2::MenuItem -> new ("Upgrade all packages");
	$menu_upg -> signal_connect ('activate', sub 
	{
		unless($startup eq '1')
		{
			\&send_to_server('upgrade');
		}
	});
	$menu_upg -> set_sensitive (FALSE);
	$menu_upg -> show;

	$menu_show = Gtk2::MenuItem -> new ("Show upgrades");
	$menu_show -> signal_connect ('activate', sub {
		unless($startup eq '1')
		{
			\&set_window_state('list_upgradeable',TRUE)
		}
	});
	$menu_show -> set_sensitive (FALSE);
	$menu_show -> show;
	
	my $image_quit = Gtk2::Image -> new_from_stock ('gtk-quit', 'menu');
	$image_quit -> show;
	$menu_quit = Gtk2::ImageMenuItem -> new ("Quit");
	$menu_quit -> set_image($image_quit);
	$menu_quit -> signal_connect ('activate', sub { &quit; });
	$menu_quit -> show;
	
	$menu -> append ($menu_check_upg);
	$menu -> append ($menu_upg);
	$menu -> append (Gtk2::SeparatorMenuItem -> new);
	$menu -> append ($menu_show);
	$menu -> append (Gtk2::SeparatorMenuItem -> new);
	$menu -> append ($menu_quit);
	$menu -> show_all;
	
	$tray -> show_all;

	my $end_mark = $textbuffer->create_mark ('end', $textbuffer->get_end_iter, FALSE);
	$textbuffer->signal_connect (insert_text => sub {
		if($window -> get('visible') == 1)
		{
			$text->scroll_to_mark ($end_mark, 0.0, TRUE, 0.0, 1.0);
		}
	});

	$window -> signal_connect('delete_event' => sub
	{
		&set_window_state(undef,FALSE);
		'1';
	});

	$treeview -> set_model($store);
	$button_update -> signal_connect(clicked => sub
	{
		unless($startup eq '1')
		{
			$button_upgrade -> set_sensitive(FALSE);
			$button_update -> set_sensitive(FALSE);
			@packages = ();
			\&send_to_server("update");
		}
	});

	$button_upgrade -> signal_connect(clicked => sub
	{
		unless($startup eq '1')
		{
			\&send_to_server("upgrade")
		}
	});

	$button_close -> signal_connect(clicked => sub
	{
		&set_window_state(undef,FALSE);
	});

	&sett_opp_kolonner;
}

sub handle_button_release
{
	
	my ($widget, $event) = @_;
	if ($event -> button == 1)
	{
		if($window -> get('visible') == 0)
		{
			&set_window_state(undef,TRUE);
		}
		else
		{
			&set_window_state(undef,FALSE);
		}
		return TRUE;
	}
	elsif ($event -> button == 3)
	{
		$menu -> popup (undef, undef, undef, undef, $event -> button, $event -> time);
		return TRUE;
	}
	return FALSE;
}

sub set_window_state
{
	my $state = $_[0];
	my $visible = $_[1];
	
	if(defined($state))
	{
		&debug("Changing window state to $state"); # Debugline
		my $sw1state = $scrolledwindow1 -> get('visible');
		my $sw2state = $scrolledwindow2 -> get('visible');
		my $wstate = $window -> get('visible');
		if($sw1state eq '1') { $last_window_state = 'upgrade_status'; }
		if($sw2state eq '1') { $last_window_state = 'list_upgradeable'; }
		
		if($state eq 'list_upgradeable')
		{
			$scrolledwindow1 -> set('visible' => FALSE);
			$scrolledwindow1 -> hide();
			if(scalar(@packages) > 0)
			{ 
				$window -> resize('400','250');
				$label -> set('label' => 'Upgradeable packages');
				$treeview -> set('visible' => TRUE);
				$treeview -> show();
				$scrolledwindow2 -> set('visible' => TRUE);
				$scrolledwindow2 -> show();
			}
			else
			{
				$window -> resize('400','70');
				$label -> set('label' => 'Your system is up to date');
				$treeview -> set('visible' => FALSE);
				$treeview -> hide();
				$scrolledwindow2 -> set('visible' => FALSE);
				$scrolledwindow2 -> hide();
			}
			$hbuttonbox1 -> set('visible' => TRUE);
			$hbuttonbox1 -> show();
		}
		elsif($state eq 'update')
		{
			$scrolledwindow1 -> set('visible' => FALSE);
			$scrolledwindow1 -> hide();
			$window -> resize('400','70');
			$label -> set('label' => 'Updating packagelists');
			$treeview -> set('visible' => FALSE);
			$treeview -> hide();
			$scrolledwindow2 -> set('visible' => FALSE);
			$scrolledwindow2 -> hide();
			$hbuttonbox1 -> set('visible' => TRUE);
			$hbuttonbox1 -> show();
		}
		elsif($state eq 'upgrade_status')
		{
			$window -> resize('400','250');
			$label -> set('label' => 'Upgrading packages');
			$scrolledwindow2 -> set('visible' => FALSE);
			$scrolledwindow1 -> set('visible' => TRUE);
			$hbuttonbox1 -> set('visible' => FALSE);
			$scrolledwindow1 -> show();
			$scrolledwindow2 -> hide();
			$hbuttonbox1 -> hide();
		}
		$window_state = $state;
	}
	if(defined($visible))
	{
		if($visible == TRUE)
		{
			$text -> set_buffer($textbuffer);
			$window -> set('visible' => TRUE);
			$window -> show();
		}
		else
		{
			$window -> set('visible' => FALSE);
			$window -> hide();
		}
	}
	return TRUE;
}

sub set_mode # ($mode,[$tip])
{
	my $mode = $_[0];
	my $tip = $_[1] if defined;
	&debug("Changing mode to $mode"); # Debugline
	$eventbox -> remove($icon_list[$current_mode]);
	if($mode eq 'update')
	{
		$menu_upg -> set_sensitive(FALSE);
		$button_upgrade -> set_sensitive(FALSE);
		$menu_show -> set_sensitive(FALSE);
		$menu_check_upg -> set_sensitive(FALSE);
		$button_update -> set_sensitive(FALSE);
		$tooltip -> set_tip($eventbox,$busy_tip,undef);
		$statusbar -> push(1,$busy_tip);
		$label -> set('label',$busy_tip);
		&set_window_state('update',undef);
		$current_mode = ICON_BUSY;
	}
	elsif($mode eq 'upgrade')
	{
		$menu_upg -> set_sensitive(TRUE);
		$button_upgrade -> set_sensitive(TRUE);
		$menu_show -> set_sensitive(TRUE);
		$menu_check_upg -> set_sensitive (TRUE);
		$button_update -> set_sensitive (TRUE);
		$tooltip -> set_tip($eventbox,scalar(@packages).' '.$critic_tip, undef);
		$statusbar -> push(1,scalar(@packages).' '.$critic_tip);
		$label -> set('label',scalar(@packages).' '.$critic_tip);
		&set_window_state('list_upgradeable',undef);
		$current_mode = ICON_CRITIC;
	}
	elsif($mode eq 'upgrading')
	{
		$menu_upg -> set_sensitive(TRUE);
		$menu_show -> set_sensitive(FALSE);
		$menu_check_upg -> set_sensitive(FALSE);
		$button_upgrade -> set_sensitive(FALSE);
		$button_update -> set_sensitive(FALSE);
		&set_window_state('upgrade_status',undef);
		$current_mode = ICON_BUSY;
	}
	elsif($mode eq 'up2date')
	{
		$menu_upg -> set_sensitive(FALSE);
		$button_upgrade -> set_sensitive(FALSE);
		$menu_show -> set_sensitive(FALSE);
		$menu_check_upg -> set_sensitive (TRUE);
		$button_update -> set_sensitive (TRUE);
		$tooltip -> set_tip($eventbox,$okay_tip,undef);
		$statusbar -> push(1,$okay_tip);
		&set_window_state('list_upgradeable',undef);
		$current_mode = ICON_OKAY
	}
	$eventbox -> add($icon_list[$current_mode]);
	1;
}

sub sett_opp_kolonner # ($treeview,$store)
{
	my $renderer;
	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++;
	}
	return 1;
}

sub skrivivindu
{
	$textbuffer -> insert_at_cursor("@_");
	if($window -> get('visible') == 1) { $text -> set_buffer($textbuffer); }
}

sub quit
{
	Gtk2 -> main_quit;
}

sub check_updates
{
	&set_mode('update');
	&send_to_server('update');
}

sub send_to_server
{
	my $msg = $_[0] if(defined($_[0]));
	my $socket = $_[1] if(defined($_[1]));
	return FALSE unless(defined($msg));
	unless(defined($socket))
	{
		&debug("Seems like we are not connected to the server.. doing so will help us communicate :)\nConnecting : "); # Debugline
		if(my $socket = &connect)
		{
#		if(defined($socket))
#		{
			&debug("socket = $socket"); # Debugline
			$sock = $socket;
			&get_response($socket);
			&send_to_server($_[0],$socket);
		}
		else
		{
			#Here should go some kind of nice message into the gui...
			print "Eeeeh!! Not connected to the server... seems like a serious problem for now, and we will quit!\n";
			die;
		}
	}
	if((defined($msg)) && (defined($socket)))
	{
		&debug("We are now connected to the server, and is sending : $msg"); # Debugline
		print $socket "$msg\n";
	}
	1;
}

sub get_response
{
	my $socket = $_[0];
	$tag = Gtk2::Helper -> add_watch ( $sock->fileno, 'in', sub { server_response ($socket, $tag); });
	1;
}

sub server_response
{
	my ($fh,$tag) = @_;
	my $disc = $_[2] if(defined($_[2]));
	if((defined($disc)) or (eof($fh)))
#	if(eof($fh))
	{
		Gtk2::Helper -> remove_watch ($tag) or print "Kunne ikke gi slipp på Gtk2::Helper under server_response";
		close($fh);
	}
	else
	{
		my $line = <$fh>;
		push(@todo,$line);
		&debug("Got from server : $line"); # Debugline
		unless(defined($dotimer))
		{
			&debug("ToDo-loop is closed.. Starting loop"); # Debugline
			$dotimer = Glib::Timeout -> add(250, \&dostuff);
		}
	}
	1;
}

sub putt_inn_i_tabell # ($store,$packageline)
{
	my $line = $_[0];
	my ($package,$versionfrom,$versionto) = (split /\ /, $line);
	push(@packages,"$package $versionfrom $versionto");
	my @data=(
		{ Name => "$package", VersionFrom => "$versionfrom", VersionTo => "$versionto" }
	);
	foreach(@data)
	{
		my $iter = $store->append;
		$store->set($iter,
			0, $_->{'Name'},
			1, $_->{'VersionFrom'},
			2, $_->{'VersionTo'}
		);
	}
}

sub connect
{
	my $socket = new IO::Socket::INET
	(
		PeerAddr => 'localhost',
		PeerPort => '9000',
		Proto => 'tcp',
	);
	return FALSE unless $socket;
	return $socket;
}

sub disconnect
{
	print "Wooops... we have a problem... sock or tag are undefined...\n" unless((defined($sock)) && (defined($tag)));
	&debug("Sock=$sock , Tag=$tag\n"); # Debugline
	&server_response($sock,$tag,'disconnect');
	undef($sock);
#	close($sock);
}

sub debug
{
	if($debug)
	{
		my $line = $_[0];
		chomp($line);
		print scalar localtime," : $line\n";
	}
}
