#!/usr/bin/perl -w
#
# comp script used to process one component with:
#  comp xxx.ccf
# called from COMPS for each one.
#
# Not to be used with stateless components, or with "system" components
#   (yet -- maybe later)
#
# -X option substitutes the .ccft "theory" file (which must exist) to run.
#
# In interactive mode the component is graphed.
#
# A .ccft file is usually produced
#
# Functionality of the form:
#  comp xxx.bin [parameters]
# runs the binary, asking for parameters, produces no .ccft file

use component;
use samplings;
use status;
use stepfunc;

$Voption = 1; #indicates verbose mode, -V means off
$Ioption = 1; #interactive mode, -I means off
$Xoption = 0; # "execute" a theory, .ccft file (but still start with .ccf param)

$ccfProcessed = 0; # indicates that subdomain data has been read from a ccf file

# Process command-line arguments

ARGS:
$component = shift || die "No component executible or ccf file specified";
if ($component eq "-V") { $Voption = 0; goto ARGS; }
if ($component eq "-I") { $Ioption = 0; goto ARGS; }
if ($component eq "-X") { $Xoption = 1; goto ARGS; }

if ($component =~ /\.ccf$/i) { # read input range and state range from ccf file
  $input_freq = 0;
  $state_freq = 0;
  $ccf_component = $component;  #ccf name, before changed to executable
  open(COMP, $component) || die "Invalid ccf file specified";
    #first line of file points to real executible
    chomp($component = <COMP>);
    unless (substr($component,0,1) eq '/') { $component = "./".$component; }
    $array = \@input_subs;
    $line_count = 0;
    $in_sub_cnt = 0;
    while($linearr = <COMP>) {
      #read line from .ccf file
      if ($linearr eq "\n") {
        $in_sub_cnt = $line_count;
        $line_count = 0;
        $array = \@state_subs;
      }
      else {
        chomp($array->[$line_count] = $linearr);
        $line_count++;
      }
    }
  close(COMP);
  $j = 0;
  foreach $subdomain (@input_subs) {
   if ($j == 0) {
     $input_start = (split(' ',$subdomain))[0];
   }
   $input_freq += (split(' ',$subdomain))[2];
   if (++$j == scalar(@input_subs)) {
     $input_end = (split(' ',$subdomain))[1];
   }
  }
  foreach $subdomain (@state_subs) {
   $state_freq += (split(' ',$subdomain))[2];
  }

  $ccfProcessed = 1;
  if ($Xoption) {
    die "No 'theory' file to 'execute'" unless (-e $ccf_component."t");
  } #should do better, check relation between .ccf and .ccft

} else {
  $Xoption = 0; #must have .ccf to execute theory
  $input_start = shift;
  $input_end = shift;
  $state_start = shift;
  $state_end = shift;
  $input_freq = shift;
  $state_freq = shift;

  unless (defined($input_start) && defined($input_end)) {
   print "Enter starting value for input range: "; chop($input_start = <STDIN>);
   print "Enter ending value for input range: "; chop($input_end = <STDIN>);
  }
  unless (defined($state_start) && defined($state_end)) {
   print "Enter starting value for state range: "; chop($state_start = <STDIN>);
   print "Enter ending value for state range: "; chop($state_end = <STDIN>);
  }
  unless (defined($input_freq) && defined($state_freq)) {
   print "Enter sampling frequency for input: "; chop($input_freq = <STDIN>);
   print "Enter sampling frequency for state: "; chop($state_freq = <STDIN>);
  }
  unless ($input_freq) { $input_freq = int(rand($input_end - $input_start)) + $input_start + 1; }
  unless ($state_freq) { $state_freq = int(rand($state_end - $state_start)) + $state_start + 1; }
  @input_subs = ( "$input_start $input_end $input_freq" );
  @state_subs = ( "$state_start $state_end $state_freq" );
}

die "Invalid component specified" unless (-x $component);

if ($Voption) { &status::init($input_freq * $state_freq, "Sampling component data ".$component); }

# Prepare input sampling
if ($Xoption) {
  $base = &component::baseName($ccf_component);
  $statefile = $base.".state";
  $errfile = $base.".errlog";
  if (-e $errfile) {unlink $errfile;} #remove any existing error log
  $compObj = component->new(component => "XqtS", comparg => $ccf_component,
    statefile => $statefile);
} else {
  $compObj = component->new(component => $component);
}
if ($ccfProcessed) { 
  &stepfunc::init(\@input_subs, \@state_subs); 
  $compObj -> run(($input_start + $input_end)/2);
  $InitialState = $compObj->getState;
}

$count = 0;
@inputs = ();
@in_state = ();
##
@outputs = ();
@runtimes = ();
@out_state = ();

# Loop through input subdomains
foreach $isub (@input_subs) {

 $input_start = (split(' ',$isub))[0];
 $input_end = (split(' ',$isub))[1];
 $input_freq = (split(' ',$isub))[2];

 $input_sampler = samplings->init($input_start, $input_end, $input_freq);

 # Loop through input

 $input = $input_sampler->next();
 while ($input !~ /^\.$/) {

  # Loop through state subdomains
  foreach $ssub (@state_subs) { 
   
    $state_start = (split(' ',$ssub))[0];
    $state_end = (split(' ',$ssub))[1];
    $state_freq = (split(' ',$ssub))[2];

    $state_sampler = samplings->init($state_start, $state_end, $state_freq);

    # Loop through state

    $state = $state_sampler->next();
    while ($state !~ /^\.$/) {

      # Record initial values
      $inputs[$count] = $input;
      $in_state[$count] = $state;

      # Run component
      $compObj->run($input, $state) || die "Cannot run component";
      $outputs[$count] = $compObj->getOutput;
      $runtimes[$count] = $compObj->getRuntime;
   
      # Get persistent state
      $out_state[$count] = $compObj->getState;

      # Process approximations
      if ($ccfProcessed) {
         unless ( &stepfunc::process($input, $in_state[$count], $outputs[$count], $runtimes[$count], $out_state[$count]) ) {
             if ($Voption) { &status::showmesg("Input ($input) / initial state ($in_state[$count]) outside subdomain boundaries!"); }
         }
      }

      $state = $state_sampler->next();
      $count++;
  
       if ($Voption) { &status::progress($count); }
    } # Next state

  } # Next state subdomain

  $input = $input_sampler->next();
 } # Next input

} # Next input subdomain

$compObj->NoState; #kill FIFO file now that it's done

# Create graphing files

open(OUTPUT, ">output.dat");
open(RUNTIME, ">runtime.dat");
open(STATE, ">state.dat");
for ($i = 0; $i < $count; $i++) {
 print OUTPUT $inputs[$i]." ".$in_state[$i]." ".$outputs[$i]."\n";
 print RUNTIME $inputs[$i]." ".$in_state[$i]." ".$runtimes[$i]."\n";
 print STATE $inputs[$i]." ".$in_state[$i]." ".$out_state[$i]."\n";
}
close(STATE);
close(RUNTIME);
close(OUTPUT);
if ($ccfProcessed) { &stepfunc::create_splot_files("out_step.dat", "run_step.dat", "state_step.dat"); }

# default options for graphing -- Add options to the
# beginning of hash, they go to end of @args.

%opts = ( 'Show 3D shading? [0-1]|[0-1]' => 0,
          'Display as mesh? [0-1]|[0-1]' => 0,
          'Hidden surface removal? [0-1]|[0-1]' => 1, 
	  'Show contour lines? [0-1]|[0-1]' => 0,
          'Label axes? [0-1]|[0-1]' => 1 );

if ($ccfProcessed) {
	$opts{'Show step approximations? [0-1]|[0-1]'} = 1;
	$opts{'Show subdomain boundaries? [0-1]|[0-1]'} = 0;
}

# save all plots to postscript files using default options
  @args = values %opts;
  unless ($ccfProcessed) { push @args, 0; unshift @args, 0; }
  if ($args[1]) {$args[1] = "Output";} &plot("output.dat", "out_step.dat", "output.ps", @args);
  if ($args[1]) {$args[1] = "Runtime";} &plot("runtime.dat", "run_step.dat", "runtime.ps", @args);
  if ($args[1]) {$args[1] = "Final State";} &plot("state.dat", "state_step.dat", "state.ps", @args);

  if ($ccfProcessed && !$Xoption) {
         &stepfunc::create_ccft_files($ccf_component."t", "theory C N $InitialState");} 
	 #create .ccft name and options

if ($Ioption) { #show interactive menu
print "Data collection complete!\n";
$plot = "";
$step = "";
MENU:
  print "\nInteractive mode menu\n";
  print "---------------------\n";
  print "1. Configure graphing options\n";
  print "2. Show graph of output vs. input x state\n";
  print "3. Show graph of input & initial state vs. runtime\n";
  print "4. Show graph of input & initial state vs. final state\n";
    print "5. Exit\n\n";
  print "Enter number: "; chop($i = <STDIN>);
  
  if ($i == 1) {
    %opts = &configopts(%opts);
    goto MENU;
  } elsif ($i == 2) {
    $plot = "output.dat";
    $step = "out_step.dat";
    $label = "Output";
  } elsif ($i == 3) {
    $plot = "runtime.dat";
    $step = "run_step.dat";
    $label = "Runtime";
  } elsif ($i == 4) {
    $plot = "state.dat";
    $step = "state_step.dat";
    $label = "Final State"
  } elsif ($i == 5) {
    exit;
  } else {print "Bad choice!\n"; goto MENU;}

  @args = values %opts;
  unless ($ccfProcessed) { push @args, 0; unshift @args, 0; }
  if ($args[2]) { $args[2] = $label; }
  &plot($plot, $step, "", @args);
  if ($ccfProcessed) { #error table to STDOUT
    &stepfunc::ErrorTable($label);
  }  
  goto MENU;
} 

# save all plots to postscript files using default options
  #@args = values %opts;
  #unless ($ccfProcessed) { push @args, 0; unshift @args, 0; }
  #if ($args[2]) {$args[2] = "Output";} &plot("output.dat", "out_step.dat", "output.ps", @args);
  #if ($args[2]) {$args[2] = "Runtime";} &plot("runtime.dat", "run_step.dat", "runtime.ps", @args);
  #if ($args[2]) {$args[2] = "Final State";} &plot("state.dat", "state_step.dat", "state.ps", @args);

# This subroutine takes a hash that describes the options
# and builds a menu based on that hash. When the user is finished
# editing the options through the menu, the subroutine returns the
# hash with the modified values.
######################################
# Argument : a hash of options in this format...
#  Key: display name|regular expression
#    Where display name is the name to display for the option and the regular expression is the
#    test for valid settings for this option.
#  Value: the option's setting
sub configopts {
  return unless (defined($_[0]));
  my (%mopts, $count, $key, $disp, $regex, @tmp, $in);
  %mopts = @_;
OMNU:
  print "\n\nGraphing options\n";
  print "----------------\n";
  $count = 1;
  foreach $key (keys %mopts) {
   ($disp, $regex) = split(/\|/, $key);
   print "$count. $disp  ".$mopts{$key}."\n";
   $tmp[$count] = $key;
   $count++;
  }
  print "\nEnter number to change, or press return to go back: "; chop($in = <STDIN>);
  if (($in =~ /^$/) || ($in < 1) || ($in > $count)) { return %mopts; }
  $count = $in;
  print "Enter new value: "; chop($in = <STDIN>);
  ($disp, $regex) = split(/\|/, $tmp[$count]);
  if ($in !~ /$regex/) { print "Invalid setting\n"; goto OMNU; }
  $mopts{$tmp[$count]} = $in;
  goto OMNU;
}

# $_[0] = file name of data
# $_[1] = file name of step data
# $_[2] = file name of ps file or display graph if ""
# $_[4] = show subdomain boundaries?
# $_[5] = show contour lines?
# $_[6] = z axis label or no labels if ""
# $_[7] = hidden surface removal?
# $_[8] = show mesh?
# $_[9] = 3d shading
# $_[10] = show step approximations?
sub plot {
 my ($data, $stepfile, $filename, $showsubs, $contour, $label, $hsr, $mesh, $shade, $steps) = @_;
 my @subd;
 die "Data file does not exist" unless (-e $data);
 open(GNUPLOT, ">temp");
  if ($mesh || $shade) {
    print GNUPLOT "set dgrid3d $state_freq, $input_freq, 3\n";
    print GNUPLOT "set style data lines\n";
    unless ($mesh) { print GNUPLOT "unset surface\n"; }
    if ($hsr && (! $shade)) {
      print GNUPLOT "set hidden3d\n";
    }
    if ($contour) {
      print GNUPLOT "set contour base\n";
    }
    if ($shade) {
      print GNUPLOT "set pm3d";
      print GNUPLOT " at s" if ($mesh);
      print GNUPLOT "\n";
    }
  }
  if ($label !~ /^0$/) {
    print GNUPLOT "set xlabel \"Input\"\n";
    print GNUPLOT "set ylabel \"Initial State\"\n";
    print GNUPLOT "set zlabel \"$label\"\n";
  }
  if ($showsubs) {
    foreach $subdomain (@input_subs) {
     @subd = split(' ',$subdomain);
     print GNUPLOT "set arrow from first $subd[0],graph 0,graph 0 to first $subd[0],graph 1,graph 0 lt 6 lw 2 nohead\n";
     print GNUPLOT "set arrow from first $subd[1],graph 0,graph 0 to first $subd[1],graph 1,graph 0 lt 6 lw 2 nohead\n";
    }
    foreach $subdomain (@state_subs) {
     @subd = split(' ',$subdomain);
     print GNUPLOT "set arrow from graph 0,first $subd[0],graph 0 to graph 1,first $subd[0],graph 0 lt 3 lw 2 nohead\n";
     print GNUPLOT "set arrow from graph 0,first $subd[1],graph 0 to graph 1,first $subd[1],graph 0 lt 3 lw 2 nohead\n";
    }
  }
  if ($filename) {
    print GNUPLOT "set terminal postscript color\n";
    print GNUPLOT "set output \"$filename\"\n";
  } else {
    print GNUPLOT "set terminal x11\n";
  }
  print GNUPLOT "splot \"$data\"";
  if ($steps) {
    print GNUPLOT ", \"$stepfile\" with lines lt 3\n";
  } else {
    print GNUPLOT "\n";
  }
  unless ($filename) { print GNUPLOT "pause -1 \"Press return to continue.\"\n"; }
 close(GNUPLOT);
 system("gnuplot temp");
}
