#! /usr/bin/perl -w

##########################################################
#
# Capture and display data from Standard Counter
#
# (C) 2011 Doug Coulter, GPL v2 license -- share and enjoy,
# but if you improve it, be sure and share it back, to
# clab@swva.net, or www.coultersmithing.com/forums
#
#
# See use section below for any modules you'll need to install first
##########################################################
# It's probably obvious I'm coding this like I was a C programmer
# (because I am), and squishing fancy expressions into single liners
# would just make the inevitable changes this is going to need harder
# to figure out later -- for a C programmer (me).
#
# Anything I thought was dodgy is marked with an @@@ for searching
#
# If you're a perl purist or stylist, sorry 'bout that.  
# TMTOWTDI and, TANSTAAFL for good measure.
# lots of steal-me code here for beginning perl people
##########################################################
#
# Note, I'm using a goodly number of globals here to avoid tons of typing
# parameters into subroutines.  Most assume you'll call them in the right
# order and they're valid.  This only works for well disciplined programmers
# and not even for them if things get too big and confusing. It's not stylin,
# don't do this at home.  It does make things faster to run and to write if
# you keep it all in your head well enough.

use strict; # make perl tell me about typos and how dumb I am

# bring in a pile of modules, this code is already written and tested by someone else (mostly)

use Gtk2 -init; # sets up GUI basics
use Glib qw(TRUE FALSE);
use Gtk2::GladeXML; # lets GUI come from a glade file
use Graphics::GnuplotIF qw(GnuplotIF); # gnuplot for plots
use File::Spec::Functions qw/:ALL/; # changes things like ./ to a real path
use File::Basename; # to get path to program for config files and logs

# the serial interface "requires" different modules for windows and perl, so they're not up here
# but are decided at runtime after perl tells us which opsys we have


################   Globals
my $gui; # main window
my $ssbut; # start-stop button
my $running = FALSE; # start off stopped...duh
my $makelogbut;  # button reference
my $makelog;     # boolean value
my $pbut1;       # radio 1 (no plots)
my $pbut2;	 # Geiger only
my $pbut3;	 # both
my $pbut4;	 # both in seperate gnuplot windows
my $makeplotopt; # radio button data (0-3)
my $logwin;	 # text reference
my $logwinbuf;	 # the text buffer reference, special GTK stuff
my $logtext;	 # finally, some actual text itself (text editor windows are complex and a PITA)

my $filepath;    # hold base path for local files, we'll make up the rest
my $logfilepath; # full path and filename for log file goes in this

my @xaxis;	# for gnu plots
my @gpm1;	# y axis data for geigers 1 sec intervals
my @gpm10;	# y axis data for geigers, 10 sec averages
my @npm1;	# y axis data for neutrons, 1 sec intervals
my @npm10;	# y axis data for neutrons, 10 sec averages

# debug stuff 
my $dbgctr = 0;
my $dbgtxt;
################   Subroutines

####################
# Make logfile path.  Assumes $filepath is already a valid path, and $logfilepath variable exists
# we'll just make a name out of the local time of day when called...should be unique
# A hassle because we can't use colons for time separator, spaces on some opsys, and slashes to make
# it look as expected, as some opsys would interpret those things as dir separators or invalid name chars
sub makelogfilepath
{
 my $tmp;
 my @time = localtime(); # get array of time data
 $tmp = join('_',$time[4]+1,$time[3],$time[5]+1900,$time[2],$time[1],$time[0]); #join the bits
 $tmp .= '.log'; # add file extension
 $logfilepath = catfile($filepath,$tmp); # put together opsys independent
 #print ("logfilepath:$logfilepath\n"); # debug
}

###########################################################
# get pathname to program directory for use in config and log files
# we need this insane dance as the program path in $0 might be ./name
# which is kind of hard to use right.  We want an absolute path where
# the script lives, and this gets it (with two modules needed!)
sub getmypath()
{
 my $path = rel2abs($0); # fix up any relative path we got
# print ("\nmunged path is: $path\n"); # debug
 $path = dirname($path); # now get just the dir without progname on it
#print ("\nfinally, path to program is: $path\n"); # debug 
 return $path;
}

####################
# create, open and insert a header line into our logfile
# assumes $logfilepath is valid when called
sub createlogfile
{
 open (LOG, "> $logfilepath") or die "Can't create $logfilepath: $!";
 print LOG "# name:$logfilepath line format: time, GPM1 GPM10 GTC NPM1 NPM10 NTC\n"; # human readable help in file
}
####################
sub writelog
{ # get text buffer from edit view and write it to file, then close
 my $start; # text iters for get text
 my $end;
 $start = $logwinbuf->get_start_iter(); # find the start
 $end = $logwinbuf->get_end_iter(); # find the end
 $logtext = $logwinbuf->get_text($start,$end,FALSE); # get the text, no hidden chars
 print LOG $logtext; # do the deed
 close (LOG) or die "Couldn't close $logfilepath: $!"; # all done with this file
}


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

# to hook this up, in glade use gtk_main_quit as the handler in the mainwin/GtkWidget/destory-event
sub gtk_main_quit  { Gtk2->main_quit; } # fall out of main loop and exit

####################
# we get here when you click the go/stop button
# to make this work, I had to select the "after" option for the signal in glade
# the assignment of this routine name to the toggled event was done in glade, manually
sub on_gostop_toggled
{
 if ($running)
 { # stop, write any log file
  $running = FALSE; # internal boolean flag - stop timer on next call to poll
  $ssbut->set_label('ReStart'); # relabel button
  print ("\nStopping\n"); # debug
  writelog() if $makelog; # this only checked at start time, so user can't change during a run



 } else # time to start up the world!
 { # setup and start things - get user's desires from GUI controls
  $running = TRUE;
  $ssbut->set_label('Stop');
  $makelog = $makelogbut->get_active() ? TRUE : FALSE; # heh, nice one-liner here	
# radio button get data - @@@ let me know if there's a better way, this stinks
  if ($pbut1->get_active) {$makeplotopt = 0;} # very primitive way to get to a number
  if ($pbut2->get_active) {$makeplotopt = 1;} # and perl doesn't have a switch anyway to
  if ($pbut3->get_active) {$makeplotopt = 2;} # use this fancy number!
  if ($pbut4->get_active) {$makeplotopt = 3;}

  makelogfilepath(); # create a path and name in current program directory
  createlogfile() if $makelog; # start up a log if requested

  print ("\nReset and startup. Makelog:$makelog Makeplotopt:$makeplotopt\n"); # debug
# clear old plots, start new if requested


  Glib::Timeout->add(250, \&poll); # start the calls to the poll subroutine
 } # end if running else "start case"

} # end on_gostop_toggled

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

# sub to add a line to our text view, then scroll it to the end
# found this trick here: http://www.gtkforums.com/viewtopic.php?t=1307
sub addtxtline
{ # text edit boxes are a PITA no matter the opsys or GUI toolset, it seems
 my $line = shift; # whatever you handed us to insert at end
 my $iter; # a "thing" that marks a place in a textedit buffer
 $iter = $logwinbuf->get_end_iter(); # find the end
 $logwinbuf->insert($iter,$line); # insert at end
 $logwin->scroll_to_iter($iter,0,FALSE,0,0); # scroll to make it visible
}

#################### this one's where the action is during a run
sub poll
{ # we'll have GTK call this on quarter seconds to look for a line of input, 
  # do stuff about that, or return if nothing to do
 return FALSE unless $running; # this also stops the timer hits

#debug here to create some fake data to test log and plot with
 addtxtline ("a test:$dbgctr\n"); # debug, create text to add to edit box
 $dbgctr++;

 return TRUE; # keep timer running
}

################  Main program

print ("\nprogram running\n"); # debug
$filepath = getmypath(); # find out where we are despite opsys munging - base dir for all other files
#print "path:$filepath\n"; # debug
$gui = Gtk2::GladeXML->new(catfile($filepath,'StdC.glade')); # uses right type of dir separators for opsys
#@@@ note there is also a new_from_buffer($buffer) that can be used to do this from a string
# once things are stable, this means we can get rid of the glade file and put it here under __END__ instead
# or some other way to put all that XML in here

$ssbut = $gui->get_widget('gostop'); # pointer to start button?
$makelogbut = $gui->get_widget('logfileb'); # pointer to make logfile check button
$pbut1 = $gui->get_widget('radiobutton1'); # pointers to radiobuttons, a little primitive here
$pbut2 = $gui->get_widget('radiobutton2'); # pointers to radiobuttons, a little primitive here
$pbut3 = $gui->get_widget('radiobutton3'); # pointers to radiobuttons, a little primitive here
$pbut4 = $gui->get_widget('radiobutton4'); # pointers to radiobuttons, a little primitive here
$logwin = $gui->get_widget('logwin'); # pointer to text display window
$logwinbuf = $logwin->get_buffer(); # later use $logwinbuf->set_text($some_text);

$gui->signal_autoconnect_from_package('main'); # hooks subs to signals if things are named right


# Enter the event loop
Gtk2->main;

# any cleanup here after user hits quit

__END__ 
