#!/usr/bin/perl
use strict subs, vars;

#
# domain 0 reserved for HA and MH
# domain 1 reserved for wired nodes
# others domains: FAs
#
# currently supports only one HA
#
# INFILE:
# first wired nodes, then HA, then MH, then FAs!
#

die "usage: gen_scen <infile>" if (@ARGV < 1);
my $basename  = ($ARGV[0] =~ /(.*)\./) ? $1 : $ARGV[0];
my $sim_file  = $basename . ".tcl";
my $scen_file = $basename . ".scn";
my $com_file  = $basename . ".com";

my $rnd_seed = 0.0;
my $prog_sec = 10;
my $default_packet_size = 512;

my $size_x = 1000;
my $size_y = 1000;
my $time = 100.0;
my $default_txrange = 250.0; # transmission range in m
my $default_color = "black"; # default node color

my $nn = 0; # no. of nodes
my $fa_domain = 2;
my $mh_node = 0; # for BS

my $fid = 0; #flow IDs for connections

my $num_con = 0;
my $ha_nr = 0; # "real" HA node nr.
my $max_rate = 10000;
my @wired_nodes = ();
my @wireless_nodes = ();
my @mobile_nodes = ();
my @all_mn_numbers = ();
my @home_agents = ();
my @scen = ();
my @link = ();
my @falink = ();
my @connections = ();
my @cluster = (0, 0, 0);
my @node_nr = (); # link script node nr. to "real" node nr.


sub make_node {
    my ($nr, $x, $y, $txrange, $priority, $color, $address, $text, $type) = @_;

    $x = 0 if ($x < 0);
    $y = 0 if ($y < 0);

    if ($type eq "HA") {
	push @home_agents, "set node_($nr) [\$ns_ node $address]\n";
	push @home_agents, "[\$node_($nr) set netif_(0)] set Pt_ $txrange\n"
	    if ($txrange != 0);
	push @home_agents, "[\$node_($nr) set regagent_] priority $priority\n"
	    if ($priority != 0);
    } elsif ($type eq "FA") {
	push @wireless_nodes, "set node_($nr) [\$ns_ node $address]\n";
	push @wireless_nodes, "[\$node_($nr) set netif_(0)] set Pt_ $txrange\n"
	    if ($txrange != 0);
	push @wireless_nodes, "[\$node_($nr) set regagent_] priority $priority\n"
	    if ($priority != 0);
    } elsif ($type eq "MH") {
	push @mobile_nodes, "set node_($nr) [\$ns_ node $address]\n";
	push @mobile_nodes, "[\$node_($nr) set netif_(0)] set Pt_ $txrange\n"
	    if ($txrange != 0);
	push @mobile_nodes, "[\$node_($nr) set regagent_] set home_agent_ " .
	                    "[AddrParams addr2id [\$node_($ha_nr) node-addr]]\n";
    } else {
	push @wired_nodes, "set node_($nr) [\$ns_ node $address]\n";
	push @wired_nodes, "[\$node_($nr) set netif_(0)] set Pt_ $txrange\n"
	    if ($txrange != 0);
    }

    my $tmp = "# attribute: node $nr text: '$text' type: $type color: $color";
    $tmp .= " txrange: $txrange" if ($txrange > 0);
    $tmp .= "\n\$node_($nr) set X_ $x\n\$node_($nr) set Y_ $y\n\n";
    push @scen, $tmp;
}


sub make_link {
    my ($from, $to, $bw, $delay, $queue) = @_;
    push @link, "\$ns_ duplex-link \$node_($from) \$node_($to) $bw $delay $queue\n";
}


sub make_connection {
    my ($from, $to, $starttime, $stoptime, $type, $packet_size, $interval, $burst, $idle) = @_;

    $packet_size = $default_packet_size if ($packet_size == 0);

    if ($type =~ /tcp/i) {
	push @connections, <<END_CON;
# $starttime $from -> $to tcp 10000 $packet_size
# $from connecting to $to at time $starttime
set tcp_($num_con) [\$ns_ create-connection TCP \$node_($from) TCPSink \$node_($to) $fid]
\$tcp_($num_con) set window_ 32
\$tcp_($num_con) set packetSize_ $packet_size
set ftp_($num_con) [new Application/FTP]
\$ftp_($num_con) attach-agent \$tcp_($num_con)
\$ns_ at $starttime "\$ftp_($num_con) start"
\$ns_ at $stoptime "\$ftp_($num_con) stop"

END_CON

} elsif ($type =~ /udp/i) {
    
    $interval = $packet_size * 8.0 / ($max_rate * 0.9) if ($interval == 0);

    push @connections, <<END_CON;
# $starttime $from -> $to udp 10000 $packet_size
# $from connecting to $to at time $starttime
set udp_($num_con) [new Agent/UDP]
set null_($num_con) [new Agent/Null]
\$udp_($num_con) set fid_ $fid
\$null_($num_con) set fid_ $fid
\$ns_ attach-agent \$node_($from) \$udp_($num_con)
\$ns_ attach-agent \$node_($to) \$null_($num_con)
\$ns_ connect \$udp_($num_con) \$null_($num_con)
\$udp_($num_con) set packetSize_ $packet_size
set cbr_($num_con) [new Application/Traffic/CBR]
\$cbr_($num_con) attach-agent \$udp_($num_con)
\$cbr_($num_con) set interval_ $interval
\$cbr_($num_con) set packetSize_ $packet_size
\$ns_ at $starttime "\$cbr_($num_con) start"
\$ns_ at $stoptime "\$cbr_($num_con) stop"

END_CON
    
} elsif ($type =~ /exp/i) {
    
    die "exp traffic error" if ($interval == 0 || $burst == 0 || $idle == 0);
    my $rate = $packet_size * 8.0 / $interval;

    push @connections, <<END_CON;
# $starttime $from -> $to exp 10000 $packet_size
# $from connecting to $to at time $starttime
set udp_($num_con) [new Agent/UDP]
set null_($num_con) [new Agent/Null]
\$udp_($num_con) set fid_ $fid
\$null_($num_con) set fid_ $fid
\$ns_ attach-agent \$node_($from) \$udp_($num_con)
\$ns_ attach-agent \$node_($to) \$null_($num_con)
\$ns_ connect \$udp_($num_con) \$null_($num_con)
\$udp_($num_con) set packetSize_ $packet_size
set exp_($num_con) [new Application/Traffic/Exponential]
\$exp_($num_con) attach-agent \$udp_($num_con)
\$exp_($num_con) set interval_ $interval
\$exp_($num_con) set burst_time_ $burst
\$exp_($num_con) set idle_time_ $idle
\$exp_($num_con) set packetSize_ $packet_size
\$ns_ at $starttime "\$exp_($num_con) start"
\$ns_ at $stoptime "\$exp_($num_con) stop"

END_CON
    
} else {
    die "unknown type: $type";
}
    # inc flow id
    ++$fid;
}


sub make_movement {
    my ($nr, $time, $dx, $dy, $speed) = @_;
    push @scen, "\$ns_ at $time \"\$node_($nr) setdest $dx $dy $speed\"\n";
}


sub service_area {
    my ($nr, $sx, $sy, $range, $density, $txrange, $priority, $color) = @_;
    my ($x, $y);

    $range = ($range > 0) ? int($range/$density) : 0;
    $node_nr[$nr] = $nn++;
    $cluster[$fa_domain] = 1;
    make_node($node_nr[$nr], $sx, $sy, $txrange, $priority, $color,
	      "$fa_domain.0.0", "FA $fa_domain.0.0", "FA");
    for ($y = -$range; $y <= $range; $y += 1) {
	for ($x = -$range; $x <= $range; $x += 1) {
	    next if ($x == 0 && $y == 0); # skip center (main FA)
	    if (sqrt($x*$x + $y*$y) <= $range) {
		push @falink, "\$ns_ duplex-link \$node_($node_nr[$nr]) \$node_($nn) 100Mb 10ms DropTail\n";
		# use previously set txrange
		make_node($nn++, $x * $density + $sx, $y  * $density + $sy, $txrange,
			  $priority, $color, "$fa_domain.$cluster[$fa_domain].0", "", "FA");
		++$cluster[$fa_domain];
	    }
	}
    }
}


print "generating scenario...\n";
open(INFILE,  "<$ARGV[0]") or die;
while (<INFILE>) {
    s/^\s+//; # remove whitespace

    if (/^size-x\s+([\d\.]+)/) {
	$size_x = int($1);
	next;
    }

    if (/^size-y\s+([\d\.]+)/) {
	$size_y = int($1);
	next;
    }

    if (/^time\s+([\d\.]+)/) {
	$time = int($1);
	next;
    }

    if (/^wired-node\s+(\d+)\s+([\d\.]+)\s+([\d\.]+)/) {
	# nr, x, y

	print "WD($nn)\t1.$cluster[1].0\n";

	$node_nr[$1] = $nn;
	make_node($nn++, $2, $3, 0, 0, $default_color, 
		  "1.$cluster[1].0", "1.$cluster[1].0", "WD");
	++$cluster[1];
	next;
    }

    if (/^home-agent\s+(.*)/) {
	# nr, x, y [,-t txrange, -p priority, -c color]
	my @fields = split(/\s+/, $1);
	my ($txrange, $priority, $color) = ($default_txrange, 0, "grey");
	my $i;

	print "HA($nn)\t0.0.0\n";

	for ($i = 3; $i < @fields; ++$i) {
	    $txrange  = $fields[++$i] if ($fields[$i] eq "-t");
	    $priority = $fields[++$i] if ($fields[$i] eq "-p");
	    $color    = $fields[++$i] if ($fields[$i] eq "-c");
	}

	$node_nr[$fields[0]] = $nn;
	$ha_nr = $nn;
	make_node($nn++, $fields[1], $fields[2], $txrange, $priority, $color,
		  "0.0.0", "HA 0.0.0", "HA");
	$cluster[0] = 1;
	++$mh_node;
	next;
    }

    if (/^mobile-node\s+(\d+)\s+([\d\.]+)\s+([\d\.]+)/) {
	# nr, x, y

	print "MH($nn)\t0.0.$mh_node\n";

	$node_nr[$1] = $nn;
	# hack: MN reaches all nodes!
	push @all_mn_numbers, $nn;
	make_node($nn++, $2, $3, int(sqrt($size_x*$size_x + $size_y*$size_y)) + 1,
		  0, "darkgray", "0.0.$mh_node", "MH 0.0.$mh_node", "MH");
	++$mh_node;
	next;
    }

    if (/^service-area\s+(.*)/) {
	# nr, x, y [,-r range, -d density, -t txrange, -p priority, -c color]
	my @fields = split(/\s+/, $1);
	my ($range, $density, $txrange, $priority, $color) = (0, 0, $default_txrange, 0, $default_color);
	my $i;

	print "SA($nn)\t$fa_domain.0.0\n";

	for ($i = 3; $i < @fields; ++$i) {
	    $range    = $fields[++$i] if ($fields[$i] eq "-r");
	    $density  = $fields[++$i] if ($fields[$i] eq "-d");
	    $txrange  = $fields[++$i] if ($fields[$i] eq "-t");
	    $priority = $fields[++$i] if ($fields[$i] eq "-p");
	    $color    = $fields[++$i] if ($fields[$i] eq "-c");
	}
	service_area($fields[0], $fields[1], $fields[2], $range, $density,
		     $txrange, $priority, $color);
	++$fa_domain;
	next;
    }
    
    if (/^link\s+(\d+)\s+(\d+)\s+(\w+)\s+(\w+)\s+(\w+)/) {
	# from, to, bw, delay, queue
	make_link($node_nr[$1], $node_nr[$2], $3, $4, $5);
	$_ = $3;
	/([\d\.]+)(\w*)/;
	my ($rate, $unit) = ($1, $2);
	$rate *= 1000000 if ($unit =~ /mb/i);
	$rate *= 1000 if ($unit =~ /kb/i);
	$max_rate = $rate if ($max_rate < $rate);
	next;
    }
    
    if (/^movement\s+(\d+)\s+([\d\.]+)\s+([\d\.]+)\s+([\d\.]+)\s+([\d\.]+)/) {
	# nr, time, dest x, dest y, speed
	make_movement($node_nr[$1], $2, $3, $4, $5);
	next;
    }
    
    if (/^connection\s+(\d+)\s+(\d+)\s+([\d\.]+)\s+([\d\.]+)\s+(\w+)\s*([\d\.]*)\s*([\d\.]*)\s*([\d\.]*)\s*([\d\.]*)/) {
	# from, to, start, stop, type(tcp/udp) [, packet size [, interval (sec) [, burst time, idle time]]]
	make_connection($node_nr[$1], $node_nr[$2], $3, $4, $5, $6, $7, $8, $9);
	++$num_con;
	next;
    }
    
    next if (/^\s*$/); # skip blank lines and comments
    next if (/^\#/);
	     
    print "ignored line: $_";
}
close(INFILE);

my $nodes = "$mh_node";
my $i;
for ($i = 0; $i < $nn - $mh_node; ++$i) {
    $nodes .= " 1";
}

open(SIMFILE, ">$sim_file") or die;
print SIMFILE "ns-random $rnd_seed\n" if ($rnd_seed > 0.0);
print SIMFILE <<END_PRINT;

set ns_ [new Simulator]
\$ns_ node-config -addressType hierarchical

AddrParams set domain_num_ $fa_domain
lappend cluster_num @cluster
AddrParams set cluster_num_ \$cluster_num
lappend eilastlevel $nodes
AddrParams set nodes_num_ \$eilastlevel

set tracefd [open output/$basename.tr w]
\$ns_ trace-all \$tracefd

set topo [new Topography]
\$topo load_flatgrid $size_x $size_y
set god_ [create-god $nn]

# wired nodes
END_PRINT
    
print SIMFILE @wired_nodes;

print SIMFILE <<END_PRINT;

set chan_ [new Channel/WirelessChannel]

\$ns_ node-config -mobileIP ON \\
                  -adhocRouting NOAH \\
                  -llType LL \\
                  -macType Mac/802_11 \\
                  -ifqType Queue/DropTail/PriQueue \\
                  -ifqLen 50 \\
                  -antType Antenna/OmniAntenna \\
                  -propType Propagation/SimpleDistance \\
                  -phyType Phy/WirelessPhy \\
                  -channel \$chan_ \\
	 	  -topoInstance \$topo \\
                  -wiredRouting ON \\
		  -agentTrace ON \\
                  -routerTrace OFF \\
                  -macTrace ON

END_PRINT

print SIMFILE "# home agents\n";
print SIMFILE @home_agents;
print SIMFILE "\n\$ns_ node-config -wiredRouting OFF\n\n# mobile agents\n";
print SIMFILE @mobile_nodes;
print SIMFILE "\n\$ns_ node-config -wiredRouting ON\n\n# foreign agents\n";
print SIMFILE @wireless_nodes;

print SIMFILE <<END_PRINT;

# source connection-pattern and node-movement scripts
source "$scen_file"
source "$com_file"

# Tell all nodes when the simulation ends
for {set i 0} {\$i < $nn } {incr i} {
    \$ns_ at $time.0 "\$node_(\$i) reset";
}

# Progress
for {set t $prog_sec} {\$t < $time} {incr t $prog_sec} {
    \$ns_ at \$t "puts stderr \\"completed through \$t/$time secs...\\""
}

\$ns_ at 0.0 "puts stderr \\\"Simulation started...\\\""
\$ns_ at $time.0000 "puts stderr \\\"Simulation finished\\\""
\$ns_ at $time.0001 "close \$tracefd"
\$ns_ at $time.0002 "exec run_plot_tp.sh ${basename}.tr @all_mn_numbers"
\$ns_ at $time.0003 "\$ns_ halt"

puts \$tracefd "M 0.0 nn $nn x $size_x y $size_y rp NOAH"
puts \$tracefd "M 0.0 sc $scen_file cp $com_file seed $rnd_seed"

\$ns_ run

END_PRINT

close(SIMFILE);


open(SCENFILE, ">$scen_file") or die;

print SCENFILE <<END_PRINT;
#
# nodes: $nn, max time: $time, max x: $size_x, max y: $size_y
# nominal range: $default_txrange link bw
#

END_PRINT

print SCENFILE @scen;
print SCENFILE "\n";
print SCENFILE @link;
print SCENFILE "\n";
print SCENFILE @falink;
close(SCENFILE);


open(CONFILE, ">$com_file") or die;

print CONFILE <<END_PRINT;
#
# nodes: $nn, max conn: $num_con, send rate: 0.0, seed: 0.0
#

END_PRINT

print CONFILE @connections;

print CONFILE <<END_PRINT;

#
#Total sources/connections: $num_con/$num_con
#
END_PRINT

close(CONFILE);
