#!/usr/bin/perl -w
##########################################################################
#
# Many Thanks to:
# The people of #perl in freenode for his patience and kind answers to my
# questions.
# Bert Hubert and Hightower for motivating me to finish this project.
#
#     
#
#
#
#
#
#
#
#
#
#
#
#
##########################################################################

use Gtk;
use Gtk::Gdk::ImlibImage;
use strict;
use warnings;

set_locale Gtk;
init Gtk;

#
# Global Program variables
# 
my $false = 0;
my $true = 1;

my $window;
my $main_vbox;
my $ctree;

	my ($closed, $closed_mask);
	my ($opened, $opened_mask);
	my ($leaf, $leaf_mask); 
#
# Array of network interfaces
# 
my @netifaces;

# Main program
#
# 
# Create Main Window
$window = new Gtk::Window( "toplevel" );
$main_vbox = new Gtk::VBox($false, 1);
$window->set_usize( 500, 300 );
$window->set_title( "QoSillas" );
$window->signal_connect( "destroy", sub { Gtk->exit( 0 ); } );
$window->border_width( 5 );
$window->realize();
$window->add($main_vbox);
$main_vbox->show();



create_ctreewin();
create_rrdwin();

scan_network_interfaces();
$window->show();

main Gtk;
die "oops, out of the gtk loop!\n";
#
#
# Main program end

#
# Program Functions
# 
#

sub sanity_checks {
	if ( $> ne "0" ) {
		printf "Warning: You are not root, you won't be able to change qdiscs\n";
	}
	if ( ! -x "/sbin/tc" ) {
		die "Error: /sbin/tc must be executable for this program to work\n";
	}
}


sub create_ctreewin {
	my $menubar;
        my $item_factory;
        my $accel_group;
        
        my $ctree_vbox;
	my @menu_items = ( { path        => '/_Actions',
			     type        => '<Branch>' },
			   { path        => '/Actions/_Rescan network interfaces',
			     accelerator => '<control>R',
			     callback    => \&scan_network_interfaces },
			   { path        => '/Actions/sep1',
			     type        => '<Separator>' },
			   { path        => '/Actions/Quit',
			     callback    => sub { Gtk->exit( 0 ); } },

			   { path        => '/_Options',
			     type        => '<Branch>' },
			   { path        => '/Options/Test' },

			   { path        => '/_Help',
			     type        => '<LastBranch>' },
			   { path        => '/_Help/About',
			     callback    => \&about });
        # Ctree stuff
        #
        #
	my @titles = ("Interface","Discipline","type","prio","rate","ceil","burst","cburst");

	#
	my $scrolled_window;

	# Auxiliar variables
	my $i;

	# Working variables
	my $iface;
	my @rootnode;
	my $tree;

	my @closed_xpm = ( "16 16 6 1",
			   "       c None s None",
			   ".      c black",
			   "X      c red",
			   "o      c yellow",
			   "O      c #808080",
			   "#      c white",
			   "                ",
			   "       ..       ",
			   "     ..XX.      ",
			   "   ..XXXXX.     ",
			   " ..XXXXXXXX.    ",
			   ".ooXXXXXXXXX.   ",
			   "..ooXXXXXXXXX.  ",
			   ".X.ooXXXXXXXXX. ",
			   ".XX.ooXXXXXX..  ",
			   " .XX.ooXXX..#O  ",
			   "  .XX.oo..##OO. ",
			   "   .XX..##OO..  ",
			   "    .X.#OO..    ",
			   "     ..O..      ",
			   "      ..        ",
			   "                ");
	my @open_xpm = ( "16 16 4 1",
			 "       c None s None",
			 ".      c black",
			 "X      c #808080",
			 "o      c white",
			 "                ",
			 "  ..            ",
			 " .Xo.    ...    ",
			 " .Xoo. ..oo.    ",
			 " .Xooo.Xooo...  ",
			 " .Xooo.oooo.X.  ",
			 " .Xooo.Xooo.X.  ",
			 " .Xooo.oooo.X.  ",
			 " .Xooo.Xooo.X.  ",
			 " .Xooo.oooo.X.  ",
			 "  .Xoo.Xoo..X.  ",
			 "   .Xo.o..ooX.  ",
			 "    .X..XXXXX.  ",
			 "    ..X.......  ",
			 "     ..         ",
			 "                ");
	my @leaf_xpm = ( "16 16 4 1",
			 "       c None s None",
			 ".      c black",
			 "X      c white",
			 "o      c #808080",
			 "                ",
			 "   .......      ",
			 "   .XXXXX..     ",
			 "   .XoooX.X.    ",
			 "   .XXXXX....   ",
			 "   .XooooXoo.o  ",
			 "   .XXXXXXXX.o  ",
			 "   .XooooooX.o  ",
			 "   .XXXXXXXX.o  ",
			 "   .XooooooX.o  ",
			 "   .XXXXXXXX.o  ",
			 "   .XooooooX.o  ",
			 "   .XXXXXXXX.o  ",
			 "   ..........o  ",
			 "    oooooooooo  ",
			 "                ");


	# Right button menu
	#
	
	my $menu_item;
	my $menu_item2;
	my $menu;
        #
        # 

	$ctree_vbox = new Gtk::VBox( $false, 1 );
	$ctree_vbox->border_width( 1 );

        #
        # Creation of menubar
        #



        $accel_group = new Gtk::AccelGroup();
        $item_factory = new Gtk::ItemFactory( 'Gtk::MenuBar',
                                         '<main>',
                                         $accel_group );
        $item_factory->create_items( @menu_items );
        $window->add_accel_group( $accel_group );
        $menubar = $item_factory->get_widget( '<main>' );

	$ctree_vbox->pack_start( $menubar, $false, $false, 0 );
	$menubar->show();
	$main_vbox->pack_start( $ctree_vbox,$true,$true,0 );
        #$ctree_vbox->realize();
        $ctree_vbox->show();


        #
        # Ctree Creation
        #

	
	($closed, $closed_mask) = initialize_pixmap( @closed_xpm );
	($opened, $opened_mask) = initialize_pixmap( @open_xpm );
	($leaf, $leaf_mask) = initialize_pixmap( @leaf_xpm );

	# Create Scrolled Window
	$scrolled_window = new Gtk::ScrolledWindow( undef, undef );
	$ctree_vbox->pack_start( $scrolled_window, $true, $true, 0);
	$scrolled_window->set_policy( 'automatic', 'always' );
	$scrolled_window->show();

	$ctree = new_with_titles Gtk::CTree ( 0, @titles );
	$ctree->set_column_width( 0, 150 );

	$scrolled_window->add($ctree);
	$ctree->show();
	
	# Rigth button menu
	$menu = new Gtk::Menu();
	
	$menu_item = new Gtk::MenuItem("Delete");
	$menu->append($menu_item);
	$menu_item->signal_connect( 'activate',\&delete_node, $ctree );
	$menu_item->show();

	$menu_item2 = new Gtk::MenuItem("Draw class activity");
	$menu->append($menu_item2);
	$menu_item2->signal_connect( 'activate', sub { print( "JAAAAAI iDRAAWPAAAyo\n" ); } );
	$menu_item2->show();

	# Add handlers
	$ctree->signal_connect( 'tree_select_row', sub {
		my ($ct, $node, $col) = @_;
		my ($t, $space);
		# print "Column: $col -> $node\n";
		($t, undef, undef) = $ct->node_get_pixtext($node, 0);
		print "node_get_pixtext Info O: $t\n";
		$t = $ct->node_get_text($node, 1);
		print "node_get_text Info 1: $t\n";
		($t, $space) = $ct->get_node_info($node);
		print "NInfo: '$t' $space\n";
	} );
	$ctree->signal_connect( 'button_press_event', \&ctree_button_press, $menu );

	
}

sub create_rrdwin()
{
        my $hbox;
        my $menubar;
        my @menu_items = ();
        my $scrolled_window;
        my $button;

        my $im = load_image Gtk::Gdk::ImlibImage("rrd.gif");
        my $w = $im->rgb_width;
        my $h = $im->rgb_height;
        $im->render($w,$h);

        $hbox = new Gtk::HBox( $true, 1 );
        $hbox->border_width( 1 );

	# Create Scrolled Window
	#$scrolled_window = new Gtk::ScrolledWindow( undef, undef );
	#$scrolled_window->set_policy( 'automatic', 'always' );
        #
        #
        #
        $button = new Gtk::Button( "Quit" );
        $button->signal_connect( "clicked", sub { Gtk->exit( 0 ); } );
        #$scrolled_window->add($button);
	$hbox->pack_start( $button, $true, $true, 0);
	#$scrolled_window->show();
        $main_vbox->pack_start($hbox,$false,$false,0);
        $hbox->show();
        $button->show();

}

sub ctree_button_press 
{
        my( $ctree, $menu, $event ) = @_;
	if ( $event->{button} == 3 ) {
		$menu->popup( undef, undef, undef, $event->{'button'}, undef );
	}
		

}

sub delete_node 
{
	my ( $menu, $ctree, $event ) = @_;
	my @selection;
	my ( $row, $column ) = $ctree->get_selection_info( $event->{'x'}, $event->{'y'} );
	my $node;
	my $position;
	printf "Button press subroutine $row $column\n";
	@selection = $ctree->selection();
	foreach $node ( @selection ) {
	#	$ctree->remove_node($node);
		printf $ctree->get_node_position ($node) . "\n";
	}
}

sub initialize_pixmap
{
   my @xpm = @_;

   my $pixmap;
   my $mask;
   my $style = $window->get_style()->bg( 'normal' );

   ($pixmap, $mask) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window, $style, @xpm );

   return ( $pixmap, $mask );
}



sub scan_network_interfaces 
{
        my @file;
	my $fh;
	my $line;
	my $i=0;

        if ( -r "/proc/net/dev" ) {
		open($fh , "/proc/net/dev");
        } else {
                die "Error: I can't access /proc/net/dev, either change permissions or check if /proc is mounted";
	}
	
	if( @netifaces ) {
		my $position;
		for (@netifaces) {
			$position = $ctree->get_node_position ($netifaces[$i]->{"node"});
			$ctree->remove($position);
			$i++;
		}	
	}

	undef @netifaces;
	$i=0;
        @file = <$fh>;
	shift @file;
	shift @file;
	foreach $line (@file) {
		$line =~ /(\w*):/;
		push @netifaces,{ name => "$1", node => "undef", qdiscs => [ ],qdrootnode => "" };
	}
	$i=0;
	for (@netifaces) {
		$netifaces[$i]->{"node"} = $ctree->insert_node(undef, undef, [$netifaces[$i]->{"name"}, "","hw","","","",""], 5 , $closed, $closed_mask, $opened, $opened_mask, $false, $true);
		$i++;
	}
        
        scan_qdiscs();
	

}



sub scan_qdiscs 
{
        my @tc_out;
        my $line;
        my $iface;
	my $i=0;
        my $sibling = undef;
        for (@netifaces) {
                @tc_out = `/sbin/tc class show dev $netifaces[$i]->{"name"}`;
                #
                #
                foreach $line (@tc_out) {
                        if( $line =~ m/class\D+htb\D+(\d+:\d+)\D+root\D+rate\D+(\d+)Kbit\D+ceil\D+(\d+)Kbit\D+burst\D+(\d+)\D+cburst\D+(\d+)/ ) {
                        # $1 ID
                        # $2 Rate 
                        # $3 Ceil
                        # $4 Burst
                        # $5 Cburst
				push @{ $netifaces[$i]->{"qdiscs"} },$1;
				$netifaces[$i]->{"qdrootnode"} = $ctree->insert_node($netifaces[$i]->{"node"},$netifaces[$i]->{"qdrootnode"}, ["$1", "HTB","$1","NA","$2","$3","$4","$5"], 5 ,$leaf, $leaf_mask, undef, undef, $false, $false);
                                print "\t aņadido: $1 $2 $3 $4 $5";
                                
                        }
                }
                
                foreach $line (@tc_out) {
                        if( $line =~ m/class\D+htb\D+(\d+:\d+)\D+parent\D+(\d+:\d+)\D+prio\D+(\d+)\D+rate\D+(\d+)Kbit\D+ceil\D+(\d+)Kbit\D+burst\D+(\d+)\D+cburst\D+(\d+)/ ) { 
				$sibling = $ctree->insert_node($netifaces[$i]->{"qdrootnode"},$sibling, [$1, "HTB","$1","$3","$4","$5","$6","$7"], 5 ,$leaf, $leaf_mask, undef, undef, $true, $false);
                                print "\t aņadido: $1 $2 $3 $4 $5";
                        }
                }


                
                #
                #
                #
                #
		$i++;
        }
}

