#!/usr/bin/perl -w
# TrafficMatrix.pm -- (c) Matthew Roughan, Fri Jun 14 2013
#   <matthew.roughan@adelaide.edu.au>
#
# code to read/write TM data into the form described in
#    http://
#
########################################################

package TrafficMatrix;
use strict;
use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);

use Exporter;
$VERSION = 1.00;              # Or higher
@ISA = qw(Exporter);

@EXPORT      = qw();       # Symbols to autoexport (:DEFAULT tag)
@EXPORT_OK   = qw(write read_file latex_write);  # Symbols to export on request

# use LaTeX::Table; don't use this, to avoid dependencies, but would be nice
use List::Util;

# write out a traffic matrix in our file format
sub write (\@\%$) {
  my $tm = shift;
  my $meta_data = shift;
  my $file = shift;
  my %meta_data = %$meta_data;
  my $N = $#$tm + 1;     # length of array
  my $key;
  my $today;
  my $file_handle;

  my %mandatory_meta_data = (
			     'name' => 'unknown',
			     'date' => 'unknown',
			     'time' => 'unknown',
			     'time interval' => 'unknown',
			     'units' => 'unknown',
			     'source' => 'unknown',
			     'topology' => 'unknown',
			     'cite' => 'unknown',
			     'type' => 'unknown',
			    );
  foreach $key (keys %mandatory_meta_data) {
    if (!exists($meta_data{$key})) {
      die "Error: meta-data '$key' is mandatory\n";
    } elsif (!defined($meta_data{$key})) {
      $meta_data{$key} = $mandatory_meta_data{$key};
    }
  }

  open($file_handle, "> $file") or die "Error:could not open $file: $!\n";
  chomp($today = `date`);
  print $file_handle "# created by = TrafficMatrix::write, version $VERSION\n";
  print $file_handle "# creation date = $today\n";
  foreach $key (keys %meta_data) {
    print $file_handle "# $key = $meta_data{$key}\n";
  }
  for (my $i = 0; $i < $N; $i++) {
    # check each row is the right length for a square array
    if ($#{$tm->[$i]}+1 != $N) {
      die "Error: traffic matrix row " . ($i+1) . " was length " . ($#{$tm->[$i]}+1) . " instead of $N.\n";
    }
    # print it
    print $file_handle join(',', @{$tm->[$i]}) . "\n";
  }
  close($file_handle) or die "Error: could not close $file: $!\n";
}


# read in our file format
sub read_file ($) {
  my $tm_file = shift;
  my $i = 0;
  my $row_length=0;
  my (@tm, @row);
  my %meta_data;
  my $file_handle;

  open($file_handle, "< $tm_file") or die "Error:could not open $tm_file: $!\n";
  while (<$file_handle>) {
    if (m"\s*#\s*([^=]+)\s*=\s*(.*)") {
      $meta_data{trim($1)} = trim($2);
    } elsif (m"\S") {
      chomp;
      @{$tm[$i]} = split(',');
      if ($row_length == 0) {
	$row_length = $#{$tm[$i]}+1;
      } elsif ($#{$tm[$i]}+1 != $row_length) {
	die "First row was length $row_length, and row $i is length " . scalar(@row) . "\n";
      }
      # print "     " . join(',', @{$tm[$i]}) . "\n";
      $i++;
    }
  }
  close($file_handle) or die "Error: could not close $tm_file: $!\n";
  if ($i != $row_length) {
    die "Matrix not square: wow was length $row_length, but there are $i rows\n";
  }
  return (\@tm, \%meta_data);
}

# create a time series of total traffic
sub totals ($$) {
  my $tm_dir = shift;
  my $outfile = shift;
  my ($i, $k, $n);
  my $file;
  my @files;
  my ($tm, $meta_data);
  my ($total, $row_sum);
  my ($year, $month, $day, $hour, $minute, $second);
  my $file_handle;
  my $key;

  @files = glob("${tm_dir}/tm.*dat");
  ($tm, $meta_data) = read_file($files[0]);

  open($file_handle, "> $outfile") or die "Error:could not open $outfile: $!\n";
  print $file_handle "# written by TrafficMatrix::totals \n";
  print $file_handle "# units: $meta_data->{'units'}\n";
  print $file_handle "# format: \n";
  print $file_handle "# yyyy-mm-dd hh-mm-ss total_traffic\n";

  $k = 1;
  $n = scalar(@files);
  foreach $file (@files) {
    print "file $k of $n: $file\n";
    ($tm, $meta_data) = read_file($file);
    $total = 0;
    for ($i=0; $i<scalar(@{$tm}); $i++) {
      $row_sum = List::Util::sum( @{$tm->[$i]} );
      $total += $row_sum;
    }
    printf $file_handle "  %s %s %12f\n", ($meta_data->{'date'}, $meta_data->{'time'}, $total);
    $k++;
  }

  close($file_handle) or die "Error: could not close $outfile: $!\n";
  return($n);
}

sub trim ($) {
  # http://stackoverflow.com/questions/4597937/perl-function-to-trim-string-leading-and-trailing-whitespace  my $str = shift;
  my $str = shift;
  $str =~ s/^\s+//;
  $str =~ s/\s+$//;
  return $str;
}

# write out a traffic matrix as a LaTeX table
sub latex_write  ($$$$) {
  my $tm_file = shift;      # file name to read
  my $print_format = shift; # standard Perl/C format string for sprintf
  my $scale_factor = shift; # amount to scale output
  my $caption = shift;
  my ($tm, $meta_data) = read_file($tm_file);
  my $N = $#$tm + 1;     # length of array
  my ($i, $j, $key);
  my $row_sum = 0;
  my $total = 0;
  my @col_sums;

  print "% created by TrafficMatrix::latex_write\n";
  foreach $key (keys %{$meta_data}) {
    print "% $key = $meta_data->{$key}\n";
  }
  print "\\begin{table}[htp]\n";
  print "  \\centering\n";
  print "  \\footnotesize\n";
  print "  \\begin{tabular}[htp]{r|";
  for ($i = 0; $i < $N; $i++) {
    print "r";
  }
  print "|r\}\n";
  print "          & \\multicolumn{$N}{c|}{dst} & \\\\\n";
  print "      src &";
  for ($i = 0; $i < $N; $i++) {
    printf " %3d &", ($i+1);
  }
  print " sum \\\\\n";
  print "    \\hline\n";
  for ($i = 0; $i < $N; $i++) {
    $row_sum = List::Util::sum( @{$tm->[$i]} );
    $total += $row_sum;
    printf " %3d &", ($i+1);
    print join(' & ', (map {sprintf($print_format,$_*$scale_factor)} @{$tm->[$i]}) );
    print " & " . sprintf($print_format,$row_sum*$scale_factor) . " \\\\\n";
    for ($j=0; $j<$N; $j++) {
      $col_sums[$j] += $tm->[$i]->[$j];
    }
  }
  print "    \\hline\n";
  print "      sum &";
  print join(' & ', (map {sprintf($print_format,$_*$scale_factor)} @col_sums ) );
  print " & " . sprintf($print_format,$total*$scale_factor) . " \\\\\n";
  print "  \\end{tabular}\n";
  if (defined($caption)) {
    print " \\caption{$caption}\n";
  } else {
    print "  \\caption{Traffic matrix from file $tm_file.}\n";
  }
  print "  \\label{tab:traffic_matrix}\n";
  print "\\end{table}\n";

}
# add in src and dst strings, from topology
#   and dst, should be rotated
#   and use big tables (small fonts)


1;

