#include "ns3/aodv-module.h"
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/mobility-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/wifi-module.h"
#include "ns3/v4ping-helper.h"
#include "ns3/netanim-module.h"
#include "ns3/applications-module.h"
#include "ns3/flow-monitor-helper.h"
#include "ns3/gnuplot.h"
#include "ns3/flow-monitor-module.h"
#include "ns3/nstime.h"
#include "ns3/simulator.h"
#include "ns3/stats-module.h"
#include <iostream>
#include <cmath>
#include <fstream>
#include <string>
#include <cassert>

using namespace ns3;
using namespace std;

uint32_t size = 7;				// Počet uzlů
double dist = 50;				// Vzdálenost mezi uzly v metrech
double range = 60;				//dosah rádia (uzlu)
double totalTime = 70;			// Čas simulace v sekundách
double speed = 5;			//rychlost v km/h
double max_rychlost = 100;
bool pcap = true;				// Záznam provozu v pcap
bool printRoutes = true;		// Záznam routovacích tabulek
bool nahoru = true;				//posun po ose x

	//--------------graf pro zpoždeni----------------------------------------------
	string jmenoZpozdeni = "Zpozdeni";
	string zpozdeniGraphicsFileName = jmenoZpozdeni + ".png";
	string zpozdeniFileName = jmenoZpozdeni + ".plt";
	string zpozdeniTitle = "Velikost zpoždění";
	string zpozdeniOsa_Y = "Průměrné zpoždění [ms]";
	string zpozdeniOsa_X = "čas [s]";
	
	//Vytvoření instance gnuplot a názvu
	Gnuplot gnuplotZP(zpozdeniGraphicsFileName);
	Gnuplot2dDataset delay;
	//-----------------------------------------------------------------------------

	//--------------graf pro ztátu paketů------------------------------------------
	string jmenoLost = "Ztratovost";
	string lostGraphicsFileName = jmenoLost + ".png";
	string lostFileName = jmenoLost + ".plt";
	string lostTitle = "Ztrátovost";
	string lostOsa_Y = "Počet ztracených paketů";
	string lostOsa_X = "čas [s]";

	//Vytvoření instance gnuplot a názvu
	Gnuplot gnuplotZT(lostGraphicsFileName);
	Gnuplot2dDataset lostP;
	//-----------------------------------------------------------------------------

	//------------rx/tx bitrate----------------------------------------------------
	string jmenoBitR = "BitRate";
	string bitRGraphicsFileName = jmenoBitR + ".png";
	string bitRFileName = jmenoBitR + ".plt";
	string bitRTitle = "Bitrate";
	string rxTitle = "rxRate";
	string txTitle = "txRate";
	string bitROsa_Y = "Propustnost [Kbps]";
	string bitROsa_X = "čas [s]";

	Gnuplot gnuplotBR(bitRGraphicsFileName);
	Gnuplot2dDataset rxR;
	Gnuplot2dDataset txR;

	double lastLost = 0;
	double lastTime = 0;
	//-----------------------------------------------------------------------------

//--------------funkce pro výpis statistik linky mezi serverem a klientem----------



void Stats(Ptr<FlowMonitor> flowMonitor) {

	flowMonitor->CheckForLostPackets();

	std::map<FlowId, FlowMonitor::FlowStats> stats =
			flowMonitor->GetFlowStats();
	for (std::map<FlowId, FlowMonitor::FlowStats>::const_iterator i =
			stats.begin(); i != stats.end(); ++i) {

		if (i->first == 2) {

			lostP.Add(Simulator::Now().GetSeconds(),
					i->second.lostPackets - lastLost);
			lastLost = i->second.lostPackets;

			delay.Add(Simulator::Now().GetSeconds() - 1,
					(i->second.lastDelay.GetMilliSeconds()));

			rxR.Add(Simulator::Now().GetSeconds() - 1,
					i->second.rxBytes * 8.0
							/ (i->second.timeLastRxPacket.GetSeconds()
									- i->second.timeFirstTxPacket.GetSeconds()) / 1024);

			txR.Add(Simulator::Now().GetSeconds() - 1,
					i->second.txBytes * 8.0
							/ (i->second.timeLastTxPacket.GetSeconds()
									- i->second.timeFirstTxPacket.GetSeconds()) / 1024);

			flowMonitor->SerializeToXmlFile("AODVFlowmon", true, true);
		}

	}
}
//---------------------------------------------------------------------------------

//-----------funkce pro posun ulzu------------------------------------------------- 
void CheckPos(Ptr<ConstantVelocityMobilityModel> mob) {
	Vector m_position = mob->GetPosition();
	int flor = floor(size / 2);
	int x_max = ((flor - 1) * dist) - 1;
	double vector = speed / 3.6;						//rychlost pohybu uzlu v m/s

/*
	 //------postupné zvyšování rychlosti (rychlost -> max_rychlost)---------------
	 if(rychlost<=max_rychlost){						//zvyšující se rychlost pohybu
	 rychlost = rychlost + (max_rychlost/totalTime);
	 }else {
	 vector = vector + 0;
	 }
	 */

	if (m_position.x < vector - 1) {
		nahoru = true;
	}

	if (m_position.x >= x_max) {
		nahoru = false;
	}

	if (nahoru == true) {
		mob->SetVelocity(Vector(vector, 0, 0));
		//std::cout << "posit " << m_position << "...\n";
	} else {
		mob->SetVelocity(Vector(-vector, 0, 0));
		//std::cout << "posit " << m_position << "...\n";
	}
}
//---------------------------------------------------------------------------------


int main(int argc, char *argv[]){ 				// Deklarace funkce main
		

	std::string animFile = "AODV_aminace.xml"; 	// Název souboru pro animaci

	NodeContainer nodes;						//vytvoření uzlů
	nodes.Create(size);

	//---------------mobilita jednoho bodu-----------------------------------------
	MobilityHelper mobility;
	mobility.SetPositionAllocator("ns3::GridPositionAllocator", "MinX",
			DoubleValue(0), "MinY", DoubleValue(0), "DeltaX", DoubleValue(dist),
			"DeltaY", DoubleValue(dist), "GridWidth", UintegerValue(size / 2),
			"LayoutType", StringValue("RowFirst"));
	mobility.SetMobilityModel("ns3::ConstantVelocityMobilityModel");
	mobility.Install(nodes);

	Ptr<ConstantVelocityMobilityModel> mob = nodes.Get(size - 1)->GetObject<
			ConstantVelocityMobilityModel>();

	for (int t = 1; t <= totalTime; t++) {

		Simulator::Schedule(Seconds(t), CheckPos, mob);
	}
	//-----------------------------------------------------------------------------

/*
	//---------------mobilita v řadě-----------------------------------------------
	 MobilityHelper mobility;
	 mobility.SetPositionAllocator ("ns3::GridPositionAllocator",
	 "MinX", DoubleValue (0.0),
	 "MinY", DoubleValue (0.0),
	 "DeltaX", DoubleValue (dist),
	 "DeltaY", DoubleValue (dist),
	 "GridWidth", UintegerValue (size),
	 "LayoutType", StringValue ("RowFirst"));
	 mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
	 mobility.Install (nodes);
	 //----------------------------------------------------------------------------
*/
/*	//--------------mobilita v obdélníku-------------------------------------------
	MobilityHelper mobility;
	 mobility.SetPositionAllocator("ns3::GridPositionAllocator", "MinX",
	 DoubleValue(0), "MinY", DoubleValue(0), "DeltaX", DoubleValue(dist),
	 "DeltaY", DoubleValue(dist), "GridWidth", UintegerValue(size / 4),
	 "LayoutType", StringValue("RowFirst"));
	 mobility.SetMobilityModel("ns3::ConstantVelocityMobilityModel");
	 mobility.Install(nodes);
	 //----------------------------------------------------------------------------
*/
/*
	 //--------náhodná mobilita, počátek ve tvaru obdelniku------------------------
	 //doporučuji nastavit (range = 100)
	 MobilityHelper mobility;
	 mobility.SetPositionAllocator ("ns3::GridPositionAllocator",
	 "MinX", DoubleValue (0.0),
	 "MinY", DoubleValue (0.0),
	 "DeltaX", DoubleValue (dist),
	 "DeltaY", DoubleValue (dist),
	 "GridWidth", UintegerValue (size/4),
	 "LayoutType", StringValue ("RowFirst"));
	 //	 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
	 mobility.SetMobilityModel ("ns3::RandomWalk2dMobilityModel",
	 "Mode", StringValue ("Distance"),
	 "Distance", DoubleValue (10.0),
	 "Direction", StringValue ("ns3::UniformRandomVariable[Min=0.0|Max=6.283184]"),
	 "Time", TimeValue (Seconds (1.0)),
	 "Speed", StringValue ("ns3::ConstantRandomVariable[Constant=2.0]"),
	 "Bounds",RectangleValue(Rectangle (-10, 500, -10, 500)));
	 mobility.Install(nodes);
	 //----------------------------------------------------------------------------
*/
/*
	//-------------náhodná mobilita, náhodná topologie
	//doporučuji nastavit (node = 20, parametry mobilyty lokálně)
	MobilityHelper mobility;
	 mobility.SetPositionAllocator("ns3::RandomRectanglePositionAllocator", "X",
	 StringValue("ns3::UniformRandomVariable[Min=0.0|Max=200.0]"), "Y",
	 StringValue("ns3::UniformRandomVariable[Min=0.0|Max=200.0]"));

	 mobility.SetMobilityModel("ns3::RandomWalk2dMobilityModel", "Mode",
	 StringValue("Distance"), "Distance", DoubleValue(10.0), "Direction",
	 StringValue("ns3::UniformRandomVariable[Min=0.0|Max=6.283184]"),
	 "Time", TimeValue(Seconds(1.0)), "Speed",
	 StringValue("ns3::ConstantRandomVariable[Constant=2.0]"), "Bounds",
	 RectangleValue(Rectangle(0, 200, 0, 200)));
	 mobility.Install(nodes);
	 //----------------------------------------------------------------------------
*/
	//---------definice kanálu-----------------------------------------------------
	QosWifiMacHelper wifiMac = QosWifiMacHelper::Default();
	wifiMac.SetType("ns3::AdhocWifiMac");

	YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default();
	YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default();
	wifiChannel.AddPropagationLoss("ns3::RangePropagationLossModel", "MaxRange",
			DoubleValue(range));
	wifiPhy.SetChannel(wifiChannel.Create());

	WifiHelper wifi = WifiHelper::Default();
	wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager", "DataMode",
			StringValue("OfdmRate6Mbps"));

	NetDeviceContainer devices;
	devices = wifi.Install(wifiPhy, wifiMac, nodes);
	//-----------------------------------------------------------------------------

	//------------konfigurace AODV-------------------------------------------------
	AodvHelper aodv;
	InternetStackHelper stack;
	stack.SetRoutingHelper(aodv);
	stack.Install(nodes);

	Ipv4AddressHelper address;
	address.SetBase("10.10.0.0", "255.255.0.0");
	Ipv4InterfaceContainer interfaces;
	interfaces = address.Assign(devices);
	//-----------------------------------------------------------------------------

	//-----------ping--------------------------------------------------------------
	V4PingHelper ping(interfaces.GetAddress(size - 1));
	ping.SetAttribute("Verbose", BooleanValue(true));
	ApplicationContainer p = ping.Install(nodes.Get(0));
	p.Start(Seconds(2));
	p.Stop(Seconds(totalTime));
	//-----------------------------------------------------------------------------

/*
	//-------------------------aplikace HTTP---------------------------------------
	// Vytvoření On/Off aplikace využívající TCP protokol
	Ptr<Node> appSource = nodes.Get(0);
	Ptr<Node> appSink = nodes.Get(size - 1);

	Ipv4Address remoteAddr =
			appSink->GetObject<Ipv4>()->GetAddress(1, 0).GetLocal();

	OnOffHelper onoffTCP("ns3::TcpSocketFactory",
			Address(InetSocketAddress(remoteAddr, 80)));

	onoffTCP.SetAttribute("DataRate", StringValue("0.2Mbps"));
	onoffTCP.SetAttribute("PacketSize", UintegerValue(512));
	onoffTCP.SetAttribute("OnTime",
			StringValue("ns3::ConstantRandomVariable[Constant=1]"));
	onoffTCP.SetAttribute("OffTime",
			StringValue("ns3::ConstantRandomVariable[Constant=0]"));

	ApplicationContainer appTCP = onoffTCP.Install(appSource);
	appTCP.Start(Seconds(5.0));
	//appTCP.Stop(Seconds(totalTime));

	// Vytvoření příjemce paketů
	PacketSinkHelper sinkTCP("ns3::TcpSocketFactory",
			Address(InetSocketAddress(Ipv4Address::GetAny(), 80)));
	appTCP = sinkTCP.Install(appSink);
	appTCP.Start(Seconds(4.0));
	//appTCP.Stop(Seconds(totalTime));
	//-----------------------------------------------------------------------------
*/
/*
	//--------------------------------aplikace SIP (UDP)---------------------------
	 Ptr<Node> appSource2 = nodes.Get(0);
	 Ptr<Node> appSink2 = nodes.Get(size-1);

	 Ipv4Address remoteAddr2 =
	 appSink2->GetObject<Ipv4>()->GetAddress(1, 0).GetLocal();


	 OnOffHelper onoffUDP("ns3::UdpSocketFactory",
	 Address(InetSocketAddress(remoteAddr2, 5060)));

	 onoffUDP.SetAttribute("DataRate", StringValue("0.2Mbps"));
	 onoffUDP.SetAttribute("PacketSize", UintegerValue(1024));
	 onoffUDP.SetAttribute("OnTime",
	 StringValue("ns3::ConstantRandomVariable[Constant=1]"));
	 onoffUDP.SetAttribute("OffTime",
	 StringValue("ns3::ConstantRandomVariable[Constant=0]"));

	 ApplicationContainer appUDP = onoffUDP.Install(appSource2);
	 appUDP.Start(Seconds(5.0));
	 appUDP.Stop(Seconds(totalTime-5));

	 PacketSinkHelper sinkUDP("ns3::UdpSocketFactory",
	 Address(InetSocketAddress(Ipv4Address::GetAny(), 5060)));
	 appUDP = sinkUDP.Install(appSink2);
	 appUDP.Start(Seconds(4.0));
	 appUDP.Stop(Seconds(totalTime));
	//-----------------------------------------------------------------------------
*/
/*
	//----------------------------------Echo UDP-----------------------------------
	 Ptr<Node> appSource3 = nodes.Get(0);
	 Ptr<Node> appSink3 = nodes.Get(size-1);

	 Ipv4Address remoteAddr3 =
	 appSink3->GetObject<Ipv4>()->GetAddress(1, 0).GetLocal();

	 UdpEchoServerHelper echoServer(80); // Definuje číslo portu, na který se dotazuje client (port serveru)

	 ApplicationContainer serverApps = echoServer.Install(appSource3); // Definuje uzel, na kterém je server
	 serverApps.Start(Seconds(3.0));
	 serverApps.Stop(Seconds(totalTime));

	 UdpEchoClientHelper echoClient(remoteAddr3, 80); // Definuje uzel, na kterém je client
	 echoClient.SetAttribute("MaxPackets", UintegerValue(7));
	 echoClient.SetAttribute("Interval", TimeValue(Seconds(1.0)));
	 echoClient.SetAttribute("PacketSize", UintegerValue(1024));

	 ApplicationContainer clientApps = echoClient.Install(appSink3);
	 clientApps.Start(Seconds(3.0));
	 clientApps.Stop(Seconds(totalTime));
	//-----------------------------------------------------------------------------
*/
	//-------------------PCAP a routovací tabulky-------------------------------------
	
	wifiPhy.EnablePcapAll(std::string("AODV_NODE"));

	// Výpis směrovací tabulky do souboru
	Ipv4GlobalRoutingHelper table;
	Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper>(
			"AODV-routing", std::ios::out);
	table.PrintRoutingTableAllEvery(Seconds(5), routingStream);
	//-----------------------------------------------------------------------------

	//-----------------------------Flow monitor------------------------------------
	FlowMonitorHelper flowHelper;
	Ptr<FlowMonitor> flowMonitor;
	flowMonitor = flowHelper.Install(nodes.Get(0));
	flowMonitor = flowHelper.Install(nodes.Get(size - 1));

	Simulator::Stop(Seconds(totalTime)); 	 // Zastavení simulace
	//-----------------------------------------------------------------------------

	//------------------------------NetAnim----------------------------------------
	AnimationInterface anim(animFile);
	anim.EnablePacketMetadata(true);			 //Povolí zobrazit popis zasílaných zpráv v simulaci
	anim.SetMaxPktsPerTraceFile(100000000); 	 //zvýšení počtu paketů pro záznam

	//-----------------------------------------------------------------------------

	//------------------graf pro zpoždění - pokračování----------------------------
	gnuplotZP.SetTitle(zpozdeniTitle);
	gnuplotZP.SetTerminal("png");						//vytvoření png souboru
	gnuplotZP.SetLegend(zpozdeniOsa_X, zpozdeniOsa_Y);	//Popisy jednotlivých os

	//gnuplotZT.AppendExtra("set xrange [0:26]"); 		//nastavení rozsahu osy x
	//gnuplotZP.AppendExtra("set yrange [0:40]");		//nastavení rozsahu osy y

	gnuplotZP.AppendExtra("set grid"); 					//zapnutí mřížky grafu
	delay.SetTitle(zpozdeniOsa_Y); 						//vytisknutí názvu grafu
	delay.SetStyle(Gnuplot2dDataset::LINES_POINTS); 	//volba typu grafu (zde křivka s body)
	//-----------------------------------------------------------------------------

	//----------------graf pro ztrátovost - pokračování----------------------------
	gnuplotZT.SetTitle(lostTitle);
	gnuplotZT.SetTerminal("png");						//vytvoření png souboru
	gnuplotZT.SetLegend(lostOsa_X, lostOsa_Y);			//Popisy jednotlivých os

	//gnuplotZT.AppendExtra("set xrange [0:26]"); 		//nastavení rozsahu osy x
	gnuplotZT.AppendExtra("set yrange [0:30]"); 		//nastavení rozsahu osy y

	gnuplotZT.AppendExtra("set grid"); 					//zapnutí mřížky grafu
	lostP.SetTitle(lostOsa_Y); 							//vytisknutí názvu grafu
	lostP.SetStyle(Gnuplot2dDataset::HISTEPS);			//volba typu grafu (zde sloupcový)
	//-----------------------------------------------------------------------------

	//------------------graf pro bitrate - pokračování-----------------------------
	gnuplotBR.SetTitle(bitRTitle);
	gnuplotBR.SetTerminal("png");						//vytvoření png souboru
	gnuplotBR.SetLegend(bitROsa_X, bitROsa_Y);			//Popisy jednotlivých os
	gnuplotBR.AppendExtra("set grid"); 					//zapnutí mřížky grafu

	rxR.SetTitle(rxTitle); 								//vytisknutí názvu grafu
	rxR.SetStyle(Gnuplot2dDataset::LINES_POINTS); 		//volba typu grafu (zde křivka s body)

	txR.SetTitle(txTitle); 								//vytisknutí názvu grafu
	txR.SetStyle(Gnuplot2dDataset::LINES_POINTS); 		//volba typu grafu (zde křivka s body)
	//-----------------------------------------------------------------------------

	//-----------flowmonitor pro linku mezi serverem a klientem--------------------
	for (int t = 1; t <= totalTime; t++) {
		Simulator::Schedule(Seconds(t), Stats, flowMonitor);
	}
	//-----------------------------------------------------------------------------

	Simulator::Run();

	//---------------flowmonitor pro všechny linky---------------------------------
	flowMonitor->CheckForLostPackets();
	Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier>(
			flowHelper.GetClassifier());
	std::map<FlowId, FlowMonitor::FlowStats> stats =
			flowMonitor->GetFlowStats();
	for (std::map<FlowId, FlowMonitor::FlowStats>::const_iterator i =
			stats.begin(); i != stats.end(); ++i) {
		Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(i->first);
		NS_LOG_UNCOND(
				"\n  Flow ID: " << i->first << "   Src Addr: " << t.sourceAddress << "   Dst Addr: " << t.destinationAddress);
		NS_LOG_UNCOND("  Tx Bytes: " << i->second.txBytes);
		NS_LOG_UNCOND("  Rx Bytes: " << i->second.rxBytes);
		NS_LOG_UNCOND("  Tx Packets: " << i->second.txPackets);
		NS_LOG_UNCOND("  Rx Packets: " << i->second.rxPackets);
		NS_LOG_UNCOND(
				"  Mean Delay: " << i->second.delaySum.GetSeconds () / (i->second.rxPackets) * 1000 << " ms");
		NS_LOG_UNCOND(
				"  Mean Jitter: " << i->second.jitterSum.GetSeconds () / (i->second.rxPackets - 1) * 1000 << " ms");
		NS_LOG_UNCOND(
				"  Throughput: " << i->second.rxBytes * 8.0 / (i->second.timeLastRxPacket.GetSeconds () - i->second.timeFirstTxPacket.GetSeconds ()) / 1024 << " Kbps");
		NS_LOG_UNCOND("  Lost Packets = " << i->second.lostPackets);
	}

	flowMonitor->SerializeToXmlFile("AODVFlowmon", true, true);
	//-----------------------------------------------------------------------------

	Simulator::Destroy();

	//-----------graf pro zpoždění - pokračování-----------------------------------
	gnuplotZP.AddDataset(delay);
	ofstream plotFileZP(zpozdeniFileName.c_str());		//Otevře soubor
	gnuplotZP.GenerateOutput(plotFileZP);				//Zapíše soubor
	plotFileZP.close();									//Zavře soubor
	//-----------------------------------------------------------------------------

	//-----------graf pro zpoždění - pokračování-----------------------------------
	gnuplotZT.AddDataset(lostP);
	ofstream plotFileZT(lostFileName.c_str());			//Otevře soubor
	gnuplotZT.GenerateOutput(plotFileZT);				//Zapíše soubor
	plotFileZT.close();									//Zavře soubor
	//-----------------------------------------------------------------------------

	//-----------graf pro bitrate - pokračování------------------------------------
	gnuplotBR.AddDataset(rxR);
	gnuplotBR.AddDataset(txR);
	ofstream plotFileBR(bitRFileName.c_str());			//Otevře soubor
	gnuplotBR.GenerateOutput(plotFileBR);				//Zapíše soubor
	plotFileBR.close();									//Zavře soubor
	//-----------------------------------------------------------------------------

	return 0;
}
