#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
#include "../src/ospf/model/ipv4-ospf-routing.cc"
#include "ns3/ipv4-ospf-routing-helper.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/netanim-module.h"
#include "ns3/flow-monitor-module.h"
#include <fstream>
using namespace ns3;


void initLSAs() {

	map<int, vector<uint16_t> > LSAs;
	for (int i = 0; i < ConfLoader::Instance()->getTotalNum() + ConfLoader::Instance()->getToRNum(); i++) {
		std::vector<uint16_t> lsa;
		if (i < ConfLoader::Instance()->getCoreNum()) {
			for (int j = ConfLoader::Instance()->getCoreNum(); j < ConfLoader::Instance()->getTotalNum(); j++) {
				lsa.push_back((uint16_t)j);
			}
		} else if (i < ConfLoader::Instance()->getTotalNum()) {
			for (int j = 0; j < ConfLoader::Instance()->getCoreNum(); j++) {
				lsa.push_back((uint16_t)j);
			}
			if (i < (ConfLoader::Instance()->getCoreNum() + ConfLoader::Instance()->getToRNum())) {
				lsa.push_back((uint16_t)(i + ConfLoader::Instance()->getBorderNum() + ConfLoader::Instance()->getToRNum()));
			}
		} else {
			lsa.push_back((uint16_t)(i - (ConfLoader::Instance()->getBorderNum() + ConfLoader::Instance()->getToRNum())));
		}
		LSAs[i] = lsa;
		for (std::vector<uint16_t>::iterator it = lsa.begin(); it != lsa.end(); ++it) {
			ConfLoader::Instance()->getNodeContainer().Get(i)->GetObject<Ipv4OSPFRouting>()->addToNeighbors((int)(*it), Simulator::Now());
		}
	}
	for (int i = 0; i < ConfLoader::Instance()->getTotalNum() + ConfLoader::Instance()->getToRNum(); i++) {
		ConfLoader::Instance()->getNodeContainer().Get(i)->GetObject<Ipv4OSPFRouting>()->setLSA(LSAs);
	}

}


void Hello() {
	cout << Simulator::Now() << "----------------hello---------" << endl;
	for (int i = 0; i < ConfLoader::Instance()->getTotalNum() + ConfLoader::Instance()->getToRNum(); i++) {
		ConfLoader::Instance()->getNodeContainer().Get(i)->GetObject<Ipv4OSPFRouting>()->sendHelloMessage();
	}
}
PointToPointHelper ospf;
void CheckNeighbor() {
	cout << Simulator::Now() << "----------------checkNeighbor---------" << endl;
	for (int i = 0; i < ConfLoader::Instance()->getTotalNum() + ConfLoader::Instance()->getToRNum(); i++) {
		ConfLoader::Instance()->getNodeContainer().Get(i)->GetObject<Ipv4OSPFRouting>()->checkNeighbors();
	}
}

void statistics() {

	//printTxInfo(5,8);

}
int
main (int argc, char *argv[])
{
	CommandLine cmd;

	cmd.Parse (argc, argv);


	NS_LOG_UNCOND ("Create nodes.");
	NodeContainer n;
	n.Create (8);
	NodeContainer n0n1 = NodeContainer(n.Get(0), n.Get(1));
	NodeContainer n0n2 = NodeContainer(n.Get(0), n.Get(2));
	NodeContainer n0n4 = NodeContainer(n.Get(0), n.Get(4));
	NodeContainer n0n5 = NodeContainer(n.Get(0), n.Get(5));

	NodeContainer n1n0 = NodeContainer(n.Get(1), n.Get(0));//
	NodeContainer n1n3 = NodeContainer(n.Get(1), n.Get(3));
	NodeContainer n1n7 = NodeContainer(n.Get(1), n.Get(7));

	NodeContainer n2n3 = NodeContainer(n.Get(2), n.Get(3));
	NodeContainer n2n0 = NodeContainer(n.Get(2), n.Get(0));//

	NodeContainer n3n1 = NodeContainer(n.Get(3), n.Get(1));//
	NodeContainer n3n2 = NodeContainer(n.Get(3), n.Get(2));//
	NodeContainer n3n7 = NodeContainer(n.Get(3), n.Get(7));
	NodeContainer n3n6 = NodeContainer(n.Get(3), n.Get(6));
	NodeContainer n3n5 = NodeContainer(n.Get(3), n.Get(5));

	NodeContainer n4n0 = NodeContainer(n.Get(4), n.Get(0)); //
	NodeContainer n4n5 = NodeContainer(n.Get(4), n.Get(5));

	NodeContainer n5n0 = NodeContainer(n.Get(5), n.Get(0));//
	NodeContainer n5n3 = NodeContainer(n.Get(5), n.Get(3)); //
	NodeContainer n5n4 = NodeContainer(n.Get(5), n.Get(4));//
	NodeContainer n5n6 = NodeContainer(n.Get(5), n.Get(6));

	NodeContainer n6n5 = NodeContainer(n.Get(6), n.Get(5));//
	NodeContainer n6n4 = NodeContainer(n.Get(6), n.Get(4));//
	NodeContainer n6n7 = NodeContainer(n.Get(6), n.Get(7));

	NodeContainer n7n6 = NodeContainer(n.Get(7), n.Get(6));//
	NodeContainer n7n3 = NodeContainer(n.Get(7), n.Get(3));//
	NodeContainer n7n1 = NodeContainer(n.Get(7), n.Get(1));//

	int a[8];
	int b[8];

	connectionMatrix();
	intialchromosome();
	vsd();
	cout << "\n\nChromosome For Source:";
	indexsource(a);
	indexdest(a);
	int p1 = noh(a);
	f1 = (p1 * 10 - 1);
	cout << "\nthe fitness value is:";
	cout << f1;
	cout << "\n\nChromosome for Destination:";
	indexsource(b);
	indexdest(b);
	cout << "\nthe Multipath route for destination ";
	int p2 = noh(b);
	f2 = (p2 * 10 - 2);
	cout << "\nthe fitness value is:";
	cout << f2;
	reproduction(a, b);
	crossover(a, b);

	cout << "\n";
	InternetStackHelper internet;
	internet.Install (n);

	ospf.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("1Gbps")));
	ospf.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));

	//
// Installing Protocol Object on Nodes Topology
//
	NetDeviceContainer dev01 = ospf.Install(n0n1);
	NetDeviceContainer dev02 = ospf.Install(n0n2);
	NetDeviceContainer dev04 = ospf.Install(n0n4);
	NetDeviceContainer dev05 = ospf.Install(n0n5);

	NetDeviceContainer dev10 = ospf.Install(n1n0);//
	NetDeviceContainer dev13 = ospf.Install(n1n3);
	NetDeviceContainer dev17 = ospf.Install(n1n7);

	NetDeviceContainer dev23 = ospf.Install(n2n3);
	NetDeviceContainer dev20 = ospf.Install(n2n0);//

	NetDeviceContainer dev31 = ospf.Install(n3n1);//
	NetDeviceContainer dev32 = ospf.Install(n3n2);//
	NetDeviceContainer dev37 = ospf.Install(n3n7);
	NetDeviceContainer dev36 = ospf.Install(n3n6);
	NetDeviceContainer dev35 = ospf.Install(n3n5);

	NetDeviceContainer dev40 = ospf.Install(n4n0); //
	NetDeviceContainer dev45 = ospf.Install(n4n5);

	NetDeviceContainer dev50 = ospf.Install(n5n0); //
	NetDeviceContainer dev53 = ospf.Install(n5n3);//
	NetDeviceContainer dev54 = ospf.Install(n5n4); //
	NetDeviceContainer dev56 = ospf.Install(n5n6);

	NetDeviceContainer dev64 = ospf.Install(n6n4);//
	NetDeviceContainer dev65 = ospf.Install(n6n5);//
	NetDeviceContainer dev67 = ospf.Install(n6n7);

	NetDeviceContainer dev76 = ospf.Install(n7n6);
	NetDeviceContainer dev73 = ospf.Install(n7n3);
	NetDeviceContainer dev71 = ospf.Install(n7n1);

//
// Install IP Address
//
	NS_LOG_UNCOND("Assign IP Addresses.");
	Ipv4AddressHelper ipv4;
	ipv4.SetBase ("10.1.1.0", "255.255.255.0");
	Ipv4InterfaceContainer i01 = ipv4.Assign (dev01);
	Ipv4InterfaceContainer i02 = ipv4.Assign (dev02);
	Ipv4InterfaceContainer i04 = ipv4.Assign (dev04);
	Ipv4InterfaceContainer i05 = ipv4.Assign (dev05);

	Ipv4InterfaceContainer i10 = ipv4.Assign (dev10);
	Ipv4InterfaceContainer i13 = ipv4.Assign (dev13);
	Ipv4InterfaceContainer i17 = ipv4.Assign (dev17);
	Ipv4InterfaceContainer i23 = ipv4.Assign (dev23);
	Ipv4InterfaceContainer i20 = ipv4.Assign (dev20);

	Ipv4InterfaceContainer i31 = ipv4.Assign (dev31);
	Ipv4InterfaceContainer i32 = ipv4.Assign (dev32);
	Ipv4InterfaceContainer i37 = ipv4.Assign (dev37);
	Ipv4InterfaceContainer i36 = ipv4.Assign (dev36);
	Ipv4InterfaceContainer i35 = ipv4.Assign (dev35);
	Ipv4InterfaceContainer i40 = ipv4.Assign (dev40);
	Ipv4InterfaceContainer i45 = ipv4.Assign (dev45);
	Ipv4InterfaceContainer i50 = ipv4.Assign (dev50);
	Ipv4InterfaceContainer i53 = ipv4.Assign (dev53);
	Ipv4InterfaceContainer i54 = ipv4.Assign (dev54);
	Ipv4InterfaceContainer i56 = ipv4.Assign (dev56);
	Ipv4InterfaceContainer i67 = ipv4.Assign (dev67);
	Ipv4InterfaceContainer i64 = ipv4.Assign (dev64);
	Ipv4InterfaceContainer i65 = ipv4.Assign (dev65);

	Ipv4InterfaceContainer i76 = ipv4.Assign (dev76);
	Ipv4InterfaceContainer i73 = ipv4.Assign (dev73);
	Ipv4InterfaceContainer i71 = ipv4.Assign (dev71);
	Simulator::Schedule (Seconds (0), &initLSAs);
	Simulator::Schedule (Seconds (7), &statistics);


	Ipv4GlobalRoutingHelper::PopulateRoutingTables ();


	NS_LOG_UNCOND ("Create Applications.");
	uint16_t port = 9;


	Address sinkLocalAddress (InetSocketAddress (Ipv4Address::GetAny (), port));
	PacketSinkHelper sinkHelper ("ns3::UdpSocketFactory", sinkLocalAddress);
	ApplicationContainer sinkApp = sinkHelper.Install (n1n7.Get (1));
	sinkApp.Start (Seconds (1.0));
	sinkApp.Stop (Seconds (6.0));

	OnOffHelper clientHelper1 ("ns3::UdpSocketFactory", Address ());
	clientHelper1.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));
	clientHelper1.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]"));
	clientHelper1.SetAttribute ("DataRate", DataRateValue (DataRate ("100Mb/s")));
	clientHelper1.SetAttribute ("PacketSize", UintegerValue (1000));

	ApplicationContainer clientApps1;
	AddressValue remoteAddress (InetSocketAddress (i17.GetAddress (1), port));
	clientHelper1.SetAttribute ("Remote", remoteAddress);
	clientApps1.Add (clientHelper1.Install (n0n1.Get (0)));
	clientApps1.Start (Seconds (1.0));
	clientApps1.Stop (Seconds (6.0));


	OnOffHelper clientHelper2 ("ns3::UdpSocketFactory", Address ());
	clientHelper2.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));
	clientHelper2.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]"));
	clientHelper2.SetAttribute ("DataRate", DataRateValue (DataRate ("100Mb/s")));
	clientHelper2.SetAttribute ("PacketSize", UintegerValue (210));

	ApplicationContainer clientApps2;
	clientHelper2.SetAttribute ("Remote", remoteAddress);
	clientApps2.Add (clientHelper2.Install (n3n7.Get (0)));
	clientApps2.Start (Seconds (1.0));
	clientApps2.Stop (Seconds (2.0));

	OnOffHelper clientHelper3 ("ns3::UdpSocketFactory", Address ());
	clientHelper3.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));
	clientHelper3.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]"));
	clientHelper3.SetAttribute ("DataRate", DataRateValue (DataRate ("100Mb/s")));
	clientHelper3.SetAttribute ("PacketSize", UintegerValue (210));

	ApplicationContainer clientApps3;
	clientHelper3.SetAttribute ("Remote", remoteAddress);
	clientApps3.Add (clientHelper3.Install (n0n2.Get (0)));
	clientApps3.Start (Seconds (1.0));
	clientApps3.Stop (Seconds (2.0));

	NS_LOG_UNCOND ("Run Simulation.");

	AnimationInterface::SetConstantPosition (n.Get (0), 1, 3);
	AnimationInterface::SetConstantPosition (n.Get (1), 4, 3);
	AnimationInterface::SetConstantPosition (n.Get (2), 2, 2.4);
	AnimationInterface::SetConstantPosition (n.Get (3), 3, 2);
	AnimationInterface::SetConstantPosition (n.Get (4), 0, 2);
	AnimationInterface::SetConstantPosition (n.Get (5), 1, 1);
	AnimationInterface::SetConstantPosition (n.Get (6), 4, 1);
	AnimationInterface::SetConstantPosition (n.Get (7), 5, 2);
	AnimationInterface anim ("anim-multi.xml");

	// Installing Flowmonitor
	//
	FlowMonitorHelper flowmon;
	Ptr<FlowMonitor> monitor = flowmon.InstallAll();

	Simulator::Stop (Seconds (100.0));
	Simulator::Run ();

	monitor->CheckForLostPackets ();
	Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier> (flowmon.GetClassifier ());
	std::map<FlowId, FlowMonitor::FlowStats> stats = monitor->GetFlowStats ();

	uint32_t txPacketsum = 0;
	uint32_t rxPacketsum = 0;
	uint32_t DropPacketsum = 0;
	uint32_t LostPacketsum = 0;
	double Delaysum = 0;
	double txBytessum = 0;
	double rxBytessum = 0;

	for (std::map<FlowId, FlowMonitor::FlowStats>::const_iterator i = stats.begin (); i != stats.end (); i++)
	{
		rxBytessum += i->second.rxBytes;
		txBytessum += i->second.txBytes;
		txPacketsum += i->second.txPackets;
		rxPacketsum += i->second.rxPackets;
		LostPacketsum += (i->second.txPackets - i->second.rxPackets);
		DropPacketsum += i->second.packetsDropped.size();
		Delaysum += i->second.delaySum.GetSeconds();

	}
	std::cout << "\n\t----------------------------------------------------------------------\n ";
	std::cout << "\t      FINAL OUTPUT FOR MULTIPATH-OSPF \n";
	std::cout << "\t----------------------------------------------------------------------\n ";
	std::cout << "\t  All Tx Packets: " << txPacketsum << "\n";
	std::cout << "\t  All Rx Packets: " << rxPacketsum << "\n";
	std::cout << "\t  Average Delay: " << Delaysum / txPacketsum << "\n";
	std::cout << "\t  All Lost Packets: " << LostPacketsum << "\n";
	std::cout << "\t  Packets Delivery Ratio: " << ((rxPacketsum * 100) / txPacketsum) << "%" << "\n";
	std::cout << "\t  Throughput: " << (rxBytessum * 8.0) / 1024     << "Kbps\n";
	std::cout << "\t ---------------------------------THANKS-------------------------------\n\n\n ";

	Simulator::Destroy ();
	NS_LOG_UNCOND ("Done.");
}
