#!/usr/bin/perl

use Modern::Perl '2014'; # we only have 5.18 on this machine
use File::Basename 'dirname'; # for knowing where we are (ease for developer)
use Gtk3 -init; # for our GUI
use Glib  qw(TRUE FALSE); # for periodic events etc
#use Gtk3::Helper; # allows us to add "watch" callbacks to handles?
#http://askubuntu.com/questions/319568/i-cant-configure-rhythmbox-as-gobject-introspection-1-is-not-installed for when you can't install gtk3 or glib
use DBI; # for mySQL interface

################ globals, because #################################
my $DSN = "DBI:mysql:Fsr:localhost";		# data source name
#my $DSN = "DBI:mysql:Fsr:fusorpi:3306";
my $DBUser = "Ops"; # we are at the op position (I hope)
my $DBPass = "data";
my $dbh; # database handle
my $sth; # statement handle

### bunch of gui stuff
my $mainwin;
my $builder = Gtk3::Builder->new(); # gtkbuilder object, creates a gui from glade's xml

my $faketimeu; # need one for each arduino
my $faketimeg;

# references to gui objects for later munging with them
my $mungetimesr; # ignore arduino times and make sequential instead
my $runnumr; 	# run number drop down ref
my $SSbuttonr;	# start/stop button ref
my $updaterunr; # get latest run number from DB (button)
my $rater;	# combo box for rate of replots
my $kvmar;	# checkbox to create kv/ma plot
my $countersr;	# checkbox to create counters plot
my $pressurer;	# checkbox to create pressure plot
my $realtimer;	# continuous mode?
# values of gui elements
my $mungetimes; # should I make up my own time?
my $runnum;	# content of selection 
my $lastrun;	# updated when runs box filled up (perhaps from button press, or at init)
my $runnumindex;# last index in runs box
my $updaterate;	# seconds per replot
my $continuous; # do we poll forever until quit?
my $kvma;	# plot this?
my $counters;	# plot that?
my $pressure;	# plot the other?
### timing
my $running; # are we polling etc
my $pollcount;

# gnuplot file handles
my $pplot; # pressure plot gnuplot handle
my $viplot; # voltages and currents, kV and Ma
my $cplot; # counters

# doh 
my $i;  # the universal iterator
my $gcalfactor = 5.01/4096; # or close, we'll adjust to reality
my $acalfactor = 4.955/4096;
my $hna;
########################### gnuplot series variables ###############
my @ux;		#uno x (time) ms since clap
my (@ua0,@ua1,@ua2,@ua3,@ua4,@ua5); #uno a/d channels
my (@uc0,@uc1,@uc0a); # counters

my @gx;		#gas/ion x (time) ms since clap
my (@ga0,@ga1,@ga2,@ga3,@gda,@gc,@gfl); #gas/ion a/d channels and other
####################################################################
sub cleararrays ()
{
 @ux=@ua0=@ua1=@ua2=@ua3=@ua4=@ua5=@uc0=@uc0a=@uc1=();
  $hna = 0;
 @gx=@ga0=@ga1=@ga2=@ga3=@gda=@gc=@gfl=();
 $pollcount = 0; # starting a new one  
}
####################################################################
sub getwidgetrefs # totally specific to this program, as it needs to match names with the GUI elements
{ # gets global references to the actual GUI objects for later use
 $mungetimesr = $builder->get_object('mungetimes');
 $runnumr = $builder->get_object('runnum');
 $SSbuttonr = $builder->get_object('SSbutton');
 $updaterunr = $builder->get_object('updaterun');
 $rater = $builder->get_object('rate');
 $realtimer = $builder->get_object('continuous');
 $kvmar = $builder->get_object('kvma');
 $countersr = $builder-> get_object('counters');
 $pressurer = $builder->get_object('pressure');
}
####################################################################
sub fillrunbox()
{
 my $txt;
 my $ar = $dbh->selectcol_arrayref('SELECT runno FROM runs WHERE 1;');
# $ary_ref = $dbh->selectcol_arrayref($statement);
 $runnumindex = -1; # correct value for "nothing in box"
 $runnumr->remove_all();  # clear out any existing..
 
 foreach $txt (@{$ar})
 {
  $runnumr->append_text($txt);
  $lastrun = $txt; # when we're done, this will be the last one
  $runnumindex++;
#  say "content of runs was:$txt at index:$runnumindex";
 }
  $runnumr->set_active($runnumindex); # 
#clear arrays since we're doing different data and they all think they start at zero time
 cleararrays();
}
####################################################################
sub initwidgets() # put defaults into things that want them
{ ### @@@ get ur piping hot defaults here
 $rater->set_active(0); # which in this case is id=1 and one second.  Go figure
 $realtimer->set_active(FALSE); # TRUE
 $kvmar->set_active(TRUE); # assume we'll want to plot this
 $countersr->set_active(TRUE);
 $pressurer->set_active(TRUE);
 $realtimer->set_active(TRUE);
 fillrunbox();	# get the list of runs out of the db to stuff runnum
}
####################################################################
sub getguivalues()
{ # we check at start of a run, no other time (can't change your mind in the middle)
# @@@ make sure runnum is consistent
 $mungetimes = $mungetimesr->get_active();
 $kvma = $kvmar->get_active(); # which plots to make
 $counters = $countersr->get_active();
 $pressure = $pressurer->get_active();
 $updaterate = $rater->get_active_id();
 $runnum = $runnumr->get_active_text(); # find out what run we selected
 $continuous = $realtimer->get_active(); # do we run till stopped?
}
####################################################################
sub create_plot_handle {
  my $plothandle;
  open ($plothandle, '|- ', "gnuplot 2> /tmp/gnuploterr") # plot_gerr_log.txt or /dev/null
                or die "\n$0 : failed to open pipe to \"gnuplot\" : $!\n";
  $plothandle->autoflush(1); # duh, required!
  gnuplot_cmd($plothandle,"set term X11 background rgb \"white\" ");
  return $plothandle;
 }
 ####################################################################
sub gnuplot_cmd {  #swiped from gnuplotif and modified
    my $gplot = shift; # gplot filehandle to send command to
    my  (@commands)  = @_; # slurp the rest
    @commands = map {$_."\n"} @commands;
    print $gplot @commands
        or die "Couldn't write to pipe: $!";
 #       print "gnuplot:@commands"; # debug

} # ----------  end of subroutine gnuplot_cmd  ----------

####################################################################
sub setup_pplot()
{ # all the usual setup for gnuplot, titles, labels and so on
  my @setups = ("set title \"Pressure\"","set log y","set grid ytics mytics xtics","set xlabel \"Seconds\"","set ylabel \"milliBar\"","set key left top","setmytics 10"); # list of commands
  gnuplot_cmd($pplot,@setups);
 }
####################################################################
sub setup_viplot()
{ # @@@ add setups for multiple line lables, yaxes (y2 for ma)
  my @setups = ("set title \"kV and mA\"","set grid ytics mytics xtics","set xlabel \"Seconds\"","set ylabel \"kV\"","set key left top"); # list of commands
  gnuplot_cmd($viplot,@setups);
 }
####################################################################
sub setup_cplot()
{ # plot for counters
  my @setups = ("set title \"Counters CPM\"","set log y","set grid ytics mytics xtics","set xlabel \"Seconds\"","set ylabel \"CPM\"","set key left top","setmytics 10"); # list of commands
  gnuplot_cmd($cplot,@setups);
 }
####################################################################
####################################################################

sub on_SSbutton_clicked ()
{
 my $updatetime;
 if ($running)
 { # stop
  $running = 0;
  $SSbuttonr->set_label("Start");
 } else
 { # start
  getguivalues(); # what does the user want?
  cleararrays(); #@@@ thought I did this elsewhere...
  $faketimeu = $faketimeg = 0.1; # ir I'm going to ignore db times due to a glitch
  $SSbuttonr->set_label("Stop");
# create gnuplot instances here for user's request, if not already existing 
  if ($pressure) {
   $pplot = create_plot_handle();
   setup_pplot(); # title, style, and so forth
  }
    if ($kvma) { # for volts/amps
   $viplot = create_plot_handle();
   setup_viplot(); # title, style, and so forth
  }
  if ($counters)
  {
    $cplot = create_plot_handle();
    setup_cplot();   
   }
  $updatetime = $updaterate * 1000; # humans think in seconds, this wants milliseconds
  Glib::Timeout::add($mainwin,$updatetime,\&poll); #!!!! Victory!  Timed polling with sleeps in between.
  $running = 1; # or any true value
 }
 say "ss button clicked, running:$running";
}
####################################################################
sub on_updaterun_clicked
{ # get latest runno from db runs table
 fillrunbox(); # refresh run box
# say "last run was:$lastrun";
}
####################################################################
sub createguifile # create the gui from xml in a .glade file 
{ # use this for developer convienience.  Final product should put glade xml
  # after the __END__ tag and use createguilocal instead
 $builder->add_from_file(dirname($0) . '/plotrun.glade'); #@@@ hardcoded filename
 $mainwin = $builder->get_object('mainwin'); #@@@ assumes main window is called mainwin
 $builder->connect_signals(undef);
 $mainwin->set_screen( $mainwin->get_screen() ); #??? from an example.  Seems redundant?
 $mainwin->signal_connect(destroy => sub {Gtk3->main_quit});
 $mainwin->show_all();
# not so program-specific you couldn't use it again
}
####################################################################
sub getgi() # get data from the gas/ion table
{
 my $ms = defined ($gx[-1] ) ? $gx[-1] : 0; # force numeric even if undef first time
 $ms *= 1000; #@@@ ugly
 my $mbar;
 my $stmt = "SELECT ms,a0,a1,a2,a3,c0,da,flags FROM fusectl WHERE runno = $runnum AND ms > $ms;";
 my $ar = $dbh->selectall_arrayref($stmt); #selectall_array doesn't exist anymore?  Gacky ugly.
 foreach my $rowr (@{$ar}) # each row of values as array ref
 { # later, we will add conversions to real units from a schema lookup that contains perl we can eval
#  say "gi_row: @{$rowr}";

 $mbar = 10**((1.667* @{$rowr}[3] *$gcalfactor*2.013)-11.33) ; # pressure in mbar
  
  if ($mungetimes)
  {
   push @gx,$faketimeg;
   $faketimeg += 0.1; # 100ms/sample assumed 
  } else
  { # use real reported time
    push @gx, @{$rowr}[0] * .001; # seconds for now
  }
  push @ga0,@{$rowr}[1]  * .0124; # main current set
  push @ga1,@{$rowr}[2] * 0.013037809648; # main 
  push @ga2,$mbar;
  push @ga3,@{$rowr}[4] * 0.0027361899822;#  0.00645327826536; # ion ma
  push @gc, @{$rowr}[5] * 600; # counter not wired yet, but
  push @gda,@{$rowr}[6] * .196; # output duty cycle to ion supply control as kv
  push @gfl,@{$rowr}[7]; # flags 4=ion on, 2= gas in, 1= gas out
 }
# say "\ngi iteration\n";
}
####################################################################
sub getu() # get data from uno main data aq
{
 my $ms = defined($ux[-1] ) ? $ux[-1] * 1000 : 0; # force numeric even if undef first time
 my $stmt = "SELECT ms,a0,a1,a2,a3,a4,a5,c0,c1 FROM uno WHERE runno = $runnum AND ms > $ms;";
 my $ar = $dbh->selectall_arrayref($stmt); #selectall_array doesn't exist anymore?  Gacky ugly.
 my $mbar; 
 my $hn;

 
 # @@@ @@@ hardcoded conversion factors...bad
 
 foreach my $rowr (@{$ar}) # each row of values as array ref
 { # later, we will add conversions to real units from a schema lookup that contains perl we can eval
 # say "uno_row: @{$rowr}";
  $mbar = 10**((1.667* @{$rowr}[3] *$acalfactor*2.013)-11.33) ; # pressure in mbar
  $hn =  @{$rowr}[7] * 600; # hornack cpm
  $hna = $hn * .1 + $hna * (1-.1);
  if ($hna < 1) {$hna = 0;} # lower limit for gnuplot

  if ($mungetimes)
  {
   push @ux,$faketimeu;
   $faketimeu += 0.1; # 100ms/sample assumed 
  } else
  { # use real reported time
    push @ux, @{$rowr}[0] * .001; # seconds for now
  }
  
#  push @ux, @{$rowr}[0] * .001; # econds for now
  push @ua0,@{$rowr}[1] * .016; # main volts (for 10kv factor is .0163)  net is kV
  push @ua1,@{$rowr}[2] * 0.0128865979381; # main ma
  push @ua2,$mbar;            # pressure
  push @ua3,@{$rowr}[4]  * 0.0119501691787; # ion volts
  push @ua4,@{$rowr}[5] *  0.00273618998224; # 0.00645327826536 ; # ion current
  push @ua5,@{$rowr}[6]; # nc for now
  push @uc0,$hn; # hornyak cpm
  push @uc0a,$hna;
  push @uc1,@{$rowr}[8] * 600; # geiger cps
 }
}
####################################################################
sub on_testb_clicked()
{
 getguivalues();
# to become a full refresh from 0 ms
 say "refresh clicked";
 $running = 0;
 sleep 1;  # wait to be noticed
 #cleararrays();  # start fresh
  on_SSbutton_clicked();
 
# getgi();
# getu();
}
####################################################################

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

sub poll()
{ # this will actually do the plotting
 say "poll routine hit:$pollcount";
 $pollcount++;
 getgi();  #update plot arrays with latest data
 getu();
 if ($pressure) { 
#    gnuplot_cmd($pplot,"clear");
    gnuplot_cmd($pplot,"plot '-' u 1:2 with lines title \"milliBar\" "); # and no doubt some other style options
    foreach $i (0..$#gx)  # assumes X axis' number of points total 
    { # most of the rest of this program was written to make this line this simple
     gnuplot_cmd ($pplot,"$gx[$i] $ga2[$i]"); # So, hard to change, eh?
    }
    gnuplot_cmd($pplot,'e'); # say we're done; take it away, gnuplot!
   } # if pressure
    if ($kvma)
    { # for voltage and current
     gnuplot_cmd($viplot,"plot '-' u 1:2 with lines title \"Main kV\", '-' u 1:2 with lines title \"Main mA\", '-' u 1:2 with lines title \"Ion kV cmd\", '-' u 1:2 with lines title \"Ion mA\", '-' u 1:2 with lines title \"Ion volts actual\""); # and no doubt some other style options
     foreach $i (0..$#ux)  # assumes X axis' number of points total 
     { # most of the rest of this program was written to make this line this simple
      gnuplot_cmd ($viplot, "$ux[$i] $ua0[$i]"); # So, hard to change, eh?
#      say "vidata: $ux[$i] $ua0[$i]";
     }
     gnuplot_cmd($viplot,'e'); # say we're done; take it away, gnuplot!
     
      foreach $i (0..$#ux)  # assumes X axis' number of points total 
      { # most of the rest of this program was written to make this line this simple
       gnuplot_cmd ($viplot, "$ux[$i] $ua1[$i]"); # So, hard to change, eh?
      }
      gnuplot_cmd($viplot ,'e'); # say we're done; take it away, gnuplot!
      #ion volts command
      foreach $i (0..$#gx)  # assumes X axis' number of points total 
      { # most of the rest of this program was written to make this line this simple
       gnuplot_cmd ($viplot, "$gx[$i] $gda[$i]"); # So, hard to change, eh?
      }
      gnuplot_cmd($viplot,'e'); # say we're done; take it away, gnuplot!

 # note below is a different set of time-series from a different arduino, and it works!
      foreach $i (0..$#gx)  # assumes X axis' number of points total 
      { # most of the rest of this program was written to make this line this simple
       gnuplot_cmd ($viplot, "$gx[$i] $ga3[$i]"); # So, hard to change, eh?
      }
      gnuplot_cmd($viplot,'e'); # say we're done; take it away, gnuplot!
      
      foreach $i (0..$#ux)  # assumes X axis' number of points total 
      { # most of the rest of this program was written to make this line this simple
       gnuplot_cmd ($viplot, "$ux[$i] $ua3[$i]"); # actual ion volts
      }
      gnuplot_cmd($viplot,'e'); # say we're done; take it away, gnuplot!
      
      
    } # if kvma
    if ($counters) {
     gnuplot_cmd($cplot,"plot '-' u 1:2 with points title \"Geiger\", '-' u 1:2 with points title \"Hornyak\", '-' u 1:2 with lines title \"C3\", '-' u 1:2 with lines lt 4 title \"Navg\""); # and no doubt some other style options
     foreach $i (0..$#ux)  # assumes X axis' number of points total 
     { # most of the rest of this program was written to make this line this simple
      gnuplot_cmd ($cplot, "$ux[$i] $uc1[$i]"); # So, hard to change, eh?
     }
     gnuplot_cmd($cplot,'e'); # say we're done; take it away, gnuplot!
     
      foreach $i (0..$#ux)  # assumes X axis' number of points total 
      { # most of the rest of this program was written to make this line this simple
       gnuplot_cmd ($cplot, "$ux[$i] $uc0[$i]"); # So, hard to change, eh?
      }
      gnuplot_cmd($cplot,'e'); # say we're done; take it away, gnuplot!
      
      foreach $i (0..$#gx)  # assumes X axis' number of points total 
      { # most of the rest of this program was written to make this line this simple
       gnuplot_cmd ($cplot, "$gx[$i] $gc[$i]"); # So, hard to change, eh?
      }
      gnuplot_cmd($cplot,'e'); # say we're done; take it away, gnuplot!
     
     # add uc0a with lines here
      foreach $i (0..$#ux)  # assumes X axis' number of points total 
      { # most of the rest of this program was written to make this line this simple
       gnuplot_cmd ($cplot, "$ux[$i] $uc0a[$i]"); # So, hard to change, eh?
      }
      gnuplot_cmd($cplot,'e'); # say we're done; take it away, gnuplot!
     
     
     } # if counters
     
 
 unless ($continuous) 
  {
   $running = 0; # just do one time and stop for this case
   $SSbuttonr->set_label("Start"); # show our real status
  }
 return $running; # keep going if running, stop is controlled elsewhere
}
####################################################################
sub initialize() # start the ball rolling
{
 createguifile(); # create gui from glade file in same dir as program
 getwidgetrefs(); # get references to the gui elements
# connect to database, prepare statement handles to execute later on
 $dbh = DBI->connect($DSN,$DBUser,$DBPass) or die "couldn't connect to $DSN:$!\n"; 
# no point staying up if we can't do this
 initwidgets(); # some need a database connection for info
}
####################################################################
 sub on_delete_run_clicked { # this is going to be "interesting"
   if ($running) {
    say "can't do this while running";
   } else { # do the delete stuff
    my $runno = $runnumr->get_active_text(); # find out what run we selected
    my $stmt; #sql statement to do
    my @blobpaths; #
    my $path;

 
    if ($runno) {
      $stmt = "DELETE from uno WHERE runno=$runno;";
      $dbh->do($stmt) or say "err doing $stmt: $dbh->errstr"; # tested
      $stmt = "DELETE from servo WHERE runno=$runno;";
      $dbh->do($stmt) or say "err doing $stmt: $dbh->errstr"; # tested
      $stmt = "DELETE from fusectl WHERE runno=$runno;";
      $dbh->do($stmt) or say "err doing $stmt: $dbh->errstr"; # tested

# @@@ add any other numeric data aq remove here (this was all of them for this rev of the code 5/11/16
# find any blobs for this runno and delete the files, then the row
       $stmt = "SELECT audio, gridcam, tricam, fusorpi FROM blobs WHERE runno=$runno;";
       @blobpaths = $dbh->selectrow_array($stmt) or say "statement:$stmt error: $dbh->errstr"; # fill arry with paths in order shown above
       foreach $path (@blobpaths) {
       if ($path) { #some might be blank
       unlink $path or say "couldn't unlink $path:$!";
      }  #if path
    } # for paths
    $stmt = "DELETE FROM blobs WHERE runno=$runno;"; # no need for this to stay and point to paths that aren't there anymore
    $dbh->do($stmt) or say "couldn't do $stmt:$dbh->errstr";

# then, finally, delete runs row of this run
    $stmt = "DELETE FROM runs WHERE runno=$runno;";
    $dbh->do($stmt) or say "couldn't do $stmt:$dbh->errstr";
     say "Run $runno deleted.";
    } 
 # have to clean out run list too
     fillrunbox(); # refresh run box
   } # end else running (delete)
  }    
####################################################################
############################ Main ##################################
####################################################################

initialize();
say "Compiled OK, smart guy."; # jerk way to say we made it.
 Gtk3->main; # GUI event Floop - when we quit, we fall out and exit
say "We're outa here.";



