#!/usr/bin/perl
#
# scan a given SWF (standard workload format) file, and use convert2swf_v3.pm
# to re-convert the data into SWF, including checks and normalizations.
# it is assumed times are OK, only sorting and user/app numbering may be needed.
#

use warnings;
use strict;
use Time::Local;
use POSIX;
use convert2swf_v4;


############################
#
# DO THE CONVERSION:
#

my ($header, $UTCstart, $tz_str, $max_procs) = get_header();

SWF::init( $tz_str, $max_procs, \() );

my $format_bugs;
my $jobs_data = get_jobs();

my ($zero, $maxtime) = SWF::convert( $jobs_data, $format_bugs );

my $newheader = regenerate_header( $zero, $maxtime );

SWF::print( $newheader );


sub get_header {
################
# copy given header and return it for reuse

    my $header  = "";
    my $startTS = 0;
    my $TZstr   = "";
    my $procs   = -1;
    my $start_jobs;

    while (<>) {

	if ((/^;/) || (/^\s*$/)) {
	    $header .= $_;
	    $start_jobs = tell(ARGV);

	    if (/^;\s*UnixStartTime:\s*(\d+)\s*$/) {
		$startTS = $1;
	    }
	    if (/^;\s*TimeZoneString:\s*([\w\/]+)\s*$/) {
		$TZstr = $1;
		$ENV{TZ} = $TZstr;
		POSIX::tzset();
	    }
	    if (/^;\s*MaxProcs:\s*(\d+)\s*$/) {
		$procs = $1;
	    }
	    if ((/^;\s*MaxNodes:\s*(\d+)\s*$/) && ($procs == -1)) {
		$procs = $1;
	    }
	}
	else {
	    seek(ARGV, $start_jobs, 0);
	    return ($header, $startTS, $TZstr, $procs);
	}
    }
    die "no jobs after header!\n";
}


my $job_cnt;

sub get_jobs {
##############
# parse input format and create a hash for each job

    my @list_of_jobs;

    #
    # scan trace and collect job info
    #

    while (<>) {

	#
	# parse line and extract info
	#

	if (/^\s*$/) {		# empty line
	    next;
	}

	$_ =~ /\s*(.*)\s*/;	# get rid of white space at ends
	$_ = $1;


	my @line = split(/\s+/, $_);
	if ($#line != 17) {
	    $format_bugs .= "format_problem $_\n";
	    warn(">>>Format problem on $_\ngot $#line fields (should be 17)");
	    next;
	}

	my %nj;

	#
	# set the desired fields as specified in convert2swf.pl
	#

	$nj{trace}  = $_;	# used for error messages

	# record submit, start, and end times
	$nj{submit}    = $UTCstart   + $line[1];
	$nj{start}     = $nj{submit} + $line[2];
	$nj{end}       = $nj{start}  + $line[3];

	$nj{procs}     = $line[4];

	$nj{cpu}       = $line[5];
	$nj{mem}       = $line[6];

	$nj{req_procs} = $line[7];
	$nj{req_time}  = $line[8];
	$nj{req_mem}   = $line[9];

	$nj{status}    = $line[10];

	$nj{user}      = $line[11];
	$nj{group}     = $line[12];
	$nj{app}       = $line[13];

	$nj{queue}     = $line[14];
	$nj{partition} = $line[15];

	$list_of_jobs[$job_cnt++] = \%nj;
    }

    return \@list_of_jobs;
}


sub regenerate_header {
    my ($zero, $maxtime) = @_;

    my $new;

    my @lines = split(/\n/, $header);

    foreach my $ln (@lines) {

	if ($ln =~ /MaxJobs/) {
	    $new .= "; MaxJobs: $job_cnt\n";
	}
	elsif ($ln =~ /MaxRecords/) {
	    $new .= "; MaxRecords: $job_cnt\n";
	}
	elsif ($ln =~ /UnixStartTime/) {
	    $new .= "; UnixStartTime: $zero\n";
	}
	elsif ($ln =~ /StartTime/) {
	    $new .= sprintf("; StartTime: %s\n",
			    strftime("%a %b %2d %H:%M:%S %Z %Y", localtime($zero)));
	}
	elsif ($ln =~ /EndTime/) {
	    $new .= sprintf("; EndTime:   %s\n",
			    strftime("%a %b %2d %H:%M:%S %Z %Y", localtime($maxtime)));
	}
	else {
	    $new .= "$ln\n";
	}
    }

    return $new;
}
