package Simulation::Automate::PostProcLib;

use vars qw( $VERSION );
$VERSION = "1.0.0";

################################################################################
#                                                                              #
#  Copyright (C) 2000,2002 Wim Vanderbauwhede. All rights reserved.            #
#  This program is free software; you can redistribute it and/or modify it     #
#  under the same terms as Perl itself.                                        #
#                                                                              #
################################################################################

=headers

Module to support synsim script for simulation automation.
This module contains a set of utility functions for use in the
PostProcessors.pm module.
This module is generic.

$Id: PostProcLib.pm,v 1.2 2003/09/04 09:54:19 wim Exp $

=cut
use warnings;
use strict;
use Carp;
#use FileHandle;
use Exporter;
use lib '.','..';

use Simulation::Automate::Analysis;
use Simulation::Automate::Dictionary;
#use Simulation::Automate::PostProcessors;
@Simulation::Automate::PostProcLib::ISA = qw(Exporter);
@Simulation::Automate::PostProcLib::EXPORT = qw(
						&prepare_plot
						&prepare
						&gnuplot
						&gnuplot_combined
						%simdata
						$last
						$verylast
						$every_sweep_val
						$current_set_str
						$current_set_except_setvar_str
						$results_file_name
						@all_results_file_names
						%current_set_vals
						@results
						&import_symbols
						$verbose
					       );

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

sub AUTOLOAD {
my $subref=$Simulation::Automate::PostProcLib::AUTOLOAD;
$subref=~s/.*:://;
print STDERR "
There is no script for this analysis in the PostProcLib.pm module.
This might not be what you intended.
You can add your own subroutine $subref to the PostProcLib.pm module.
";

}

#------------------------------------------------------------------------------
sub prepare {
#print "Entering prepare()\n";
  use Cwd;

  #*&Simulation::Automate::PostProcessors::$subref(\$dataset,\$i,\$dataref1,\$flagsref,\$returnvalue,\$preprocref);
  #&prepare_plot(@args);
#  print "PREPARE\n";
  my $datafile_name=shift;
  my $count=shift;
  my $dataref=shift;
  my $flagsref=shift;
  my $verylastref=shift;
  my @extra_args=@_;

  my $preprocref=((@extra_args>=1) && $extra_args[0])?$extra_args[0]:0;
  
  my $every_sweep_val=((@extra_args==2) && ($extra_args[1]==2))?1:0;
  my $verylast=1;
  my @results=();

  if($verylastref && $verylastref!=1){
    @results=@{$verylastref};
     $verylast=0;
  } 

  (my $batch, $Simulation::Automate::PostProcLib::interactive,my $nosims,$Simulation::Automate::PostProcLib::plot,$Simulation::Automate::PostProcLib::verbose)=@{$flagsref};
  my $copy_results=1;
  (my $nsims, my $simdataref,my $current_set_valsref,my $lastref)=@{$dataref};
  
  my %synsimdata=%{$simdataref};
  my %current_set_vals=%{$current_set_valsref};
#to be exported: current_set_str
  my $current_set_str='';
  foreach my $key (sort keys %current_set_vals) {
    $current_set_str.="${key}-".$current_set_vals{$key}.'-';
  }
  $current_set_str=~s/-$//;

#The idea is to use XVAR or SWEEPVAR for any X-value sweep. As there is no longer a requirement to have semicol-lists for grouping, if XVAR is not semicol, make it semicol.
#If there was a semicol-list, make it comma. 
#If there were 2 or more semicol lists, add them to %grouped
#CONDVAR must only be defined if a CONDITION is present. In this case, CONDVAR is swept, XVAR is stepped, so XVAR should be a comma-separated list. If not, make it one. Again, if semicol grouping is used, convert to new grouping
#So, internally:
#(COND or CONDITION) {
#XVAR=SETVAR
#SWEEPVAR=CONDVAR
#} else {
#XVAR=SWEEPVAR
#}


  my $setvar=$synsimdata{SETVAR}||'';
  if(exists $synsimdata{COND} or exists $synsimdata{CONDITION}) {
    my $condvar= $synsimdata{CONDVAR}||$synsimdata{SWEEPVAR}||'none';
    $synsimdata{CONDVAR}=$synsimdata{SWEEPVAR}=$condvar;
    my $xvar= $synsimdata{XVAR}|| $synsimdata{SETVAR}||'';
    $synsimdata{XVAR}=$synsimdata{SETVAR}=$xvar;
    $setvar=$xvar;
  } else {
    my $xvar= $synsimdata{XVAR}|| $synsimdata{SWEEPVAR}||'';
    $synsimdata{XVAR}=$synsimdata{SWEEPVAR}=$xvar;
  }

  my $cond=$synsimdata{COND}||$synsimdata{CONDITION}||'<1';
  $synsimdata{COND}=$synsimdata{CONDITION}=$cond;

  my $setvarval=$synsimdata{$setvar}->[0]; # if SETVAR is defined, this would be the first value in the list for SETVAR. This is to used check if the last element in the SETVAR value list has been reached
my $current_set_except_setvar_str=$current_set_str;
my $setvar_str=($setvar ne '')?$setvar.'-'.$setvarval:'';
$current_set_except_setvar_str=~s/$setvar_str\-*//;

  my %last=%{$lastref}; # the last value in the list for every variable

  my $last=($setvar && (exists $last{$setvar}) && ($setvar ne '') && $setvarval && ($setvarval==$last{$setvar})); # SETVAR is defined and the element in the value list has been reached. 

  $synsimdata{OUTPUT_FILTER_PATTERN}||= '.*';

  my $plotext=$synsimdata{PLOTTEMPL}||$synsimdata{PLOTTEMPLATE}||$synsimdata{PLOT_TEMPLATE}||'.gnuplot';
  $synsimdata{PLOTTEMPL}=$synsimdata{PLOTTEMPLATE}=$synsimdata{PLOT_TEMPLATE}=$plotext;

  $synsimdata{PLOTCOMMAND}||='/usr/bin/ggv';
  if((not -e '/usr/bin/ggv') and ( -e '/usr/X11R6/bin/gv')) {
  $synsimdata{PLOTCOMMAND}||='/usr/X11R6/bin/gv';
  }
  my $normvar=$synsimdata{NORMVAR}||1;
  (!$synsimdata{$normvar} || !@{$synsimdata{$normvar}})&&(${$synsimdata{$normvar}}[0]=1);
  my $current_norm_val=(@{$synsimdata{$normvar}}>1)?$current_set_vals{$normvar}:${$synsimdata{$normvar}}[0];
  $synsimdata{NORMVAR}=$normvar;

(exists $synsimdata{XVAR}) && ($synsimdata{XCOL}||=1);
  my $datacol=$synsimdata{DATACOL}||$synsimdata{YCOL}||1;
  $synsimdata{DATACOL}=$synsimdata{YCOL}=$datacol;

  my $simtempl=$synsimdata{SIMULATION}||$synsimdata{SIMNAME}||$synsimdata{SIMTYPE}||$synsimdata{TEMPLATE}||$synsimdata{SIMTEMPL};
  $synsimdata{SIMULATION}=$synsimdata{SIMNAME}=$synsimdata{SIMTYPE}=$synsimdata{TEMPLATE}=$synsimdata{SIMTEMPL}=$simtempl;

  my $anatempl=$synsimdata{ANALYSIS_TEMPLATE}||$synsimdata{ANALYSIS}||$synsimdata{ANATEMPL}||'None';
  $synsimdata{ANALYSIS_TEMPLATE}=$synsimdata{ANALYSIS}=$synsimdata{ANATEMPL}=$anatempl;

  my $results_file_name=$simtempl.'-'.$anatempl.'-'.$current_set_str.'.res';

  #NEW 24/11/2003 Copy "old" results files to new names
  if(not $verylast){
    system("cp ${simtempl}_C${count}.res $results_file_name");
}
  my $devtype=$synsimdata{DEVTYPE}|| $synsimdata{DEVICE}||'';
  $synsimdata{DEVTYPE}=$synsimdata{DEVICE}=$devtype;
  $synsimdata{TITLE}||="$devtype $simtempl simulation";
  my $simtitle = my $title = $synsimdata{TITLE};
  foreach my $key (keys %synsimdata) {
    ($key!~/^_/) && next;
    ($simtitle=~/$key/) && do {
      my $val=$synsimdata{$key};
      my $nicekey=$make_nice{$key}{title}||&make_nice($key);
      my $niceval=$make_nice{$key}{${$val}[0]}||join(',',@{$val});
      $simtitle=~s/$key/$nicekey:\ $niceval/;
    };
    $title=$simtitle;
  }

  # For Gnuplot
  #XSTART, XSTOP, YSTART, YSTOP, XTICS, YTICS, YLABEL, XLABEL, LOGSCALE, STYLE,
  $synsimdata{XSTART}||="";
  $synsimdata{XSTOP}||="";
  $synsimdata{YSTART}||="";
  $synsimdata{YSTOP}||="";
  $synsimdata{XTICS}||="";
  $synsimdata{YTICS}||="";
  $synsimdata{YLABEL}||="$title";
  $synsimdata{XLABEL}||=&make_nice($synsimdata{XVAR});#"$title";
  $synsimdata{LOGSCALE}=($synsimdata{LOGSCALE})?"set nologscale xy\nset logscale ".lc($synsimdata{LOGSCALE}):'set nologscale xy';
  my $plotstyle=$synsimdata{PLOTSTYLE}||$synsimdata{STYLE}||'linespoints';
  $synsimdata{PLOTSTYLE}=$synsimdata{STYLE}=$plotstyle;
  $synsimdata{DATAFILENAME}=$datafile_name;
  (my $legendtitle, my $legend)=@{&create_legend($current_set_str,\%make_nice)};
  my @all_results_file_names=glob($simtempl.'-'.$anatempl.'*.res');

  @Simulation::Automate::PostProcLib::all_results_file_names=@all_results_file_names;
  @Simulation::Automate::PostProcLib::results=@results;
  %Simulation::Automate::PostProcLib::simdata=%synsimdata;
  %Simulation::Automate::PostProcLib::current_set_vals=%current_set_vals;
  $Simulation::Automate::PostProcLib::results_file_name=$results_file_name;
  $Simulation::Automate::PostProcLib::current_set_str=$current_set_str;
  $Simulation::Automate::PostProcLib::current_set_except_setvar_str=$current_set_except_setvar_str;
  $Simulation::Automate::PostProcLib::verylast=$verylast;
  $Simulation::Automate::PostProcLib::last=$last;
  $Simulation::Automate::PostProcLib::simtempl=$simtempl;
  $Simulation::Automate::PostProcLib::anatempl=$anatempl;
  $Simulation::Automate::PostProcLib::plotext=$plotext;
#  if ($preprocref!=0) {
#    eval('use Simulation::Automate::PostProcessors;');
#    &{$preprocref}();
#  }
#%Simulation::Automate::PostProcessors::simdata=%synsimdata;
#@Simulation::Automate::PostProcessors::results=@results;

&import_symbols();
my $postprocpath= $INC{"Simulation/Automate/PostProcLib.pm"};
$postprocpath=~s/Lib/essors/;
($postprocpath=~/^\./)&&($postprocpath='../'.$postprocpath);
require $postprocpath;

my $workingdir=cwd();
if( -d "../PLUGINS") {
  my @preprocs=glob("../PLUGINS/*.pm");
  foreach my $plugin (@preprocs) {
    if($synsimdata{PREPROCESSOR} && ($plugin=~/$synsimdata{PREPROCESSOR}/) or $synsimdata{POSTPROCESSOR} && ($plugin=~/$synsimdata{POSTPROCESSOR}/)) {
      require $plugin;
    }
  }
}
if(exists $synsimdata{PREPROCESSOR}) {
#print "Calling preprocessor $synsimdata{PREPROCESSOR} from prepare\n";
#@_= @Simulation::Automate::PostProcLib::results;
 eval('&Simulation::Automate::PostProcessors::'.$synsimdata{PREPROCESSOR});#.'(@_)');
# @Simulation::Automate::PostProcLib::results=@_;
}

#print "Calling Simulation::Automate::PostProcessors::$synsimdata{ANATEMPL} from prepare\n";
if( $synsimdata{ANATEMPL} ne 'None' ) {
eval('&Simulation::Automate::PostProcessors::'.$synsimdata{ANATEMPL});
}

#print "Leaving prepare()\n";
  return [@_];
} # END of prepare()

#------------------------------------------------------------------------------
sub gnuplot {
my $commands=shift;
my $persist=shift||'';
if($Simulation::Automate::PostProcLib::plot) {
open GNUPLOT,"| gnuplot $persist";
print GNUPLOT $commands;
close GNUPLOT;
}
} # END of gnuplot()
#------------------------------------------------------------------------------
sub gnuplot_combined {
#my $firstplotline=shift||'';
#my $plotlinetempl=shift||'';
#my $col=$Simulation::Automate::PostProcLib::datacol;
#my $ycol=$Simulation::Automate::PostProcLib::ycol;
#if($firstplotline=~/^\d+$/) {
#$ycol=$firstplotline;
#$firstplotline='';
#}
my $firstplotline='';
my $plotlinetempl='';
#my $synsimdataref=shift;
#my %synsimdata=%Simulation::Automate::PostProcLib::synsimdata;
my %synsimdata=%Simulation::Automate::PostProcLib::simdata;
$synsimdata{YCOL}= $Simulation::Automate::PostProcLib::ycol;

### On the very last run, collect the results 
#1. get a list of all plot files
my @plotfiles=glob("${Simulation::Automate::PostProcLib::simtempl}-${Simulation::Automate::PostProcLib::anatempl}-*.res");

#2. create a gnuplot script 
#this should be a full script, but with room for additional feature
my @lines=();
my $legendtitle='';
my $lt=0;
my $range='';
foreach my $filename (@plotfiles) {
$lt++;
my $title=$filename;

$title=~s/${Simulation::Automate::PostProcLib::simtempl}-${Simulation::Automate::PostProcLib::anatempl}-//;
$title=~s/\.res//;
$title=~s/\-\-/\-\_MINUS_/g;
#(my $legendkey,my $legendvalue)=split('-',$title);
my %legend=split('-',$title);
my @legendkeys=();
my @legendvalues=();
foreach  my $varname (sort keys %legend){
push @legendkeys,$varname;
push @legendvalues,$legend{$varname};
}
my $legendkey='';
$legendkey.=join(', ',@legendkeys);
my $legendvalue='';
$legendvalue.=join(', ', @legendvalues);

#$legendkey||=' ';
($legendkey eq '') && ( $legendkey=' ');
($legendvalue eq '') && ($legendvalue=' ');
#($legendvalue=~/^\d+$/)&&($legendvalue!=0) && ($legendvalue||=' ');
($legendkey=~/_MINUS_/)&&($legendkey=~s/_MINUS_/\-/g);
($legendvalue=~/_MINUS_/)&&($legendvalue=~s/_MINUS_/\-/g);

my %title= ($legendkey=>$legendvalue);

my $legend='';
$legendtitle='';
foreach my $key (sort keys %title) {
$legendtitle.=',';
$legendtitle.=$make_nice{$key}{title}||&make_nice($key);
$legend.=$make_nice{$key}{$title{$key}}||&make_nice($title{$key});
$legend.=',';
}
$legend=~s/,$//;
$legendtitle=~s/^,//;
$synsimdata{LEGEND}=$legend;
$synsimdata{LEGENDTITLE}=$legendtitle;

if(($firstplotline eq '') and ($plotlinetempl eq '')){
($firstplotline, $plotlinetempl,$range)=@{&parse_gnuplot_templ(\%synsimdata)};
}

my $plotline;
#print "PLOTLINE:", '$plotline='.$plotlinetempl;
eval('$plotline='.$plotlinetempl);
#carp "PLOTLINE:$plotline";
push @lines, $plotline
}
$firstplotline=~s/set\s+key\s+title.*/set key title "$legendtitle"/;
my $plot="\nplot $range ";
if($firstplotline=~/$plot/ms){$plot=''};
my $line=$firstplotline.$plot.join(",\\\n",@lines);

if($Simulation::Automate::PostProcLib::plot) {
open GNUPLOT,"| gnuplot";
print GNUPLOT $line;
close GNUPLOT;
}
open GNUPLOT,">${Simulation::Automate::PostProcLib::simtempl}-${Simulation::Automate::PostProcLib::anatempl}.gnuplot";
print GNUPLOT $line;
close GNUPLOT;

if($Simulation::Automate::PostProcLib::interactive) {
system("${Simulation::Automate::PostProcLib::plotcommand} ${Simulation::Automate::PostProcLib::simtempl}-${Simulation::Automate::PostProcLib::anatempl}.ps &");
}
} # END of gnuplot_combined()
#------------------------------------------------------------------------------
sub parse_gnuplot_templ {
my $synsimdataref=shift;
my %synsimdata=%{$synsimdataref};

  no strict;

  foreach my $key (keys %synsimdata){
    ($key=~/^\d*$/)&&next;
    my $lcname=lc($key);
    if($key=~/^_/) {
      @{$lcname}=@{$synsimdata{$key}};
    } else {
      $$lcname=$synsimdata{$key};
    }
  }
my $normvarval=$synsimdata{$normvar}[0];

#plot templates can be either 
#SIMPTYPE.PLOTTEMPL, stored under TEMPLATES or TEMPLATES/SYMTYPE
#$dataset.PLOTTEMPL, stored next to $dataset.data
my $plot_templ_file="../$datafilename$plottempl"; 
#print "../$datafilename$plottempl\n"; 
if( not -e $plot_templ_file) {
  $plot_templ_file="../TEMPLATES/$simtempl$plottempl";
  if( not -e $plot_templ_file) {
    $plot_templ_file='';
  }
}
#print " plot_templ_file $plot_templ_file\n";

my $xcolentry='(\$_XCOL*1):';

if(not exists $synsimdata{XCOL} and not exists $synsimdata{XVAR} and not exists $synsimdata{SWEEPVAR} and ($xcol && ($xcol!~/\d/))){
$xcolentry='';
}
my $firstplotline=<<"ENDH";
set terminal postscript landscape enhanced  color solid "Helvetica" 14
set output "${simtempl}-${anatempl}.ps"

$logscale

#set xtics $xtics
#set mxtics 2
set grid xtics ytics mxtics mytics

set key right top box 
set key title "$legendtitle" 

set title "$title" "Helvetica,18"
set xlabel "$xlabel" "Helvetica,16"
set ylabel "$ylabel" "Helvetica,16"

ENDH

my $plotlinetempl=q["\'$filename\' using _XCOLENTRY(\$_YCOL/_NORMVAR) title \"$legend\" with _PLOTSTYLE"];
$plotlinetempl=~s/_XCOLENTRY/$xcolentry/;
$plotlinetempl=~s/_NORMVAR/$normvarval/;
$plotlinetempl=~s/_YCOL/$ycol/;
$plotlinetempl=~s/_XCOL/$xcol/;
$plotlinetempl=~s/_PLOTSTYLE/$plotstyle/;
my $range='';
  if($plot_templ_file) {
    $firstplotline='';
    open(PLOTTEMPL,"<$plot_templ_file");

    while (<PLOTTEMPL>) {
      /FILENAME/ && ($plotlinetempl=$_) && last;
      s/OUTPUT/${simtempl}-${anatempl}/;
      s/PLOTTITLE/$title/;
      s/LEGENDTITLE/$legendtitle/;
      s/XLABEL/$xlabel/;
      s/YLABEL/$ylabel/;
      s/XTICS/$xtics/;
      s/YTICS/$ytics/;
      s/LOGSCALE/$logscale/;
      $firstplotline.=$_;
    }
    chomp($plotlinetempl);
    $plotlinetempl=~s/\$/\\\$/g;
    $plotlinetempl=~s/RESULTSFILENAME/\$filename/g;
    $plotlinetempl=~s/FILENAME/\$filename/g;
    $plotlinetempl=~s/XCOL/$xcol/;
    $plotlinetempl=~s/YCOL/$ycol/;
    $plotlinetempl=~s/NORMVAR/$normvarval/;
    $plotlinetempl=~s/LEGENDENTRY/\$legend/;
    $plotlinetempl=~s/LEGEND/\$legend/;
    $plotlinetempl=~s/PLOTSTYLE/$plotstyle/;
    $plotlinetempl=~s/^plot\s+//;
    $plotlinetempl=~s/\'/\\\'/g;
    $plotlinetempl=~s/\"/\\\"/g;
    $plotlinetempl=~s/XSTART/$xstart/;
    $plotlinetempl=~s/XSTOP/$xstop/;
    $plotlinetempl=~s/YSTART/$ystart/;
    $plotlinetempl=~s/YSTOP/$ystop/;


( $plotlinetempl=~s/(\[[\d\.eE\-\+]*\:[\d\.eE\-\+]*\]\s+)//) && do {
$range=$1;
};
    $plotlinetempl='"'.$plotlinetempl.'"';
  }
$range||='';

  return [$firstplotline,$plotlinetempl,$range];

} # END of parse_gnuplot_templ()
#------------------------------------------------------------------------------
sub copy_results {
use Cwd;
my $workingdir=cwd();
  if(not(-e "$workingdir/../../Results")) {
mkdir  "$workingdir/../../Results";
}
  if(not(-e "$workingdir/../../Results/$Simulation::Automate::PostProcLib::simtempl")) {
mkdir  "$workingdir/../../Results/$Simulation::Automate::PostProcLib::simtempl";
}

  if(not(-e "$workingdir/../../Results/$Simulation::Automate::PostProcLib::simtempl/$Simulation::Automate::PostProcLib::anatempl")) {
mkdir  "$workingdir/../../Results/$Simulation::Automate::PostProcLib::simtempl/$Simulation::Automate::PostProcLib::dataset";
}
system("cp ${Simulation::Automate::PostProcLib::simtempl}-${Simulation::Automate::PostProcLib::anatempl}.* $workingdir/../../Results/$Simulation::Automate::PostProcLib::simtempl/$Simulation::Automate::PostProcLib::dataset");

} #END of copy_results()
#------------------------------------------------------------------------------
sub create_legend {
my $title=shift;

my %make_nice=%{shift(@_)};

$title=~s/\-\-/\-\_MINUS_/g;
(my $legendkey,my $legendvalue)=split('-',$title);
$legendkey||=' ';
$legendvalue||=' ';
($legendkey=~/_MINUS_/)&&($legendkey=~s/_MINUS_/\-/g);
($legendvalue=~/_MINUS_/)&&($legendvalue=~s/_MINUS_/\-/g);

my %title= ($legendkey=>$legendvalue);

my $legend='';
my $legendtitle='';
foreach my $key (sort keys %title) {
my $titlepart=$make_nice{$key}{title}||&make_nice($key);
$legendtitle.=','.$titlepart;
my $legendpart=$make_nice{$key}{$title{$key}}||$title{$key};
$legend.=','.$legendpart;
}
$legend=~s/^,//;
$legendtitle=~s/^,//;

return [$legendtitle,$legend];
}
#------------------------------------------------------------------------------
sub import_symbols { 
  no strict;
  foreach my $name (sort keys %Simulation::Automate::PostProcLib::simdata) {
    ($name=~/^\d*$/) && next;
    my $lcname=lc($name);
    if($name=~/^_/) {
      @{"Simulation::Automate::PostProcLib::$lcname"}=@{$Simulation::Automate::PostProcLib::simdata{$name}};
      @{"Simulation::Automate::PostProcessors::$lcname"}=@{$Simulation::Automate::PostProcLib::simdata{$name}};
      push @Simulation::Automate::PostProcLib::EXPORT,'@'.$lcname;
    } else {
      ${"Simulation::Automate::PostProcLib::$lcname"}=$Simulation::Automate::PostProcLib::simdata{$name};
      ${"Simulation::Automate::PostProcessors::$lcname"}=$Simulation::Automate::PostProcLib::simdata{$name};
      push @Simulation::Automate::PostProcLib::EXPORT,'$'.$lcname;
    }
  }
}
#------------------------------------------------------------------------------
sub make_nice {
my $varname=shift;
if($varname=~/,/){
  my @varnames=split(', ',$varname);
  foreach my $varname (@varnames) {
    $varname=~s/^_//;
    $varname=~s/_/ /g;
    $varname=lc($varname);
    $varname=~s/^([a-z])/uc($1)/e;
  }
  $varname=join(', ',@varnames);
} else {
$varname=~s/^_//;
$varname=~s/_/ /g;
$varname=lc($varname);
$varname=~s/^([a-z])/uc($1)/e;
}
return $varname;
}
#------------------------------------------------------------------------------
1;
#print STDERR "#" x 80,"\n#\t\t\tSynSim simulation automation tool\n#\n#\t\t\t(C) Wim Vanderbauwhede 2002\n#\n","#" x 80,"\n\n Module SynSim::PostProcLib loaded\n\n";


