/*
 * VoWifi.cc
 *
 *  Created on: Feb 19, 2015
 *      Author: shujaansari
 */


// Network Topology
//           AP
//     *
//                  *
// *:VoIP nodes
//

#include "ns3/flow-monitor.h"
#include "ns3/flow-monitor-module.h"
#include "ns3/flow-monitor-helper.h"
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/applications-module.h"
#include "ns3/wifi-module.h"
#include "ns3/mobility-module.h"
#include "ns3/internet-module.h"
#include "ns3/object.h"
#include "ns3/uinteger.h"
#include "ns3/traced-value.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/random-variable-stream.h"
#include "ns3/type-id.h"
#include "ns3/object-base.h"
#include "ns3/scheduler.h"
#include "ns3/calendar-scheduler.h"
#include "ns3/callback.h"
#include "ns3/netanim-module.h"
#include "ns3/trace-helper.h"
#include "ns3/gnuplot.h"


#include <fstream>
#include <iostream>

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

void ThroughputMonitor (ns3::FlowMonitorHelper *fmhelper, ns3::Ptr<FlowMonitor> flowMon, Gnuplot2dDataset *dataset, double Throughput, double xtime);

void CourseChange (std::string context, Ptr<const MobilityModel> model)
    {
      Vector position = model->GetPosition ();
      NS_LOG_UNCOND (context <<
                     " x = " <<
					 position.x <<
					 ", y = " <<
					 position.y);
	}

void SetTagTid ( uint8_t mytid ,Ptr<const Packet> pkt )
	{
	  QosTag  qosTag ;
	  qosTag.SetTid (mytid);
	  pkt->AddPacketTag ( qosTag );
	}
/*
void SetPosition (Ptr<Node> node, Vector position)
	{
	  Ptr<MobilityModel> mobility = node->GetObject<MobilityModel> ();
	  mobility->SetPosition (position);
	}
*/

int main (int argc, char *argv[])
{
	Time::SetResolution (Time::NS);
	LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
	LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);

	NodeContainer wifiApNode;
	wifiApNode.Create(1);
	NodeContainer wifiStaNodes;
	wifiStaNodes.Create(2);

	// Create & setup wifi channel
	YansWifiChannelHelper channel = YansWifiChannelHelper::Default ();
	YansWifiPhyHelper phy = YansWifiPhyHelper::Default ();
	phy.SetChannel (channel.Create ());

	// Install wireless devices
	WifiHelper wifi = WifiHelper::Default ();
	wifi.SetRemoteStationManager ("ns3::ArfWifiManager");

	QosWifiMacHelper mac = QosWifiMacHelper::Default ();

	Ssid ssid = Ssid ("ns-3-ssid");
	mac.SetType ("ns3::ApWifiMac", "Ssid", SsidValue (ssid));

	NetDeviceContainer apDevices;
	apDevices = wifi.Install (phy, mac, wifiApNode);

	mac.SetType ("ns3::StaWifiMac", "Ssid", SsidValue (ssid), "ActiveProbing", BooleanValue (true));

	NetDeviceContainer staDevices;
	staDevices = wifi.Install (phy, mac, wifiStaNodes);

	MobilityHelper mobility;

	mobility.SetPositionAllocator ("ns3::GridPositionAllocator",
	                                 "MinX", DoubleValue (0.0),
	                                 "MinY", DoubleValue (5.0),
	                                 "DeltaX", DoubleValue (5.0),
	                                 "DeltaY", DoubleValue (10.0),
	                                 "GridWidth", UintegerValue (3),
	                                 "LayoutType", StringValue ("RowFirst"));

	mobility.SetMobilityModel ("ns3::RandomWalk2dMobilityModel",
	                             "Bounds", RectangleValue (Rectangle (-50, 50, -50, 50)));
	mobility.Install (wifiStaNodes);

	mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
	mobility.Install (wifiApNode);

	InternetStackHelper stack;
	stack.Install (wifiApNode);
	stack.Install (wifiStaNodes);

	Ipv4AddressHelper address;
	NS_LOG_INFO ("Assign IP Addresses.");
	address.SetBase ("10.1.3.0", "255.255.255.0");
	Ipv4InterfaceContainer wifiApInterfaces;
	wifiApInterfaces = address.Assign (apDevices);
	Ipv4InterfaceContainer wifiInterfaces;
	wifiInterfaces = address.Assign (staDevices);

	
	
	//Installing applications
	
	
	
	
	//CBR = VoIP ==============================
		//first VoIP traffic
	  // VoIP receiver---
	Address remoteAddress1 = InetSocketAddress(wifiInterfaces.GetAddress (0), 4000);
	PacketSinkHelper receiver1 ("ns3::UdpSocketFactory", remoteAddress1);
	ApplicationContainer receiverapp1 =receiver1.Install(wifiStaNodes.Get (0));
	receiverapp1.Start (Seconds (5.0));
	receiverapp1.Stop (Seconds (8.0));

	// VoIP receiver AP
	Address remoteAddress2 = InetSocketAddress(wifiApInterfaces.GetAddress (0), 4000);
	PacketSinkHelper receiver2 ("ns3::UdpSocketFactory", remoteAddress2);
	ApplicationContainer receiverapp2 =receiver2.Install(wifiApNode.Get(0));
	receiverapp2.Start (Seconds (2.0));
	receiverapp2.Stop (Seconds(5.0));
	
	
	  // VoIP Sender ----
	OnOffHelper sender2("ns3::UdpSocketFactory", remoteAddress2);
	sender2.SetConstantRate( DataRate("68.8Kbps") , 172);	// 160 byte(G.711) + 12 byte (RTP header) = 172 byte/ for 20ms packet generation intervall is 68.8kbps
	sender2.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));
	sender2.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]"));
	ApplicationContainer senderapps2;
	senderapps2  = sender2.Install(wifiStaNodes.Get(1));
	Config::ConnectWithoutContext("/NodeList/1/ApplicationList/3/$ns3::OnOffApplication/Rx",  MakeCallback (&SetTagTid));
	Ptr<OnOffApplication> onoffappVO1;
	onoffappVO1 = DynamicCast<OnOffApplication>(senderapps2.Get(0));
	onoffappVO1->TraceConnectWithoutContext("Tx",  MakeBoundCallback (&SetTagTid, AC_VO));
	senderapps2.Start(Seconds(2.0));
	senderapps2.Stop(Seconds(5.0));
	
	
	OnOffHelper sender1("ns3::UdpSocketFactory", remoteAddress1);
	sender1.SetConstantRate( DataRate("68.8Kbps") , 172);	// 160 byte(G.711) + 12 byte (RTP header) = 172 byte/ for 20ms packet generation intervall is 68.8kbps
	sender1.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));
	sender1.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]"));
	ApplicationContainer senderapps1;
	senderapps1  = sender1.Install(wifiStaNodes.Get(1));
	Config::ConnectWithoutContext("/NodeList/0/ApplicationList/3/$ns3::OnOffApplication/Rx",  MakeCallback (&SetTagTid));
	Ptr<OnOffApplication> onoffappVO;
	onoffappVO = DynamicCast<OnOffApplication>(senderapps1.Get(0));
	onoffappVO->TraceConnectWithoutContext("Tx",  MakeBoundCallback (&SetTagTid, AC_VO));
	senderapps1.Start(Seconds(5.0));
	senderapps1.Stop(Seconds(8.0));


	Ipv4GlobalRoutingHelper::PopulateRoutingTables ();


	std::string fileName = "FlowVSThroughput";
	std::string graphicsFileName = fileName + ".png";
	std::string plotFileName = fileName + ".plt";
	std::string plotTitle = "Flow Vs Throughput";
	std::string dataTitle = "Throughput";

	Gnuplot gnuplot (graphicsFileName);
	gnuplot.SetTitle(plotTitle);
	gnuplot.SetTerminal("png");
	gnuplot.SetLegend("Flow","Throughput");

	Gnuplot2dDataset dataset;
	dataset.SetTitle ("Throughput");
	dataset.SetStyle (Gnuplot2dDataset::LINES_POINTS);

	double Throughput=0;
	double xtime=0;

	FlowMonitorHelper flowmon;
	Ptr<FlowMonitor> monitor = flowmon.InstallAll();
	Simulator::Schedule(Seconds(3),&ThroughputMonitor,&flowmon, monitor, &dataset, Throughput, xtime);
	NS_LOG_INFO("Run Simulation");

	Simulator::Stop (Seconds (10.0));

	  //output================

	  phy.EnablePcap ("VoWifi", apDevices);
	  phy.EnablePcap ("VoWifi" , staDevices);
	  phy.EnableAscii ("VoWifi" , apDevices);
	  phy.EnableAscii ("VoWifi" , staDevices);
	  stack.EnableAsciiIpv4 ("VoWifi" , wifiApInterfaces);			//output tr file for trace of internet stack
	  stack.EnableAsciiIpv4 ("VoWifi" , wifiInterfaces);		//output tr file for trace of internet stack

	AnimationInterface anim("VoWifi.xml");

	Simulator::Run ();
	NS_LOG_UNCOND("Flow Monitor Statistics: ");
	monitor->CheckForLostPackets();

	ThroughputMonitor(&flowmon, monitor, &dataset, Throughput, xtime);
	/*
	Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier> (flowmon.GetClassifier());
	std::map<FlowId, FlowMonitor::FlowStats> stats = monitor->GetFlowStats();
	double Throughput=0.0;

	for (std::map<FlowId, FlowMonitor::FlowStats>::const_iterator iter = stats.begin(); iter != stats.end (); ++iter)
	{
		  Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(iter->first);

		  NS_LOG_UNCOND ("Flow IF: " << iter->first << "  Src Addr "<<t.sourceAddress<<"Dst Addr "<<t.destinationAddress);
		  NS_LOG_UNCOND ("Tx Pkts= " << iter->second.txPackets);
		  NS_LOG_UNCOND ("Rx Pkts= " << iter->second.rxPackets);
		  Throughput= iter->second.rxBytes * 8.0 / (iter->second.timeLastRxPacket.GetSeconds()-iter->second.timeFirstTxPacket.GetSeconds())/1024;
		  NS_LOG_UNCOND ("Throughput: " << Throughput <<"Kbps");
		  dataset.Add((double)iter->first ,(double) Throughput);
	}

	*/
	NS_LOG_UNCOND("Done");
/*
	std::map<FlowId,FlowMonitor::FlowStats>::const_iterator iter2
	for (uint32_t x=0; x==8; x++)
	{

		Throughput= iter2->second.rxBytes * 8.0 / (iter2->second.timeLastRxPacket.GetSeconds()-iter->second.timeFirstTxPacket.GetSeconds())/1024;
				  NS_LOG_UNCOND ("Throughput: " << Throughput <<"Kbps");
				  dataset.Add((double)x ,(double) Throughput);
	}
	NS_LOG_UNCOND("Done");
*/

	gnuplot.AddDataset(dataset);
	std::ofstream plotFile (plotFileName.c_str());
	gnuplot.GenerateOutput(plotFile);

	plotFile.close();

	monitor->SerializeToXmlFile("VoWifi.flowmon", true, true);
	Simulator::Destroy ();
	return 0;

}

void ThroughputMonitor (ns3::FlowMonitorHelper *fmhelper, ns3::Ptr<FlowMonitor> flowMon, Gnuplot2dDataset *dataset, double Throughput, double xtime)
	{

		std::map<FlowId, FlowMonitor::FlowStats> flowStats = flowMon->GetFlowStats();
		Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier> (fmhelper->GetClassifier());
		for (std::map<FlowId, FlowMonitor::FlowStats>::const_iterator iter = flowStats.begin (); iter != flowStats.end (); ++iter)
		{
			Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(iter->first);

					  NS_LOG_UNCOND ("Flow IF: " << iter->first << "  Src Addr "<<t.sourceAddress<<"Dst Addr "<<t.destinationAddress);
					  NS_LOG_UNCOND ("Tx Pkts= " << iter->second.txPackets);
					  NS_LOG_UNCOND ("Rx Pkts= " << iter->second.rxPackets);
					  NS_LOG_UNCOND ("Duration		: "<<iter->second.timeLastRxPacket.GetSeconds()-iter->second.timeFirstTxPacket.GetSeconds());
					  Throughput= iter->second.rxBytes * 8.0 / (iter->second.timeLastRxPacket.GetSeconds()-iter->second.timeFirstTxPacket.GetSeconds())/1024;
					  NS_LOG_UNCOND ("Throughput: " << Throughput <<"Kbps");
					  dataset->Add((double)xtime ,(double) Throughput);
					  xtime++;

		}
			Simulator::Schedule(Seconds(1),&ThroughputMonitor, fmhelper, flowMon, dataset, Throughput, xtime);


		}
