#! /usr/bin/perl
#
# script to fill in part of SourceCodeException.java
# based upon the contents of errorTypes.
# Copyright (c)	  Tristan Allwood (toa02) 2004,
#		  Matthew Sackman (ms02) 2004.



use warnings;
use strict;

use Data::Dumper;

#my $sourceFile = 'errorTypes';
#my $targetFile = 'SourceCodeException.java';
#my $extractExcScript = 'mkExtractKTypeSourceCodeException';

my $arg1 = shift or die("Missing argument 1: path to errors types");
my $arg2 = shift or die("Missing argument 3: path to type exception script");
my $arg3 = shift or die("Missing argument 2: path to generated exception file");
my $arg4 = shift or die("Missing argument 3: path to generated type exception file");

my $sourcePath = $arg1;
my $scriptPath = $arg2;
my $targetPath = $arg3;


# Ok, lets start by building a representation of errorTypes.

my @linesToPut = doETypes($sourcePath);

writeOutputFile($targetPath, \@linesToPut);

# Detangle error and type package by extracting all exceptions from SourceCodeExceptions that
# reference classes in kenya.types

system("/usr/bin/perl $scriptPath $targetPath $arg4");

exit(0);



sub writeOutputFile{
   my $targetPath = shift;
   my @linesToPut = @{shift @_ };


   open( TFILE, "<", $targetPath) or die ("Can't open file $targetPath : $!");
   my @tLines = <TFILE>;
   close TFILE;

   open( TFILE, ">", $targetPath) or die("Can't open file $targetPath : $!");

   my $dp = 1;
   foreach my $cLine ( @tLines ) {
      if( $cLine =~ /\/\/MARKER BEGIN INJECT/ ){
	 print TFILE $cLine;
	 $dp = 0;

	 foreach( @linesToPut ){
	    print TFILE $_;
	 }

      }


      if( $cLine =~ /\/\/MARKER END INJECT/ ){
	 $dp = 1;
      }

      if( $dp ){
	 print TFILE $cLine;
      }
   }

   close TFILE;
}

# Builds up the line-by-line representation of what should be in
sub doETypes{
   my $sourcePath = shift;

   my @lines = ();

   my $struct;

   my $mode = "unset";
   my %modes = 
   (
      "findLine" => sub	{  my $line = shift;
			   $struct = {'NAME' => $line };
			   $mode = "javac";
			},
			   

      "javac"    => sub	{ 
			   my $message = shift;
			   my $jMP = [];

			   while( $message =~ /([^\$]*)(\$([^\$]*)\$)?/g ){
			      # $1 is normal text.
			      # $2 is a variable
			      if( defined $1 ){
				 push @$jMP,"\"".$1."\"";
			      }

			      if( defined $3 ){
				 push @$jMP,"$3";
			      }
			   }

			   $$struct{'JAVAC'} = $jMP;
			   $mode = "human";

			},

      "human"    => sub	{ 
			   my $message = shift;
			   my $hMP = [];

			   while( $message =~ /([^\$]*)(\$([^\$]*)\$)?/g ){
			      # $1 is normal text.
			      # $2 is a variable
			      if( defined $1 ){
				 push @$hMP,"\"".$1."\"";
			      }

			      if( defined $3 ){
				 push @$hMP,"$3";
			      }
			   }

			   $$struct{'HUMAN'} = $hMP;
			   $mode = "lnk";
			},

      "lnk"      => sub {
			   my $message = shift;
			   
			   if( $message =~ /^YES$/i ){
			      $$struct{'LNK'} = "YES";
			   }elsif( $message =~ /^NO$/i ){
			      $$struct{'LNK'} = "NO";
			   }else{
			      $$struct{'LNK'} = undef; #explicitly pointless but what the heck
			   }

			   $mode = "vars";
			},

      "vars"     => sub {
			   my $message = shift;

			   my $vars = {};
			   while( $message =~ /\((\w+),(\w+)\)/g ){
			      $$vars{$2} = $1;  
			   }

			   $$struct{'VARS'} = $vars;
			},
      "unset"    => sub { print "."; }
   );
		  

   open( ETYPES, "<", $sourcePath ) or die ("Couldn't open $sourcePath; $!");

   while( my $line = <ETYPES> ){
      next if( $line =~ /^#/ );	 # skip comments

      if( $line =~ /^(\w+)/ ){
	 push @lines, dumpStruct($struct);
	 $mode = "findLine";
	 $line = $1;
      }elsif( $line =~/^\s+(.+)$/ ){
	 $line = $1;
      } else{
	 next;
      }
      

      my $sub = $modes{$mode};
      &$sub($line);
   }

   close ETYPES;

   push @lines, dumpStruct($struct);

   return @lines;
}

my $errID = -1;

sub dumpStruct{
   my $struct = shift;

   # first of all, check everything is properly defined or print a warning.
   if(! (
	 defined( $$struct{'NAME'} ) and
	 defined( $$struct{'JAVAC'} ) and
	 defined( $$struct{'HUMAN'} ) and 
	 defined( $$struct{'LNK'} )
       )
   ){
      #print "Warning, badly formed area found.\n";
      #print Dumper($struct);
      return ();
   }

   # now do the methods.
   my @lines = ();
   
   # Method signature.

   # Check if KType, KClassType or KVariable are referenced
   my $packageDependence = "";
   if( defined $$struct{'VARS'} ){
      foreach my $key ( sort keys %{$$struct{'VARS'}} ){
		  if ($struct->{'VARS'}->{$key} =~ /KType/ ||
		  	  $struct->{'VARS'}->{$key} =~ /KClassType/ ||
		  	  $struct->{'VARS'}->{$key} =~ /KVariable/)
		   {
				$packageDependence = ", depends on kenya.types ";
 			}
      }
   }

   push @lines, "\t/** AutoGenerated Method, errID = " . ++$errID . $packageDependence ."*/\n";
   push @lines, "\tstatic final int $$struct{'NAME'} = $errID;\n";
   my $fl = "\tpublic static void throw$$struct{'NAME'}(int ln, int pos, int len, ";


   if( $$struct{'LNK'} =~ /^YES$/ ){
      $fl .= "int[][] linkPos, ";
   }

   if( defined $$struct{'VARS'} ){
      foreach my $key ( sort keys %{$$struct{'VARS'}} ){
	 $fl .= ($struct->{'VARS'}->{$key});
	 $fl .= " " . $key . ", " ;
      }
   }

   $fl =~ s/, $//;
   $fl .= "){\n";
   push @lines, $fl;

   # now for the method body
   # we are aiming to call:
   #  private SourceCodeException( String[] messages, int[] errPos, int[][] linkPos, int errID ){

   push @lines, "\t\tStringBuffer javacSB = new StringBuffer();\n";
   push @lines, "\t\tStringBuffer humanSB = new StringBuffer();\n\n";

   foreach my $cl ( @{$$struct{'JAVAC'}} ){
      next if( $cl =~ /""/);
      $cl =~ s/\\n/" + NEWLINE + "/g;
      push @lines, "\t\tjavacSB.append(" . $cl . ");\n";
   }

   push @lines, "\t\t\n";

   foreach my $cl ( @{$$struct{'HUMAN'}} ){
      next if( $cl =~ /""/);
      $cl =~ s/\\n/" + NEWLINE + "/g;
      push @lines, "\t\thumanSB.append(" . $cl . ");\n";
   }

   push @lines, "\t\t\n";

   push @lines, "\t\tString[] out = { javacSB.toString(), humanSB.toString() };\n";


   if( $$struct{'LNK'} =~ /^NO$/ ){
      push @lines, "\t\tint[][] linkPos = null;\n";
   }
   push @lines, "\t\tint[] errPos = { ln, pos, len };\n";

   push @lines, "\t\t\n";

   push @lines, "\t\tthrow new SourceCodeException(out, errPos, linkPos, $$struct{'NAME'});\n";

   push @lines, "\t}\n\n";

   return @lines;
}
