#!/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
#
# -X option substitutes the .ccft "theory" file (which must exist) to run.
#
# In interactive mode the component is graphed.
#
# In any case, the .ccft file is produced
#
# -O (oh) option runs code (usually produced by SmeasS) from information 
#  in the system.pscf file.  The parameters are taken from the .ccf file
#  of the first component in the system, and two forms of output file for
#  plotting/analysis are created:  *.datt  in which all component states
#  are included, and *.dat for plotting.  In output.datt a typical line is:
#    input state1 state2 ... stateN output
#  In output.dat, the state N-tuple is reduced to
#  a single value of sqrt((state1**2 + ... + stateN**2)/N).
#
# 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)
$Ooption = 0; # get params from system.pscf, etc.

$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 eq "-O") { $Ooption = 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;
    while($linearr = <COMP>) {
      #read line from .ccf file
      if ($linearr eq "\n") {
        $line_count = 0;
        $array = \@state_subs;
      }
      else {
        chomp($array->[$line_count] = $linearr);
        $line_count++;
      }
    }
  close(COMP);
  foreach $subdomain (@input_subs) {
   $input_freq += (split(' ',$subdomain))[2];
  }
  foreach $subdomain (@state_subs) {
   $state_freq += (split(' ',$subdomain))[2];
  }

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

} elsif ($Ooption) { #get input parameters from first component for system
    $Xoption = 0; # O and X incompatable
    open (PSCF, "<system.pscf") or die "Must have \"system.pscf\" file";
    $first = <PSCF>; #polish line
    @polish = split(" ",$first);
    $firstcomp = $polish[0];
    for ($cc=1; $cc<=$firstcomp; $cc++) { #skip down to the first polish component
      $first = <PSCF>;  #.ccf names
    } 
    close (PSCF);
#debug
#print "first component: $first";
    open (FCOMP,"<$first") or die "Something wrong with first component $first";
    $lineval = <FCOMP>; #skip executable name
    @input_subs = ();
    @state_subs = ();
    $input_freq = 0;
    $k = 0;
    PL:
    while ($lineval=<FCOMP>) {
      chomp($lineval);
      last PL if ($lineval eq ""); #end of input subdomains
      $input_subs[$k++] = $lineval;
      @trip = split(" ",$lineval);
      if ($k == 0) {
        $input_start = $trip[0];
      }
      $input_freq += $trip[2]; #count
    }
    $input_end = $trip[1];
    $state_freq = 0;
    $k = 0;
    while ($lineval=<FCOMP>) {
      chomp($lineval);
      $state_subs[$k++] = $lineval;
      @trip = split(" ",$lineval);
      if ($k == 0) {
        $state_start = $trip[0];
      }
      $state_freq += $trip[2]; #count
    }
    $state_end = $trip[1];
    close (FCOMP);
#debug
#foreach $d (@input_subs) {print "$d\n";}
  } 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 || $Ooption) { 
  $FirstTime = 1; #for real initialization
  &stepfunc::init(\@input_subs, \@state_subs); 
  }
else {
  $FirstTime = 0;
  }

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

# Loop through input subdomains
foreach $isub (@input_subs) {
#debug
print "$isub\n";

 $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

 if ($FirstTime) { #run once to get real initial state for .ccft file
 $compObj -> run(($input_start + $input_end)/2);
 $InitialState = $compObj->getState;
   $FirstTime = 0;
   }
 $input = $input_sampler->next();
 while ($input !~ /^\.$/) {

  # Loop through state subdomains
  foreach $ssub (@state_subs) { 
#debug
print "$isub X $ssub\n";
    $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");
open(OUTALL, ">output.datt");
open(RUNALL, ">runtime.datt");
open(STATEALL, ">state.datt");
for ($i = 0; $i < $count; $i++) {
   print OUTALL $inputs[$i]." ".$in_state[$i]." ".$outputs[$i]."\n";
   print RUNALL $inputs[$i]." ".$in_state[$i]." ".$runtimes[$i]."\n";
   print STATEALL $inputs[$i]." ".$in_state[$i]." ".$out_state[$i]."\n";
 @composin = split(" ",$in_state[$i]);
 if (scalar(@composin) > 1) { #fix plot files
   @composout = split(" ",$out_state[$i]);
   $inct = 0; $outct = 0;
   for ($ss=0;$ss<scalar(@composin);$ss++) {
     if ($composin[$ss] =~ /\d/) {
       $combinstate += $composin[$ss]**2;
       $inct++;
     }
     if ($composout[$ss] =~ /\d/) {
       $comboutstate += $composout[$ss]**2;
       $outct++;
     }
   }
   $in_state[$i] = sqrt($combinstate/$inct);  #destroy multiple values with vector magnitude
   $out_state[$i] = sqrt($comboutstate/$outct);
 }
 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);
close(STATEALL);
close(RUNALL);
close(OUTALL);
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");
}
