#!/usr/bin/perl -w
use Tk;
use strict;

# style notes:  I am a C(++) programmer, which should be obvious looking at this.
# I DO know how to reduce this to quite a few less lines, but am going for
# clarity here, and easy-to-copy examples of how to do some things.


# declare stuff upfront
my 	($mw,$ssbutton,$timeentry,	# widgets
	$canvas,$arc,$frame,$exit,
	$sstext,$running, $timetext,	# vars
	$timenum,$arcext,$clocksize,	#
	$countdown,$deftime, 
	$timerid);			# IDs


# I like to put my subs first, just to pee off folks who do it otherwise
# besides, in a gui, this is where all the action is, the main is just setup
#///////////////////////
sub modechange 
{
# $ssbutton->focus; # grab focus to ourself
 if ($running)
 {
  # do whatever we do to stop
  $mw->afterCancel($timerid); # stop timer ticks, cancels "repeat" or "after"
  $running = 0;
  $sstext = "Resume";
  $canvas->itemconfigure($arc, -extent => 0);
 } else
 {
  # do whatever to begin
  $timenum = int ($timetext * 60);	# forces numeric interpretation of text
  $countdown = $timenum;	
  if(0 == $timenum) {exit}; # if they type abc as a time, they deserve this
  $running = 1;
  $sstext = "Pause";
  $arcext = 359.9; # start with clock full, 360 overflows
  $canvas->itemconfigure($arc, -extent => $arcext);
  $timerid = $mw->repeat(1000, \&on_tick); # every second
  }
} #end modechange
#///////////////////////
sub on_tick
{
 unless ($running) # belt AND suspenders, here 
 {
  die "We got a tick and we didn't think we were running!/n"; 
 }
 if ($countdown >= 1) #since we allow floats now...be careful 
 {
  $countdown--;
  $arcext = 359.9 * ($countdown/$timenum);
  $timetext = $countdown/60;
  $canvas->itemconfigure($arc, -extent => $arcext);
  if (0 == int $countdown) {finished();}
 } else 
 { # more belt and suspenders, from unless above
  finished(); # stop things, tell user time is up
 }
}
#///////////////////////
sub finished
{
 modechange(); # stop, since we had to be running to get here
 $timetext = $deftime; # run again, same default period 
 $timenum = 0; # cure for any roundoff errors
 $canvas->itemconfigure($arc, -extent => 0); # show doneness
 $exit->focus; # user may want to quit now, make it easy
 $countdown = 10; # number of times to announce doneness
 $timerid = $mw->repeat(300, \&announce); # restart timer for fast beep
}

#///////////////////////
sub announce
{ # do whatever to tell user time is up.  TODO, think of more stuff
 if ($countdown > 0)
 {
  $mw->bell;
  if ($countdown % 2)
  {
   $canvas->configure(-background => 'blue');
  } else
  {
   $canvas->configure(-background => 'white');
  }
  $countdown--;
 } else
 {
  $mw->afterCancel($timerid); # stop timer ticks, cancels "repeat" or "after" 
 }
}
#///////////////////////

# !!! start of main program

#init things
$running = 0;
$sstext = "Start";
$deftime = "4.2";
$timetext = $deftime;
$timenum = $timetext * 60;
$arcext = 359.9;
$clocksize = 240; # pixels

# create main window
$mw = MainWindow->new;
$mw->title("PCB Timer");

# to grab the real bottom, then have things side by side there, this has to go first.
$frame = $mw->Frame()->pack(-side => 'bottom');
$ssbutton = $frame->Button(-textvariable => \$sstext, -cursor => 'exchange', 
			-command => \&modechange)->pack(-expand => 1, -side => 'left');

$exit = $frame->Button(-text => "Exit", -command => sub { exit }, -cursor => 'pirate')
	->pack(-expand => 1, -side => 'right', -fill => 'x');

# drawing space for our fakey clock/timer analog
$canvas = $mw->Canvas(-background => 'blue', -height => $clocksize, 
			-width => $clocksize)->pack(-side => "top");
$arc = $canvas->createArc(5,5,$clocksize-10,$clocksize-10, 
			-fill => 'red', -extent => $arcext, -start =>90);

#setup input time and lables
$mw->Label(-text => "Time:")->pack(-side => "left");
$timeentry = $mw->Entry(-textvariable => \$timetext, -background => 'white', 
			-width => 5)->pack(-side => "left", -expand => 1);
$mw->Label(-text => "minutes")->pack(-side => "left", -expand => 1); 

$timeentry->bind("<Return>", \&modechange); # make enter push the start button
$timeentry->focus; # you gotta type something, as the default is total BS
								
MainLoop; # draw the windows and process events

# fall out when user hits exit
