#
# This is the common code used in the XDM regression testing 
# 

package XdmCommon;
use strict;
use Scalar::Util qw(looks_like_number);
use XyceRegression::Tools;

#
# used to drive the overall control flow
#
sub verifyXDMtranslation
{
  my ($XYCE,$XYCE_VERIFY,$CIRFILE,$absTol,$relTol,$zeroTol,$verbose,$searchStringsPtr)=@_;

  my $Tools = XyceRegression::Tools->new();
  #$Tools->setDebug(1);

  # script will be used to convert .csd to .prn
  my $TRANSLATESCRIPT = $XYCE_VERIFY;
  my $TRANSLATESCRIPT =~ s/xyce_verify.pl/convertToPrn.py/;

  # hard code XDM locations for now
  #my $PSPICEXML="\$XDMFORMAT/pspice_9_1.xml";
  #my $XYCEXML="\$XDMFORMAT/xyce_6_3.xml";
  my $PSPICEXML="pspice_16_6";
  my $XYCEXML="xyce_6_4";

  # PSPICE netlist file name
  my $PSPICEFILE="$CIRFILE.pspice";

  if (defined($verbose)) { $Tools->setVerbose(1); }

  #Clean up from any previous runs
  `rm -f *.csd *.prn *.err *.out`;
  `rm -f ./translated/*.csd ./translated/*.prn ./translated/*.err ./translated/*.out`;

  # run known good Xyce .cir file, and check that it worked
  my $retval=system("$XYCE $CIRFILE > $CIRFILE.out 2> $CIRFILE.err");
  if ($retval != 0)
  {
    print STDERR "Xyce crashed trying to run gold file\n";
    print "Exit code = 14 \n";
    exit 14;
  }
  else
  {
    print "Gold Xyce netlist ran\n";
  }
  if (not -s "$CIRFILE.csd" ) 
  {
    print ".csd file missing for gold Xyce netlist\n"; 
    print "Exit code = 14\n"; 
    exit 14; 
  }

  # run xdm on the gold PSpice netlist
  #my $CMD="xdm.py $PSPICEFILE $PSPICEXML pspice ./translated $XYCEXML";
  #my $CMD="xdm.py $PSPICEFILE $PSPICEXML ./translated $XYCEXML > $CIRFILE.xdm.out";
  my $CMD="xdm_wrap $PSPICEFILE $PSPICEXML ./translated $XYCEXML > $CIRFILE.xdm.out";

  if (system($CMD) != 0)
  {
    print "XDM exited with errors, or failed to run\n";
    print "Exit code = 2\n";
    exit 2;
  }
  else
  {
    print "XDM translation succeeded\n";
  }

  # rename the translated file
  $CMD="mv ./translated/$PSPICEFILE ./translated/$CIRFILE";
  if (system($CMD) != 0)
  {
    print "Rename of translated file failed.\n";
    print "Exit code = 2\n";
    exit 2;
  }

  # run the translated netlist and check for errors.
  # need to change directories to ./translated so that the translated .lib and .NET files
  # are used.
  chdir "./translated";
  $retval=system("$XYCE $CIRFILE > $CIRFILE.out 2> $CIRFILE.err");
  if ($retval != 0)
  {
    print STDERR "Xyce crashed trying to run translated files\n";
    print "Exit code = 14 \n";
    exit 14;
  }
  else
  {
    print "Translated Xyce netlist ran\n";
  } 
  if (not -s "$CIRFILE.csd" ) 
  {
    print ".csd file for translated Xyce netlist missing\n"; 
    print "Exit code = 14\n"; 
    exit 14; 
  }

  # change back to the top-level directory for the comparison
  chdir "..";

  # now compare the output (in .csd format) from the
  # "gold" and translated netlists.
  $retval = XdmCommon::compareCSDfiles("$CIRFILE.csd","./translated/$CIRFILE.csd",$absTol,$relTol,$zeroTol);
  if ($retval != 0)
  { 
    print "comparison of gold and translated .csd files failed\n";
    print "Exit code = $retval\n";
    exit $retval;
  }
  else
  {
    print ".csd file comparison succeeded\n";
  }

  # now check for any xdm warning messages in the .stdout file
  if (defined $searchStringsPtr)
  {
    print "checking for xdm warning/error messages\n";
    my @searchstrings = @$searchStringsPtr;
    $retval = $Tools->checkError("$CIRFILE.xdm.out",@searchstrings);
    if ($retval != 0)
    {
      print "search for xdm warning/error messages failed\n";
      print "Exit code = $retval\n";
      exit $retval;
    }
    else
    {
      print "test for xdm warning/error messages passed\n";
    }
  } 

  print "Exit code = $retval\n"; exit $retval;  
}

#
# Used to compare .csd file
#
sub compareCSDfiles 
{
  my ($goldFileName,$testFileName,$absTol,$relTol,$zeroTol)=@_;

  my $exitCode=0;
  my $testFileCount=0;
  my $gsCount=0;
  my $absDiff=0;
  my $relDiff=0;
  my $i=0;
  my $idx=0;
  my @gsData;
  my @goldData;
  my @testFileData;
  my @testData;

  # open files and check that the files have the same number of lines
  if (not -s "$goldFileName" ) 
  { 
    print STDERR "Missing Gold Standard file: $goldFileName\nExit code = 2\n"; 
    $exitCode=2; 
    print "test Failed!\n";
    print "Exit code = $exitCode\n";
    exit $exitCode;
  }
  if (not -s "$testFileName" ) 
  { 
    print STDERR "Missing Test file: $testFileName\nExit code = 14\n"; 
    $exitCode=14; 
    print "test Failed!\n";
    print "Exit code = $exitCode\n";
    exit $exitCode;
  }

  open(TESTFILE,"$testFileName");
  open(GSFILE,"$goldFileName");

  # first test if the files have the same number of lines
  for (my $testFileCount=0; <TESTFILE>; $testFileCount++) { }
  for (my $gsCount=0; <GSFILE>; $gsCount++) { }

  close(GSFILE);
  close(TESTFILE);

  $exitCode=0;
  if ($testFileCount != $gsCount) 
  {
    print STDERR "file $testFileName doesn't match the Gold Standard\n";
    print STDERR "$testFileName line count= $testFileCount\n";
    print STDERR "Gold Standard line count= $gsCount\n";
    $exitCode=2;
  }
  else
  { 
    # If the line counts match, then compare in detail.
    # Re-open the files and compare them line by line.
    open(TESTFILE,"$testFileName");
    open(GSFILE,"$goldFileName");
    my $lineCount=0;
    my $lineGS;
    my $lineTestFile;
    my @gsData;
    my @goldData;
    my @testFileData;
    my @testData;
    my $foundDataStart=0;
    while( ($lineGS=<GSFILE>) && ($lineTestFile=<TESTFILE>) )
    {
      $lineCount++;
      # process a line into text and values.
      chop $lineGS;
      # Remove leading spaces on lineGS, otherwise the spaces become 
      # element 0 of "@gsData" instead of the first column of data.
      $lineGS =~ s/^\s*//;
      @gsData = (split(/[\s,]+/, $lineGS));

      # process a line into text and values.
      chop $lineTestFile;
      # Remove leading spaces on line, otherwise the spaces become 
      # element 0 of "testFileData" instead of the first column of data.
      $lineTestFile =~ s/^\s*//;
      @testFileData = (split(/[\s,]+/, $lineTestFile));

      if ($lineTestFile =~ /#C/)
      {
        $foundDataStart=1;
      }
           
      if ($#gsData != $#testFileData)
      {
        print STDERR "File $testFileName and Gold Standard have a different # of elements on line $lineCount:\n";
        print STDERR "File $testFileName had $#testFileData elements\n";
        print STDERR "Gold Standard had $#gsData elements\n";
        $exitCode=2;
      }
      # skip version and date/time lines, since they may change in each
      # test run.  Also need to handle XBEGIN line separately.  Also skip 
      # title line for now since it has the file name, which differs
      elsif ( ($lineTestFile =~ /SOURCE/) || ($lineTestFile =~ /TIME/) || ($lineTestFile =~ /TITLE/))
      {
        #no op
      }
      else
      {
        # the two files have the same number of items on a line.  
        # compare individual values as scalars or strings  
        for( $i=0; $i<=$#testFileData; $i++ )
        {
          if ( ($testFileData[$i] =~ /XBEGIN/) || ($testFileData[$i] =~ /XEND/) )
          {
            # XBEGIN line has numbers formatted as XBEGIN='0.00000000e+00' XEND='1.00000000e-02'
            # Need to extract the numbers.
            @testData = split(/[\']/,$testFileData[$i]);
            @goldData = split(/[\']/,$gsData[$i]);
            if ($#gsData != $#testFileData)
            {
              print STDERR "File $testFileName and Gold Standard have mismatched XBEGIN line\n";
              last;
              $exitCode=2;
            }
            if( ($testData[0] eq $goldData[0]) ) 
            {
              # two string fields match for XBEGIN and XEND
            }
            else
            {
              print STDERR "String elements failed compare on line $lineCount:\n";
              print STDERR "File $testFileName produced: \"$testFileData[$i]\" \n";
              print STDERR "Gold Standard was: \"$gsData[$i]\" \n";
              $exitCode=2;
              last;
            }
            if ( ( abs($testData[1]) <= $zeroTol ) && ( abs($goldData[1]) <= $zeroTol ) )
	    {
              # no op since both test and gold data are less than the zero tolerance
	    }
            else
            {
	      $absDiff = abs($testData[1] - $goldData[1]);
              $relDiff = $absDiff / abs($goldData[1]);   
              if ( ( $absDiff < $absTol ) && ( $relDiff < $relTol ) )
              {
                # two numeric fields match to within specified absolute tolerance and relative tolerance
              }
              else
              {
                print STDERR "Numeric comparison failed in column $i on line $lineCount: \n";
                print STDERR "File $testFileName had \"$testFileData[$i]\" while Gold Standard had \"$gsData[$i]\" \n";
                print STDERR "Calculated absDiff and relDiff = ($absDiff,$relDiff)\n";
                $exitCode=2;
                last;
              }
            }
          }
          else
          {
            # this handles date elements from lines that don't start with XBEGIN.  
            # There are three cases, as noted in the comments below.
	    if ( ($foundDataStart > 0) && ($testFileData[$i] =~ /:/ ) && ($gsData[$i] =~/:/) )
            { 
              # these are fields that contain time points.  They are of the form  0.0001:1
              # where 0.0001 is the value and 1 is the idx.  This next block breaks them
              # apart into two pieces, each of which is tested separately.  These values are
              # found after the #C line
              @testData = split(/:/,$testFileData[$i]);
              @goldData = split(/:/,$gsData[$i]); 
              foreach $idx (1 ..2)
              {
                if ( ( abs($testData[$idx]) <= $zeroTol ) && ( abs($goldData[$idx]) <= $zeroTol ) )
	        {
                  # no op since both test and gold data are less than the zero tolerance
	        }
                else
                {
	          $absDiff = abs($testData[$idx] - $goldData[$idx]);
                  $relDiff = $absDiff / abs($goldData[$idx]);   
                  if ( ( $absDiff < $absTol ) && ( $relDiff < $relTol ) )
                  {
                    # two numeric fields match to within specified absolute tolerance and relative tolerance
                  }
                  else
                  {
                    print STDERR "Numeric comparison failed in column $i on line $lineCount: \n";
                    print STDERR "File $testFileName had \"$testFileData[$i]\" while Gold Standard had \"$gsData[$i]\" \n";
                    print STDERR "Calculated absDiff and relDiff = ($absDiff,$relDiff)\n"; 
                    $exitCode=2;
                    last;
                  }
		}
                if ($exitCode != 0) { last;}
              } 
            }
            elsif ( looks_like_number($testFileData[$i]) && looks_like_number($gsData[$i]) )
            {  
              # this block is for numbers that didn't match the value:idx format above
              if ( ( abs($testFileData[$i]) <= $zeroTol ) && ( abs($gsData[$i]) <= $zeroTol ) )
	      {
                # no op since both test and gold data are less than the zero tolerance
	      }
              else
              {
	        $absDiff = abs($testFileData[$i] - $gsData[$i]);
                $relDiff = $absDiff / abs($gsData[$i]);   
                if ( ( $absDiff < $absTol ) && ( $relDiff < $relTol ) )
                {
                  # two numeric fields match to within specified absolute tolerance and relative tolerance
                  # debug info, to verify that the comparison is working okay
                  #print STDERR "Numeric comparison for column $i on line $lineCount: \n";
                  #print STDERR "File $testFileName had \"$testFileData[$i]\" while Gold Standard had \"$gsData[$i]\" \n";
                  #print STDERR "Calculated absDiff and relDiff = ($absDiff,$relDiff)\n"; 
                }
                else
                {
                  print STDERR "Numeric comparison failed in column $i on line $lineCount: \n";
                  print STDERR "File $testFileName had \"$testFileData[$i]\" while Gold Standard had \"$gsData[$i]\" \n";
                  print STDERR "Calculated absDiff and relDiff = ($absDiff,$relDiff)\n"; 
                  $exitCode=2;
                  last;
                }
              }
            }
            elsif( ($testFileData[$i] eq $gsData[$i]) ) 
            {
              # two string fields match
              # debug info to verify that the string comparison is working okay
              #print STDERR "$testFileData[$i] same as $gsData[$i]\n";
            }
            else
            {
              print STDERR "String elements failed compare on line $lineCount:\n";
              print STDERR "File $testFileName produced: \"$testFileData[$i]\" \n";
              print STDERR "Gold Standard was: \"$gsData[$i]\" \n";
              $exitCode=2;
              last;
            }
          }                 
        }     
      }
    }

    # close files
    close(GSFILE);
    close(TESTFILE);
  }

  return $exitCode;
}

#
# We need the 1; at the end because, when Perl loads a module, Perl checks
# to see that the module returns a true value to ensure it loaded OK. 
#
1;

#
# End of common code used in each .MEASURE test
#  


