#!/usr/bin/perl -w
#----------------------------------------------------------------------------------------
#
# Convert data in avt format to tabbed format.  The avt
# format was defined by Brighten Godfrey.  We apply this
# script to the following data sets formatted by Brighten Godfrey:
# 
# PlanetLab (Stribling 2005), 
# web servers (Bakkaloglu et al 2002)
# DNS servers (Pang et al 2004)
# Skype superpeers (Guha et al 2006)
#
# Credit goes to Brighten Godfrey for intially formatting in
# avt those data sets.

# usage: 
#  ./avt2tab.pl -input ~/research/fta/data/avt03_raw/traces/web_sites.avt -output ~/research/fta/data/websites_tab -platformname "websites02" -platformtype "web" -creator "Mehmet_Bakkaloglu" -cite "bakk_02"
#
# Contact: Derrick Kondo derrick.kondo :: inria fr
#
# Copyright 2009 by Failure Trace Archive site (http://fta.inria.fr)
#
#----------------------------------------------------------------------------------------

############################################################################
# TESTS:
#
# component start and stop times look right.
# event trace looks right
#	last event recorded correctly
#	works for empty trace


# limit scope of variables
use strict;
 
# Module to help handle command line switches
use Getopt::Long;
use FTA_Format;

# method headers
sub main();
sub extract_parameters();

sub create_platform ($);
sub create_node ($);
sub create_node_perf ($);
sub create_component ($);
sub create_event_trace ($$);
sub create_event_state ($);
sub process_probe ($$$$);

# platform globals
my ($PLATFORM_ID, $PLATFORM_NAME, $PLATFORM_LOCATION, $PLATFORM_TYPE);

$PLATFORM_ID = 0;
$PLATFORM_LOCATION = "worldwide";

# DATA STRUCTURES

# %NODE is a hash table that points to a array reference,
# which points to an array of events:

# node_name -> events arr (node_id, reference to event_traces array of
# (event_id, event_type, start time, stop time))

my (%NODE, $NODE_ID_COUNTER);

$NODE_ID_COUNTER = 0;

# component globals
my ($COMPONENT_ID, $COMPONENT_CITE, $RESOLUTION);
$COMPONENT_ID = 0;
$RESOLUTION = 10*60; # 10 minutes

# creator globals
my ($CREATOR_ID, $CREATOR, $CITE, $COPYRIGHT);
$CREATOR_ID = 0;
# assume no spaces here

# bibtex entry difficult to parse in tabbed format. Thus put
# bibtex citation instead.

main;

sub main (){
    my (@args, $start, $stop, $cwd);

    my ($inputfile, $outputdir, $a, $b);

    ($inputfile, $outputdir, $PLATFORM_NAME,
     $PLATFORM_TYPE, $CREATOR, $CITE) = extract_parameters();

    $COPYRIGHT = "$CITE";
    
    createDir ($outputdir);
    create_platform ($outputdir);
    create_node_perf ($outputdir);
    create_event_trace ($inputfile, $outputdir);
    create_event_state ($outputdir);
# must be called after create_event_trace
    create_node ($outputdir);
    create_component ($outputdir);
    create_creator ($outputdir);
}

sub create_platform ($){
    my ($outputdir) = @_;

    open (OUT, ">$outputdir/platform.tab") or die "could not open $outputdir/platform.tab";

    print_header (*OUT, "platform");

    print_ln (*OUT, "platform", [$PLATFORM_ID, $PLATFORM_NAME,
		     $PLATFORM_LOCATION, $PLATFORM_TYPE, "NULL"]);

    close (OUT);
}
 

sub create_creator ($){
  my ($outputdir) = @_;
  
  open (OUT, ">$outputdir/creator.tab") or die "could not open $outputdir/creator.tab";
  
  print_header (*OUT, "creator");
  
  my (@node_names) = keys (%NODE);
  @node_names = sort @node_names;
  
  @node_names > 0 or die "no nodes\n";
  
  my ($node_name);
  foreach $node_name (@node_names){
    my ($events, $node_id, $event_traces, $event_info);
    
    print_ln (*OUT, "creator", [$CREATOR_ID, $COMPONENT_ID,
		$NODE{$node_name}->[0], $PLATFORM_ID, $CREATOR, $CITE, $COPYRIGHT]);
    
  }
  
  close (OUT);
}


# create_event_trace must be called before
sub create_node ($){
    my ($outputdir) = @_;

    my ($node_name);

    open (NODE, ">$outputdir/node.tab") or die "could not open $outputdir/node.tab";

    print_header (*NODE, "node");

    my (@node_names) = keys (%NODE);
    @node_names = sort @node_names;

    @node_names > 0 or die "no nodes\n";

    foreach $node_name (@node_names){
	my ($events, $node_id, $event_traces, $event_info);
	
	print_ln (*NODE, "node",
		  [$NODE{$node_name}->[0], $PLATFORM_ID,
		   $node_name, "NULL", "NULL", "NULL", "NULL", "NULL",
		   "NULL", "NULL", "NULL", "NULL", "NULL", "NULL",
		   "NULL", "NULL", "NULL"]);
	
    }

    close (NODE);
}

# create_node_perf
sub create_node_perf($){
    my ($outputdir) = @_;

    open (NODE_PERF, ">$outputdir/node_perf.tab") or die "could not open $outputdir/node_perf.tab";

    print_header (*NODE_PERF, "node_perf");

    close (NODE_PERF);

}

# create_event_state
sub create_event_state($){
    my ($outputdir) = @_;

    open (EVENT_STATE, ">$outputdir/event_state.tab") or die "could not open $outputdir/event_state.tab";

    print_header (*EVENT_STATE, "event_state");

    close (EVENT_STATE);

}

# create_event_trace must be called before

sub create_component ($){
    my ($outputdir) = @_;
    
    my ($component_type) = "0"; # 0 -> host availability
# should have crawler availability
#     my ($component_type2_crawler) = "crawler host availability ";

    open (COMPONENT, ">$outputdir/component.tab") or die "could not open $outputdir/component.tab";
    
    print_header (*COMPONENT, "component");

    my (@node_names) = keys (%NODE);
    @node_names = sort @node_names;

    @node_names > 0 or die "no nodes\n";
    
    my ($node_name);
    foreach $node_name (@node_names){
	my ($events, $node_id, $event_traces, $event_info);
	
	$events = $NODE{$node_name};

	$node_id = $events->[0];
	$event_traces = $events->[1];

	my ($trace_start, $trace_end);

# get start time of first event
	$trace_start = (defined($event_traces->[0]->[2])? $event_traces->[0]->[2]: "NULL");
# get stop time of last event
	$trace_end = (defined($event_traces->[-1]->[3])? $event_traces->[-1]->[3]: "NULL");

	print_ln (*COMPONENT, "component", [$COMPONENT_ID,
					    $node_id,
					    $PLATFORM_ID,
					    $CREATOR_ID,
					    $node_name,
					    $component_type,
					    $trace_start,
					    $trace_end,
					    $RESOLUTION]);
	
    }

    close (COMPONENT);
    
}

# works for empty trace, i.e., creates an empty
# event_traces arr


sub create_event_trace ($$){
    my ($inputfile, $outputdir) = @_;
    
    my ($event_id, $component_id, $node_id,
	$event_type, $event_state, $event_start_time,
	$event_end_time, $event_end_reason, $line, @info,
	$node_name, $num_intv, $event_traces);

    my (@intvs, $f);

    $node_id = 0;

    open (IN, "$inputfile") or die "could not open file $inputfile";

    while ($line = <IN>){
      chomp($line);

      next if $line =~ /^(\#)/;  # skip comment
      next if $line =~ /^(\s)*$/;  # skip blank lines
      
      @info = split /\s+/, $line;
#       print "@info\n";
      @info > 0 or die "host trace is empty";

      $node_name = $info[0];
      $num_intv = $info[1];
      
      @intvs = @info[2..$#info];
      @intvs/2 == $num_intv or die "wrong number of intvs for host $node_name: " . @intvs/2 . " versus $num_intv ";

      warn "host $node_name has no failures nor intervals" if (@intvs==0);
      
      $event_traces = form_event_traces (@intvs);
      
      $NODE{$node_name} = [$node_id, $event_traces];
      $node_id++;
    }

    print_event_trace($outputdir);

    close (IN);
}

sub form_event_traces (@){
  my (@intvs) = @_;
  
  my (@event_traces, $event_id, $event_type, $start_time,
  $stop_time, $ind);
  $event_id=0;
  $event_type = 0;
  
# * checked that we exit correctly. last interval included correctly
  for ($ind=0; defined($intvs[$ind+1]); $ind++){
    
    $event_type = ($event_type==1? 0: 1);
    
    push (@event_traces, [$event_id, $event_type,
			  $intvs[$ind], $intvs[$ind+1]]);
    
    $event_id++;
  }

# * my @event_traces is local to this function so it is a
# unique reference  
  return \@event_traces;
}


sub print_event_trace($){
    my ($outputdir)=@_;
 
    my (@node_names) = keys (%NODE);
    @node_names = sort @node_names;

    my ($node_name);

    open (EVENT_TRACE, ">$outputdir/event_trace.tab") or die
	"file $outputdir/event_trace.tab could not be used for output";

    print_header (*EVENT_TRACE, "event_trace");

    foreach $node_name (@node_names){
	my ($events, $node_id, $event_traces, $event_info);
	
	$events = $NODE{$node_name};
	defined ($events) or die "events undef";
	$node_id = $events->[0];

	$event_traces = $events->[1];
	
# 	print "node_name: $node_name\n";

	foreach $event_info (@$event_traces){
	    my ($event_id, $event_type, $start_time,
		$stop_time);

	    defined ($event_info) or die "event_info undef";
# 	    print "event_info: " . @$event_info . "\n";
# 	    print "event_id: " . $event_info->[1] . "\n";

	    $event_id = $event_info->[0];
	    $event_type = $event_info->[1];
	    $start_time = $event_info->[2];
	    $stop_time = $event_info->[3];

# 	    print "\tevent_id: $event_id, COMPONENT_ID: $COMPONENT_ID, node_id: $node_id, platform_id: $PLATFORM_ event_type: $event_type, start_time: $start_time, stop_time: $stop_time\n";

	    defined ($event_id) or die "event id undef";
	    defined ($COMPONENT_ID) or die "COMPONENT_ID undef";
	    defined ($node_id) or die "node_id undef";
	    defined ($PLATFORM_ID) or die "platform_id undef";
	    defined ($event_type) or die "event_type undef";
	    defined ($start_time) or die "start_time undef";
	    defined ($stop_time) or die "stop_time undef";

	    print_ln (*EVENT_TRACE, "event_trace", 
		 [$event_id, $COMPONENT_ID,
		 $node_id, $PLATFORM_ID, $node_name, $event_type, 
		 $start_time, $stop_time,
		 "NULL"]);
	}
    }
    close (EVENT_TRACE);
}


# If the dir doesn't exist, create it                                                                     
sub createDir ($){
    my ($dir) = @_;                                                                                                           
                                                                                                                              
    if (! -e $dir){                                                                                                           
        print "Creating directory $dir\n";                                                                                    
        my $retval = mkdir($dir, 0777);                                                                                       
        if ($retval == 0)                                                                                                     
        {                                                                                                                     
            die "Fatal Error: $dir could not be created\n Message is: $!\n. Program Terminating....\n";                       
        }                                                                                                                     
    }                                                                                                                         
                                                                                                                              
}                                                                                                                             


sub extract_parameters()
{
	my %params = (
		inputfile => \(my $inputfile = undef),
		outputdir => \(my $outputdir = undef),
		platformname => \(my $platformname = undef),
		platformtype => \(my $platformtype = undef), 
		creator => \(my $creator = undef), 
		cite => \(my $cite = undef)
	);

	# Call Library function which will extract and remove all switches and
	# their corresponding values.
	# These parameters will be removed from @ARGV

	my $errorval = &GetOptions(\%params, "inputfile=s",
				   "outputdir=s",
				   "platformname=s",
				   "platformtype=s",
				   "creator=s", "cite=s");
                        # the exclaimation point allows for the negation
                        # of the switch (i.e. -nobackup/-nobody is a switch)

	# Error value should have returned 1 if nothing went wrong
	# Otherwise, an unlisted parameter was specified.
	if($errorval !=1)
	{
		# Expecting GetOptions to state error.

		print "Exiting Program...\n";
		exit 0;
	}



	defined ($inputfile) or die "inputfile not defined\n";
	defined ($outputdir) or die "outputdir not defined\n";
	(!-e $outputdir) or die "dir $outputdir already exists";
	return ($inputfile, $outputdir, $platformname,
	$platformtype, $creator, $cite);
	
}
