#Author: Mete Rodoper
# For more detail refer the How to code and use Various Mobility Model Scripts to Emulate Mobility at ORBIT tutorial

#!/usr/bin/perl

use Math::Round qw(:all);
use Math::Trig 'rad2deg';

#############################################################################################################
######################################## Enter the values here ##############################################
#############################################################################################################

@sourceXValue = (19, 1, 4); # Source X values of all 3 VMs. 
@sourceYValue = (15, 4, 12);# Source Y values of all 3 VMs. 
			    # e.g.: First node is at (19,15)
@destinationXValue = ([2, 12, 19], [2, 3, 7], [3, 4, 12]); # Checkpoints' X values for 3 mobile nodes
@destinationYValue = ([3, 20, 20], [3, 1, 17], [4, 12, 18]); # Checkpoints' Y values for 3 mobile nodes
$numberOfDestinations = scalar(@destinationXValue); # Number of checkpoints to be visited
$maxIntervalTime = 100; # Maximum waiting time at a node
$minIntervalTime = 10; # Minumum waiting time at a node
@accelerationRandomness = ([25, -5, -2],
			   [12, -4, -4],
			   [32, -12, 3]); # Acceleration values between checkpoints, e.g.: Between VM1 source and 1st checkpoint point acceleration is 25, then -5, last, -2 and same for other VMs.
$randomnessInterval = 2; # Maximum distance that a node can jump at an iteration
$mobileNumber = 3; # Number of VMs

#############################################################################################################
#############################################################################################################
#############################################################################################################

$randomTemp;
$intervalTime = $maxIntervalTime;
$cumulativeTime = 0;

$mobileNumberIterator = 0;

while($mobileNumberIterator < $mobileNumber)
{
$mobileNumberIteratorToPrint = $mobileNumberIterator +1;
print "Mobile node $mobileNumberIterator starts\n";
	$cumulativeTime = 0;
	$cumulativeTimeOld = 0;
	$intervalTime = $maxIntervalTime;

	$randomTemp = int(rand($accelerationRandomness[$mobileNumberIterator][1]));
	if ($intervalTime - $randomTemp >= $minIntervalTime && $intervalTime - $randomTemp <= $maxIntervalTime)
	{
		$intervalTime = $intervalTime - $randomTemp;
	}

	$cumulativeTime = $cumulativeTime + $intervalTime;
	$cumulativeTimeOld = $cumulativeTime-$intervalTime;
	$nodeNumber = $sourceXValue[$mobileNumberIterator] * 100 + $sourceYValue[$mobileNumberIterator];
	if(isUnique($intervalTime , $cumulativeTime, $nodeNumber, @timer))
	{
		$loopIterator = 0;
		while ($loopIterator < $intervalTime)
		{
			push @{$timer{$nodeNumber}}, $cumulativeTimeOld + $loopIterator;
			$loopIterator++;
		}

		#print "$nodeNumber $cumulativeTimeOld - $cumulativeTime\n";
		$nodeNameAndTime = "$cumulativeTimeOld node$sourceXValue[$mobileNumberIterator]-$sourceYValue[$mobileNumberIterator]";
		print "$cumulativeTimeOld node$sourceXValue[$mobileNumberIterator]-$sourceYValue[$mobileNumberIterator]\n";
		push @{$store{$mobileNumberIterator}}, $nodeNameAndTime;
	}
	else
	{
		$cumulativeTime = $cumulativeTime - $intervalTime;
		$cumulativeTimeOld = $cumulativeTime + $intervalTime;
	}

	for($destinationNo = 0 ; $destinationNo < $numberOfDestinations ; $destinationNo++)
	{

		$distance = sqrt(($destinationYValue[$mobileNumberIterator][$destinationNo]-$sourceYValue[$mobileNumberIterator])*($destinationYValue[$mobileNumberIterator][$destinationNo]-$sourceYValue[$mobileNumberIterator]) + ($destinationXValue[$mobileNumberIterator][$destinationNo]-$sourceXValue[$mobileNumberIterator])*($destinationXValue[$mobileNumberIterator][$destinationNo]-$sourceXValue[$mobileNumberIterator]));

		$absdistance = int($distance);

		$angle = atan2(($destinationYValue[$mobileNumberIterator][$destinationNo]-$sourceYValue[$mobileNumberIterator]), ($destinationXValue[$mobileNumberIterator][$destinationNo]-$sourceXValue[$mobileNumberIterator]));
		$angle2 = rad2deg($angle);

		$previousNode = "";
		$k=1;

		while ($k < $absdistance + 1)
		{
			do{
				$pointX = $sourceXValue[$mobileNumberIterator] + $k * cos($angle) + rand($randomnessInterval)-($randomnessInterval)/2;
			}while($pointX > 20);
			do{
				$pointY = $sourceYValue[$mobileNumberIterator] + $k * sin($angle) + rand($randomnessInterval)-($randomnessInterval)/2;
			}while($pointY > 20);
	
			$fractionX = $pointX - int($pointX);
			$fractionY = $pointY - int($pointY);
		
			$firstQuarter = $fractionY * $fractionY + $fractionX * $fractionX;
			$secondQuarter = $fractionY * $fractionY + (1-$fractionX) * (1-$fractionX);
			$thirdQuarter = (1-$fractionY) * (1-$fractionY) + (1-$fractionX) * (1-$fractionX);
			$fourthQuarter = (1-$fractionY) * (1-$fractionY) + $fractionX * $fractionX;
		
			$floorX = int($pointX);
			$floorY = int($pointY);
			$ceilingX = $floorX+1;
			$ceilingY = $floorY+1;
			
			$minDistance = $firstQuarter;
			$min = "1";
			if ($secondQuarter < $minDistance)
			{
				$min = "2";
			        $minDistance = $secondQuarter;
			}
			if ($thirdQuarter < $minDistance)
			{
				$min = "3";
			        $minDistance = $thirdQuarter;
			}
			if ($fourthQuarter < $minDistance)
			{
				$min = "4";
			}
	
			if ($min == "1")
			{

				$nodeName = "node$floorX-$floorY";
				if ($previousNode ne $nodeName)
				{
					$randomTemp = int(rand($accelerationRandomness[$mobileNumberIterator][$destinationNo]));
					if ($intervalTime - $randomTemp >= $minIntervalTime && $intervalTime - $randomTemp <= $maxIntervalTime)
					{
						$intervalTime = $intervalTime - $randomTemp;
					}
					$cumulativeTime = $cumulativeTime + $intervalTime;
					$cumulativeTimeOld = $cumulativeTime-$intervalTime;
					$nodeNumber = $floorX * 100 + $floorY;

					if(isUnique($intervalTime, $cumulativeTime, $nodeNumber, @timer))
					{
						$loopIterator = 0;
						while ($loopIterator < $intervalTime)
						{
							push @{$timer{$nodeNumber}}, $cumulativeTimeOld + $loopIterator;
							$loopIterator++;
						}

						$nodeNameAndTime = "$cumulativeTimeOld node$floorX-$floorY";
						print "$cumulativeTimeOld node$floorX-$floorY\n";
						push @{$store{$mobileNumberIterator}}, $nodeNameAndTime;
						$previousNode = $nodeName;
					}
					else
					{
						$cumulativeTime = $cumulativeTime - $intervalTime;
						$cumulativeTimeOld = $cumulativeTime + $intervalTime;
						$k--;
					}
				}
			}
			elsif ($min == "2")
			{
				$nodeName = "node$ceilingX-$floorY";
				if ($previousNode ne $nodeName)
				{
					$randomTemp = int(rand($accelerationRandomness[$mobileNumberIterator][$destinationNo]));
					if ($intervalTime - $randomTemp >= $minIntervalTime && $intervalTime - $randomTemp <= $maxIntervalTime)
					{
						$intervalTime = $intervalTime - $randomTemp;
					}
					$cumulativeTime = $cumulativeTime + $intervalTime;
					$cumulativeTimeOld = $cumulativeTime-$intervalTime;
					$nodeNumber = $ceilingX * 100 + $floorY;

					if(isUnique($intervalTime , $cumulativeTime, $nodeNumber, @timer))
					{
						$loopIterator = 0;
						while ($loopIterator < $intervalTime)
						{
							push @{$timer{$nodeNumber}}, $cumulativeTimeOld + $loopIterator;
							$loopIterator++;
						}

						$nodeNameAndTime = "$cumulativeTimeOld node$ceilingX-$floorY";
						print "$cumulativeTimeOld node$ceilingX-$floorY\n";
						push @{$store{$mobileNumberIterator}}, $nodeNameAndTime;
						$previousNode = $nodeName;
					}
					else
					{
						$cumulativeTime = $cumulativeTime - $intervalTime;
						$cumulativeTimeOld = $cumulativeTime + $intervalTime;
						$k--;
					}
				}
			}
			elsif ($min == "3")
			{
				$nodeName = "node$ceilingX-$ceilingY";
				if ($previousNode ne $nodeName)
				{
					$randomTemp = int(rand($accelerationRandomness[$mobileNumberIterator][$destinationNo]));
					if ($intervalTime - $randomTemp >= $minIntervalTime && $intervalTime - $randomTemp <= $maxIntervalTime)
					{
						$intervalTime = $intervalTime - $randomTemp;
					}
					$cumulativeTime = $cumulativeTime + $intervalTime;
					$cumulativeTimeOld = $cumulativeTime-$intervalTime;
					$nodeNumber = $ceilingX * 100 + $ceilingY;
					if(isUnique($intervalTime , $cumulativeTime, $nodeNumber, @timer))
					{
						$loopIterator = 0;
						while ($loopIterator < $intervalTime)
						{
							push @{$timer{$nodeNumber}}, $cumulativeTimeOld + $loopIterator;
							$loopIterator++;
						}
	
						$nodeNameAndTime = "$cumulativeTimeOld node$ceilingX-$ceilingY";
						print "$cumulativeTimeOld node$ceilingX-$ceilingY\n";
						push @{$store{$mobileNumberIterator}}, $nodeNameAndTime;
						$previousNode = $nodeName;
					}
					else
					{
						$cumulativeTime = $cumulativeTime - $intervalTime;
						$cumulativeTimeOld = $cumulativeTime + $intervalTime;
						$k--;
					}
				}
			}
			elsif ($min == "4")
			{
				$nodeName = "node$floorX-$ceilingY";
				if ($previousNode ne $nodeName)
				{	
					$randomTemp = int(rand($accelerationRandomness[$mobileNumberIterator][$destinationNo]));
					if ($intervalTime - $randomTemp >= $minIntervalTime && $intervalTime - $randomTemp <= $maxIntervalTime)
					{
						$intervalTime = $intervalTime - $randomTemp;
					}
					$cumulativeTime = $cumulativeTime + $intervalTime;
					$cumulativeTimeOld = $cumulativeTime-$intervalTime;
					$nodeNumber = $floorX * 100 + $ceilingY;
					if(isUnique($intervalTime , $cumulativeTime, $nodeNumber, @timer))
					{
						$loopIterator = 0;
						while ($loopIterator < $intervalTime)
						{
							push @{$timer{$nodeNumber}}, $cumulativeTimeOld + $loopIterator;
							$loopIterator++;
						}

						$nodeNameAndTime = "$cumulativeTimeOld node$floorX-$ceilingY";
						print "$cumulativeTimeOld node$floorX-$ceilingY\n";
						push @{$store{$mobileNumberIterator}}, $nodeNameAndTime;
						$previousNode = $nodeName;
					}
					else
					{
						$cumulativeTime = $cumulativeTime - $intervalTime;
						$cumulativeTimeOld = $cumulativeTime + $intervalTime;
						$k--;
					}
				}
			}
		$k++;
		}

		$nodeName = "node$destinationXValue[$mobileNumberIterator][$destinationNo]-$destinationYValu[$mobileNumberIterator]e[$destinationNo]";
		if ($previousNode ne $nodeName)
		{	
			$randomTemp = int(rand($accelerationRandomness[$mobileNumberIterator][$destinationNo]));
			if ($intervalTime - $randomTemp >= $minIntervalTime && $intervalTime - $randomTemp <= $maxIntervalTime)
			{
				$intervalTime = $intervalTime - $randomTemp;
			}
			$cumulativeTime = $cumulativeTime + $intervalTime;
			$cumulativeTimeOld = $cumulativeTime-$intervalTime;
			$nodeNumber = $destinationXValue[$mobileNumberIterator][$destinationNo] * 100 + $destinationYValue[$mobileNumberIterator][$destinationNo];
			if(isUnique($intervalTime , $cumulativeTime, $nodeNumber, @timer))
			{
				$loopIterator = 0;
				while ($loopIterator < $intervalTime)
				{
					push @{$timer{$nodeNumber}}, $cumulativeTimeOld + $loopIterator;
					$loopIterator++;
				}
	
				$nodeNameAndTime = "$cumulativeTimeOld node$destinationXValue[$mobileNumberIterator][$destinationNo]-$destinationYValue[$mobileNumberIterator][$destinationNo]";
				print "$cumulativeTimeOld node$destinationXValue[$mobileNumberIterator][$destinationNo]-$destinationYValue[$mobileNumberIterator][$destinationNo]\n";
				push @{$store{$mobileNumberIterator}}, $nodeNameAndTime;
				$previousNode = $nodeName;
			}
			else
			{
				$cumulativeTime = $cumulativeTime - $intervalTime;
				$cumulativeTimeOld = $cumulativeTime + $intervalTime;
			}
		}

	$destinationNoToPrint = $destinationNo +1;
	print "Destination No : $destinationNoToPrint is Reached for Mobile Node Number : $mobileNumberIteratorToPrint\n";	

	$sourceXValue[$mobileNumberIterator] = $destinationXValue[$mobileNumberIterator][$destinationNo];
	$sourceYValue[$mobileNumberIterator]= $destinationYValue[$mobileNumberIterator][$destinationNo];
	}
$destinationNo = 0;
$mobileNumberIterator++;
print "\n";
}

$j = 0;

$mobileNumberIterator--;
$printNode = $mobileNumberIterator + 1;

while($mobileNumberIterator > -1)
{
	while (${$store{$mobileNumberIterator}}[$j] ne ())
	{
		$tempWrite = ${$store{$mobileNumberIterator}}[$j];
		system("echo $tempWrite >> VM$printNode.txt");
		$j = $j + 1;
	}
	$mobileNumberIterator--;
	$printNode--;
	$j = 0;
}

sub isUnique
{
	$intervalTime = $_[0];
	$cumulativeTime = $_[1];
	$nodeNumber = $_[2];
	@timer = @_[3];
	
	$cumulativeTimeOld = $cumulativeTime - $intervalTime;
	$loopIterator = 0;

	while ($loopIterator < $intervalTime)
	{	
		while (${$timer{$nodeNumber}}[$j] ne ())
		{
			if (${$timer{$nodeNumber}}[$j] == $cumulativeTimeOld + $loopIterator)
			{
				print "ERROR ${$timer{$nodeNumber}}[$j]\n";
				return 0;
			}
			$j = $j + 1;
		}
		$loopIterator++;
		$j = 0;
	}
	return 1;
}
