#!/usr/bin/perl -I/mit/gimp/lib/perl5/site_perl

use strict;

use Gtk;
use Gtk::Atoms;

init Gtk;


package CheckListItem;

sub new
{
    # effects:  creates a new checklist item with specified text 
    #           and point values.  Also passed a callback to call
    #           when the state changes.
    #           this also contains a gtk object 
    #           with untested/pass/fail buttons.
    my ($class, $recalc, $pointval, $text) = @_;
    my $this = bless {}, $class;
    $this->{"pointval"} = $pointval;
    $this->{"text"}     = $text;
    $this->{"recalc"}   = $recalc;
    $this->{"status"}   = "untested";
    $this->init () or return undef;
    return $this;
}

sub handle_toggle 
{
    my ($b, $this, $newstatus) = @_;
    #print "handle_toggle: this:<$this> b:<$b> newstatus:<$newstatus>\n";
    if   ($b->active) { 
	$this->{"status"} = $newstatus; 
	#print "Status switched to $newstatus\n";
    }
    &{$this->{"recalc"}};
}

sub init 
{
    # effects:  initializes this to contain the appropriate gtk object.
    my ($this) = @_;
    
    my $listline = new Gtk::HBox(0,0);
    $listline->show;
    
    my $untested = new Gtk::RadioButton "untested";
    $listline->pack_start($untested,0,0,0);
    $untested->set_state(1);
    $untested->show;		
    $untested->signal_connect("toggled",  \&handle_toggle, $this, "untested");
    $this->{"w_untested"} = $untested;

    my $pass = new Gtk::RadioButton "pass", $untested;
    $listline->pack_start($pass,0,0,0); 
    $pass->show;
    $pass->signal_connect("toggled",  \&handle_toggle, 
			  $this, "pass");
    
    my $fail = new Gtk::RadioButton "fail", $untested;
    $listline->pack_start($fail,0,0,0);
    $fail->show;
    $fail->signal_connect("toggled",  \&handle_toggle, 
			  $this, "fail");
    
    my $label = new Gtk::Label $this->getText;
    $listline->pack_start($label,0,0,20);
    $label->show;

    my $val = new Gtk::Label ("(".$this->getPointVal.")");
    $listline->pack_start($val,0,0,10);
    $val->show;

    $this->{"listline"} = $listline;
    return 1;
}

sub clear
{
    # effects: clears this to untested
    my ($this) = @_;
    $this->{"w_untested"}->set_state(1);
}

sub getText     { my ($this) = @_; return $this->{"text"}; }
sub getPointVal { my ($this) = @_; return $this->{"pointval"}; }
sub getListLine { my ($this) = @_; return $this->{"listline"}; }

sub isTested    { my ($this) = @_; return ($this->{"status"} ne "untested"); }
sub isPassed    { my ($this) = @_; return ($this->{"status"} eq "pass"); }

sub loadList {
    # effects: Loads in a list of checklist items from $fname and 
    #          returns the list.  Also sets the recalc_all field
    #          on them to $rcall.
    my ($fname, $rcall) = @_;
    my (@ilist, $pts, $desc);
    my $RSsave = $/;
    $/ = "*";
    print "Loading $fname...\n";
    open IN, "<$fname" or die "Couldn't open $fname\n";
    while ($pts = <IN>) {
	$pts =~ s/^\s*//;
	$pts =~ s/\s*\*?$//;
	$desc = <IN>;
	$desc =~ s/\s*\*?$//;
	if (!$pts) { print "Skipped $desc...\n"; next; }
	print "Added <$pts> <$desc>\n";
	push @ilist, new CheckListItem($rcall, $pts, $desc),	
    }
    close IN;
    $/ = $RSsave;
    return @ilist;
}


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

package main;

sub destroy_window {
	my($widget, $windowref, $w2) = @_;
	$$windowref = undef;
	$w2 = undef if defined $w2;
	0;
}


sub do_exit {
	Gtk->exit(0);
}

use vars qw/ $nametext $w_passcount $w_untestedcount $w_name  /;

sub create_main_window {
    # effects:  Creates the main window.  Sets global variables
    #           to point to some of the key widgets.

    my (@item_list, $resultdir, $summfile) = @_;

    my ($window, $outervb, $scw, $scrollbox);

    $window = new Gtk::Window('toplevel');
    $window->set_name("Grading Checklist");
    $window->set_uposition(20, 20);
    $window->set_usize(500, 300);
    
    $window->signal_connect("destroy" => \&do_exit);
    $window->signal_connect("delete_event" => \&do_exit);

    $outervb = new Gtk::VBox(0,0);
    $window->add($outervb);
    $outervb->show;

    $scw = new Gtk::ScrolledWindow(undef, undef);
    $scw->set_policy('automatic', 'automatic');
    $scw->show;
    $scw->border_width(10);
    $outervb->pack_start($scw, 1, 1, 0);

    $scrollbox =  new Gtk::VBox(0, 0);
    #border_width $box2 10;
    $scrollbox->show;
    $scrollbox->border_width(10);
    $scw->add($scrollbox);

    my $item;
    for $item (@item_list) {
	#printf "adding <%s>\n", $item;	
	#printf "adding %s\n", $item->getText;	

	$scrollbox->pack_start($item->getListLine,0,0,0);
	
	my $hline = new Gtk::HSeparator;
	$scrollbox->pack_start($hline,1,1,0);
	$hline->show;
    }

    my $nameline = new Gtk::HBox(0,10);
    $outervb->pack_start($nameline, 0, 0, 5);
    my $namelabel = new Gtk::Label("Name: ");
    $namelabel->show;
    $nameline->pack_start($namelabel, 0, 0, 5);
    $w_name = new Gtk::Entry;
    $w_name->set_text("");
    $w_name->signal_connect("changed", sub { my ($b)=@_;
					     $nametext = $b->get_text;
					 });
    $nameline->pack_start($w_name, 0, 0, 5);
    $w_name->show;
    $nameline->show;

    ############
    
    my $resultline = new Gtk::HBox(0,10);
    $outervb->pack_start($resultline, 0, 0, 5);

    my $label; my $e;
    $label = new Gtk::Label("Untested: ");
    $label->show;
    $resultline->pack_start($label, 0, 0, 5);

    $e = new Gtk::Entry;
    $e->set_text("---");
    $e->set_editable(0);
    $resultline->pack_start($e, 0, 0, 5);
    $e->show;
    $w_untestedcount = $e;

    $label = new Gtk::Label("Passed: ");
    $label->show;
    $resultline->pack_start($label, 0, 0, 5);

    $e = new Gtk::Entry;
    $e->set_text("---");
    $e->set_editable(0);
    $resultline->pack_start($e, 0, 0, 5);
    $e->show;
    $w_passcount = $e;

    $resultline->show;

    ##########

    my $line = new Gtk::HBox(1,10);
    $line->show;
    $outervb->pack_start($line, 0, 0, 5);

    my $button; 
    $button = new Gtk::Button("Save");
    $button->show;
    $line->pack_start($button, 0, 0, 10);
    $button->signal_connect("pressed", \&do_save, $resultdir, $summfile);
   
    $button; 
    $button = new Gtk::Button("Clear");
    $button->show;
    $line->pack_start($button, 0, 0, 10);
    $button->signal_connect("pressed", sub { my ($b,@il)=@_;
					     for (@il) { $_->clear; };
					     $w_name->set_text("");
					 },
			    @item_list);

    my $button; 
    $button = new Gtk::Button("Quit");
    $button->show;
    $line->pack_start($button, 0, 0, 10);
    $button->signal_connect("pressed", \&do_exit);
   
    $window->show;

}

use vars qw/ @checklistitems /;

sub do_save {
    my ($button, $basedir, $summarylog) = @_;
    
    my $utaccum=0;
    my $passaccum=0;
    for (@checklistitems) {
	if (!$_->isTested) { $utaccum   += $_->getPointVal; }
	if ($_->isPassed)  { $passaccum += $_->getPointVal; }
    }

    if ($nametext eq "") { print "Empty name text!  Not saved!\n"; return; }
    if ($utaccum > 0) { print "Some tests not performed!  Not saved!\n"; return; }


    my $fname = "$basedir/$nametext.failed";

    print "Saving to $summarylog and $fname\n";

    open SLOG, ">>$summarylog" or die "couldn't append to $summarylog\n";
    printf SLOG "%s:%s\n", $nametext, $passaccum;
    close SLOG;

    open OUT, ">$fname" or die "couldn't append to $fname\n";
    for (@checklistitems) {
	if (!$_->isPassed) {
	    printf OUT "%s*%s*\n", $_->getPointVal, $_->getText;
	}
    }
    close OUT;
}

sub recalc_all {
    my $utaccum=0;
    my $passaccum=0;
    for (@checklistitems) {
	if (!$_->isTested) { $utaccum   += $_->getPointVal; }
	if ($_->isPassed)  { $passaccum += $_->getPointVal; }
    }
    $w_passcount->set_text($passaccum);
    $w_untestedcount->set_text($utaccum);
}

#@checklistitems = (
#    new CheckListItem(\&recalc_all, 3, "hello bob world"),
#    new CheckListItem(\&recalc_all, 1, "hello world"),
#    new CheckListItem(\&recalc_all, 2, "hello world zog"),
#    new CheckListItem(\&recalc_all, 3.5, "wobble hello world"),
#    new CheckListItem(\&recalc_all, .5, "meeples hello world"),
#    new CheckListItem(\&recalc_all, 3, "hello world"),
#    new CheckListItem(\&recalc_all, 3, "hello world"),
#    new CheckListItem(\&recalc_all, 10, "wheeeeeeeeeeeeeee\nboom")
#);

if (@ARGV != 3) {
    print "Usage: $0 questions resultdir summaryfile\n";
    exit -1;
}

@checklistitems;
@checklistitems = CheckListItem::loadList($ARGV[0], \&recalc_all);

parse Gtk::Rc "checklist.gtkrc";

#create_main_window(@checklistitems, "results", "summary.out");
create_main_window(@checklistitems, $ARGV[1], $ARGV[2]);
recalc_all;

main Gtk;
