/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2011 University of Kansas
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Justin Rohrer <rohrej@ittc.ku.edu>
 *
 * James P.G. Sterbenz <jpgs@ittc.ku.edu>, director
 * ResiliNets Research Group  http://wiki.ittc.ku.edu/resilinets
 * Information and Telecommunication Technology Center (ITTC)
 * and Department of Electrical Engineering and Computer Science
 * The University of Kansas Lawrence, KS USA.
 *
 * Work supported in part by NSF FIND (Future Internet Design) Program
 * under grant CNS-0626918 (Postmodern Internet Architecture),
 * NSF grant CNS-1050226 (Multilayer Network Resilience Analysis and Experimentation on GENI),
 * US Department of Defense (DoD), and ITTC at The University of Kansas.
 */

/*
 * This example program allows one to run ns-3 DSDV, AODV, or OLSR under
 * a typical random waypoint mobility model.
 *
 * By default, the simulation runs for 200 simulated seconds, of which
 * the first 50 are used for start-up time.  The number of nodes is 50.
 * Nodes move according to RandomWaypointMobilityModel with a speed of
 * 20 m/s and no pause time within a 300x1500 m region.  The WiFi is
 * in ad hoc mode with a 2 Mb/s rate (802.11b) and a Friis loss model.
 * The transmit power is set to 7.5 dBm.
 *
 * It is possible to change the mobility and density of the network by
 * directly modifying the speed and the number of nodes.  It is also
 * possible to change the characteristics of the network by changing
 * the transmit power (as power increases, the impact of mobility
 * decreases and the effective density increases).
 *
 * By default, OLSR is used, but specifying a value of 2 for the protocol
 * will cause AODV to be used, and specifying a value of 3 will cause
 * DSDV to be used.
 *
 * By default, there are 10 source/sink data pairs sending UDP data
 * at an application rate of 2.048 Kb/s each.    This is typically done
 * at a rate of 4 64-byte packets per second.  Application data is
 * started at a random time between 50 and 51 seconds and continues
 * to the end of the simulation.
 *
 * The program outputs a few items:
 * - packet receptions are notified to stdout such as:
 *   <timestamp> <node-id> received one packet from <src-address>
 * - each second, the data reception statistics are tabulated and output
 *   to a comma-separated value (csv) file
 * - some tracing and flow monitor configuration that used to work is
 *   left commented inline in the program
 */

#include <fstream>
#include <iostream>
#include <thread>
#include <chrono>
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/mobility-module.h"
#include "ns3/aodv-module.h"
#include "ns3/olsr-module.h"
#include "ns3/dsdv-module.h"
#include "ns3/dsr-module.h"
#include "ns3/applications-module.h"
#include "ns3/yans-wifi-helper.h"
#include "ns3/flow-monitor-helper.h"
#include "ns3/flow-monitor-module.h"
#include "ns3/data-rate.h"
#include "ns3/wifi-module.h"
#include "ns3/ipv4.h"
#include "ns3/discovery-packet-header.h"
#include "ns3/reply-packet-header.h"
#include "ns3/discovery-application.h"
//#include "ns3/netanim-module.h"
#include "ns3/markovchain-mobility-model.h"
//#include "ns3/animation-interface.h"
#include "ns3/myrtable.h"
#include <vector>


//#define YELLOW_CODE "\033[33m"
//#define TEAL_CODE "\033[36m"
//#define BOLD_CODE "\033[1m"
//#define END_CODE "\033[0m"

using namespace ns3;
using namespace dsr;


NS_LOG_COMPONENT_DEFINE ("ManetRoutingCompare");




//uint64_t
//DiscoveryApplication::GetDataRate(Ptr<Packet> packet){
//	DataRateTag pTag;
//
//	if(packet->PeekPacketTag(pTag)){
//		m_txDataRate = pTag.Get()/1000000;
//		NS_LOG_DEBUG(Simulator::Now ().GetSeconds () << " Received the packet with Data Rate: " << m_txDataRate);
//	}
//	return m_txDataRate;
//}



/// Main Routing table for the node


class RoutingExperiment
{
public:
    RoutingExperiment ();
    void Run (int nSinks, double txp, std::string CSVfileName);
    //static void SetMACParam (ns3::NetDeviceContainer & devices,
    //                                 int slotDistance);
    std::string CommandSetup (int argc, char **argv);

private:
    Ptr<Socket> SetupPacketReceive (Ipv4Address addr, Ptr<Node> node);
    Ptr<Socket> SetupPacketReceiveWD (Ipv4Address addr, Ptr<Node> node);
    Ptr<Socket> SetupDiscoveryReceive (Ipv4Address addr, Ptr<Node> node);
    Ptr<Socket> SetupDiscoveryReceiveWD (Ipv4Address addr, Ptr<Node> node);
    Ptr<Socket> SetupReplyReceive (Ipv4Address addr, Ptr<Node> node);
    Ptr<Socket> SetupReplyReceiveWD (Ipv4Address addr, Ptr<Node> node);
    void ReceivePacket (Ptr<Socket> socket);
    void ReceivePacketWD (Ptr<Socket> socket);
    void ReceiveDiscovery (Ptr<Socket> socket);
    void ReceiveDiscoveryWD (Ptr<Socket> socket);
    void ReceiveReply (Ptr<Socket> socket);
    void ReceiveReplyWD (Ptr<Socket> socket);
    void CheckThroughput ();
    void RxWD (std::string context, Ptr <const Packet> packet, uint16_t channelFreqMhz,  WifiTxVector txVector, MpduInfo aMpdu, SignalNoiseDbm signalNoise);
    void Rx (std::string context, Ptr <const Packet> packet, uint16_t channelFreqMhz,  WifiTxVector txVector, MpduInfo aMpdu, SignalNoiseDbm signalNoise);
    void Tx (std::string context, Ptr <const Packet> packet, uint16_t channelFreqMhz,  WifiTxVector txVector, MpduInfo aMpdu);
    void TxWD (std::string context, Ptr <const Packet> packet, uint16_t channelFreqMhz,  WifiTxVector txVector, MpduInfo aMpdu);
    Ipv4Address GetDestAddress(Ptr<const Packet> p);
    Ipv4Address GetSourceAddress(Ptr<const Packet> p);
    void txApp(Ptr<const Packet> pkt);
    void txAppWD(Ptr<const Packet> pkt);
    void PrintDropWD();
    void PrintDrop();
    void MacTxDrop(Ptr<const Packet> p);
    void MacTxDropWD(Ptr<const Packet> p);
    void PhyTxDrop(Ptr<const Packet> p);
    void PhyTxDropWD(Ptr<const Packet> p);
    void PhyRxDropWD(Ptr<const Packet> p, ns3::WifiPhyRxfailureReason reason);
    void PhyRxDrop(Ptr<const Packet> p, ns3::WifiPhyRxfailureReason reason);
    std::vector<std::string> Explode(const std::string& str, const char& ch);
    void LinkLifeTimer();
    void CourseChange (std::string context, Ptr<const MobilityModel> model);
    void allocateAndSend(int nodeID, double tDataSize, double tDeadLine);
    void StartTaskGeneration();
    void GenerateTasks();
    Time TimeIntervalToTime(uint16_t interval);
    void InstallApplicationsW (Ptr<Node> source, Ipv4Address des, double simTime);
    void InstallApplicationsWD (Ptr<Node> source, Ipv4Address des, double simTime);

    NodeContainer adhocNodes;
    uint32_t port;
    uint32_t portWD;
    uint32_t bytesTotal;
    uint32_t packetsReceived;
    uint32_t bytesTotalWD;
    uint32_t packetsReceivedWD;
    std::string m_CSVfileName;
    uint32_t m_nSinks;
    std::string m_protocolName;
    double m_txp;
    bool m_traceMobility;
    uint32_t m_protocol;
    uint32_t currentSeqNo = 0;
    long double delay = 0.0;
    long double rcv = 0.0;
    long double sqhd = 0.0;
    std::map<Ipv4Address, Ipv4Address> m_interfaceMap;
    uint32_t currentSeqNoWD = 0;
    long double delayWD = 0.0;
    long double rcvWD = 0.0;
    long double sqhdWD = 0.0;
    uint32_t m_nSources;
    uint32_t MacTxDropCount = 0;
    uint32_t PhyTxDropCount = 0;
    uint32_t PhyRxDropCount = 0;
    uint32_t MacTxDropCountWD = 0;
    uint32_t PhyTxDropCountWD = 0;
    uint32_t PhyRxDropCountWD = 0;
    uint64_t appPkt = 0;
    uint64_t appPktWD = 0;
    uint64_t discoverPkt = 0;
    uint64_t discoverPktWD = 0;
    uint64_t counterTX = 0;
    uint64_t counterRX = 0;
    uint64_t counterAppTX = 0;
    uint64_t counterAppRX = 0;

    uint64_t counterTXWD = 0;
    uint64_t counterRXWD = 0;
    uint64_t counterAppTXWD = 0;
    uint64_t counterAppRXWD = 0;

    uint64_t m_txDataRate = 0;
    uint64_t m_rxDataRate = 0;
    uint64_t m_txDataRateWD = 0;
    uint64_t m_rxDataRateWD = 0;
    uint64_t m_timer = 0;

    //Ipv4Address m_mainAddressW, m_mainAddressWD;
    RTable m_rTableWD, m_rTableW;
    Ptr<Socket> sink, sinkWD;
    Ptr<Socket> DiscoverySink, DiscoverySinkWD;
    Ptr<Socket> ReplySink, ReplySinkWD;




};

class UserTask
{
public:
    UserTask() {
    }
    UserTask(std::uint16_t deadline, std::uint16_t task_size, std::uint16_t task_id): m_deadline(deadline), m_task_size(task_size), m_task_id(task_id){
    }
    void print_id(){
        std::cout<< "Running task: Task ID: " << m_task_id << ", Task Size: " << m_task_size <<", Task Deadline: " << m_deadline << std::endl;
    }

    std::uint16_t get_deadline(){
        return m_deadline;
    }
    std::uint16_t get_task_size(){
        return m_task_size;
    }
    std::uint16_t get_task_id(){
        return m_task_id;
    }

private:
    std::uint16_t m_deadline;
    std::uint16_t m_task_size;
    std::uint16_t m_task_id;
};

auto cmp_deadline = [](UserTask left, UserTask right) {
        return (left.get_deadline()) > (right.get_deadline()); };
using deadline_priority_queue = std::priority_queue<UserTask, std::vector<UserTask>, decltype(cmp_deadline)>;
deadline_priority_queue ordered_queue(cmp_deadline);

template<typename T> void print_queue(T& q){
    while(!q.empty()) {
        auto task = q.top();
        task.print_id();
        q.pop();
    }
}
template <typename T>
void populate_queue(T &user_task_queue){
    srand((int) time(NULL));
    int i = 0;
    while (i++ < 10){
        int ds = (rand() % 100 + 1);
        std::cout<<"randomly generated data size: " << ds << ", taskID: " << i << std::endl;
        int dl = (rand() % 3600 + 1);
        std::cout<<"randomly generated data transfer deadline: " << dl << ", taskID: " << i << std::endl;
        user_task_queue.push(UserTask(dl,ds,i));
        std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 2000 + 1));
    }
}

void
RoutingExperiment::InstallApplicationsW (Ptr<Node> source, Ipv4Address des, double simTime)
{
//	for (uint32_t i = 0; i <= m_nSinks ; i++ ) //- 1
//	{
//		Ptr<Node> node = NodeList::GetNode (i);
//		Ipv4Address nodeAddress = node->GetObject<Ipv4> ()->GetAddress (i, 0).GetLocal ();
//		NS_LOG_DEBUG("Node AddressW: " << nodeAddress);

//		Ptr<Socket> sink = SetupPacketReceiveW (nodeAddress, node);
//	}

//	for (uint32_t clientNode = 0; clientNode <= m_nSinks; clientNode++ ) //- 1
//	{
//		for (uint32_t j = 0; j <= m_nSinks ; j++ ) //- 1
//		{
            OnOffHelper onoff1 ("ns3::UdpSocketFactory", Address (InetSocketAddress (des, port+3)));
            NS_LOG_DEBUG("Interface Address of WiFi: " << des << "and Port Number: " << port+3);
            onoff1.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
            onoff1.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
            onoff1.SetAttribute ("DataRate", DataRateValue(DataRate("11Mbps")));
            onoff1.SetAttribute("PacketSize", StringValue("1024"));

//			if (j != clientNode)
//			{
                ApplicationContainer apps = onoff1.Install (source);
                //Ptr<UniformRandomVariable> var = CreateObject<UniformRandomVariable> ();
                apps.Start (Seconds(0));//var->GetValue (m_dataStart, m_dataStart + 1)));
                //std::cout << "Wifi App Started" << std::endl;
               // NS_LOG_DEBUG("Sending data from port number:" << port);
                apps.Stop (Seconds (simTime));
               // std::cout << "Wifi App Stopped at: " << simTime << std::endl;
            //}
        //}
    //}
}

void RoutingExperiment::InstallApplicationsWD(Ptr<Node> source, Ipv4Address des, double simTime)
{
    //	for (uint32_t i = 0; i <= m_nSinks ; i++ ) //- 1
    //	{
    //		Ptr<Node> node = NodeList::GetNode (i);
    //		Ipv4Address nodeAddress = node->GetObject<Ipv4> ()->GetAddress (i, 0).GetLocal ();
    //		NS_LOG_DEBUG("Node AddressW: " << nodeAddress);

    //		Ptr<Socket> sink = SetupPacketReceiveW (nodeAddress, node);
    //	}

    //	for (uint32_t clientNode = 0; clientNode <= m_nSinks; clientNode++ ) //- 1
    //	{
    //		for (uint32_t j = 0; j <= m_nSinks ; j++ ) //- 1
    //		{
                OnOffHelper onoff1 ("ns3::UdpSocketFactory", Address (InetSocketAddress (des, portWD+3)));
                NS_LOG_DEBUG("Interface Address of WiFi: " << des << " and Port Number: " << portWD+3);
                onoff1.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
                onoff1.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
                onoff1.SetAttribute ("DataRate", DataRateValue(DataRate("54Mbps")));
                onoff1.SetAttribute("PacketSize", StringValue("1024"));

    //			if (j != clientNode)
    //			{
                    ApplicationContainer apps = onoff1.Install (source);
                    //Ptr<UniformRandomVariable> var = CreateObject<UniformRandomVariable> ();
                    apps.Start (Seconds(0));//var->GetValue (m_dataStart, m_dataStart + 1)));
                    //std::cout << "Wifi App Started" << std::endl;
                  //  NS_LOG_DEBUG("Sending data from port number: " << portWD);
                    apps.Stop (Seconds (simTime));
                    //std::cout << "Wifi App Stopped at: " << simTime << std::endl;
                //}
            //}
        //}

}


RoutingExperiment::RoutingExperiment ()
: port (9),
  portWD (80),
  bytesTotal (0),
  packetsReceived (0),
  bytesTotalWD (0),
  packetsReceivedWD (0),
  //	m_dataRate (0),
  m_CSVfileName ("manet-routing.output.csv"),
  m_traceMobility (true),
  m_protocol (0),
  m_nSources(3)// DSDV
{
}

Time
RoutingExperiment::TimeIntervalToTime(uint16_t interval)
{
    switch (interval)
    {
    case 0: return Seconds(10);
    case 1: return Seconds(60);
    case 2: return Seconds(120);
    default: return Seconds(0);
    }
}
void
RoutingExperiment::StartTaskGeneration()
{
    GenerateTasks();
}

void
RoutingExperiment::GenerateTasks()
{
//    Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable>();
//    x->SetAttribute("Min",DoubleValue(0));
//    x->SetAttribute("Max",DoubleValue(500));
//    double tDataSize = x->GetValue();
//    x->SetAttribute("Min",DoubleValue(0));
//    x->SetAttribute("Max",DoubleValue(50));
//    double tDeadLine = x->GetValue();
    auto task = ordered_queue.top();
    double deadline = task.get_deadline();
    NS_LOG_DEBUG("Task ID: "<< task.get_task_id() << ", deadline: " << deadline);
    double dataSize = task.get_task_size();
    NS_LOG_DEBUG("DataSize: " << dataSize);
    Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable>();
    x->SetAttribute("Min",DoubleValue(0));
    x->SetAttribute("Max",DoubleValue(m_nSources));
    int nodeID = x->GetInteger();

    allocateAndSend(nodeID, dataSize, deadline);
    //Simulator::Schedule(Seconds(tDeadLine + 10), &RoutingExperiment::GenerateTasks, this);


}

void
RoutingExperiment:: allocateAndSend(int nodeID, double tDataSize, double tDeadLine)
{
       bool transferPossible = false;
       double speedW = m_rxDataRate; //Mbps
       double speedWD = m_rxDataRateWD;
       double remainingData, finalData, totalData;
       double remainingTimeW = 0;
       double remainingTimeWD = 0;
       double interArrivalDelayW = delay;
       double interArrivalDelayWD = delayWD;

       Ptr<Node> source = NodeList::GetNode(nodeID);
       Ipv4Address sourceIPW = source->GetObject<Ipv4>()->GetAddress(1,0).GetLocal();
       Ipv4Address sourceIPWD = source->GetObject<Ipv4>()->GetAddress(2,0).GetLocal();
       std::map<Ipv4Address, RTableEntry> allRoutesW = m_rTableW.GetAllRoutesWithIP(sourceIPW);
       std::map<Ipv4Address, RTableEntry> allRoutesWD = m_rTableWD.GetAllRoutesWithIP(sourceIPWD);
       for (auto const map_entry : allRoutesW)
       {
           double T_DT_W = (tDataSize/speedW) + (tDataSize * interArrivalDelayW);
           double T_DT_WD = (tDataSize/speedWD) + (tDataSize * interArrivalDelayWD);

           double aW = std::min(tDeadLine, map_entry.second.getlinkLifeTime().GetSeconds());
           Ipv4Address ipWD = m_interfaceMap[map_entry.first];
           double aWD = std::min(tDeadLine, allRoutesWD.at(ipWD).getlinkLifeTime().GetSeconds());
           double maxDataW = speedW * (aW - (aW * interArrivalDelayW));
           double maxDataWD = speedWD * (aWD - (aWD * interArrivalDelayWD));
           if (maxDataW > tDataSize)
               maxDataW = tDataSize;
           if (maxDataWD > tDataSize)
               maxDataWD = tDataSize;

           if(maxDataWD > maxDataW){
                   remainingData = tDataSize - maxDataWD;
                   remainingTimeW = remainingData*(interArrivalDelayW + (1/speedW));
                   finalData = maxDataWD;
                   totalData = remainingData + maxDataWD;
                   } else {
                   remainingData = tDataSize - maxDataW;
                   remainingTimeWD = remainingData*(interArrivalDelayWD + (1/speedWD));
                   finalData = maxDataW;
                   totalData = remainingData + maxDataW;
                   }

//           double remainingDataW = tDataSize - maxDataW;
//           double remainingDataWD = tDataSize - maxDataWD;
//           double remainingTimeW = remainingDataW*(interArrivalDelay + (1/speedW));
//           double remainingTimeWD = remainingDataWD*(interArrivalDelay + (1/speedWD));
            double remainingTime = remainingTimeW + remainingTimeWD;
          // double totalMaxData = maxDataW + maxDataWD;
            if(totalData < tDataSize || remainingTime > tDeadLine){
                        NS_LOG_DEBUG("This data transfer is not possible");
                        continue;
          }
            else {
                    transferPossible = true;
                    if(maxDataWD >= maxDataW) {
                            if(T_DT_WD < tDeadLine) {
                                std::cout << "Sending the maximum data " << finalData << " to node using Wi-Fi Direct for the time: " << T_DT_WD << "s" << std::endl;

                                InstallApplicationsWD (source, ipWD, T_DT_WD);
                                //appIfTwo(nodes, sBandWidthWD, sDataTransferTimeWD, dataTransferTimeWD);
                                ordered_queue.pop();
                            } else {
                                std::cout << "Sending the maximum data " << finalData << " to node using Wi-Fi Direct for the time: " << tDeadLine << "s" << std::endl;
                                //sDeadline = std::to_string(deadline);
                                InstallApplicationsWD (source, ipWD, tDeadLine);
                                //appIfTwo(nodes, sBandWidthWD, sDeadline, deadline);
                                ordered_queue.pop();
                            }
                            if(remainingData == 0){
                                std::cout << "No need to use another WCT" << std::endl;
                            } else {
                                std::cout << "Sending the remaining data " << remainingData << " to node using Wi-Fi for the time: " << remainingTimeW << "s" << std::endl;

                                InstallApplicationsW (source, map_entry.first, remainingTimeW);
                                //appIfOne(nodes, sBandWidthW, sRemainingDTTW, remainingDTTW);
                                ordered_queue.pop();
                            }

                        } else {
                            if(T_DT_W < tDeadLine) {
                                std::cout << "Sending the maximum data " << finalData << " to node using Wi-Fi for the time: " << T_DT_W << "s" << std::endl;

                                InstallApplicationsW (source, map_entry.first, T_DT_W);
                                //appIfOne(nodes, sBandWidthW, sDataTransferTimeW, dataTransferTimeW);
                                ordered_queue.pop();
                            } else {
                                std::cout << "Sending the maximum data " << finalData << " to node using Wi-Fi for the time: " << tDeadLine << "s" << std::endl;

                                InstallApplicationsW (source, map_entry.first, tDeadLine);
                                //appIfOne(nodes, sBandWidthW, sDeadline, deadline);
                                ordered_queue.pop();
                            }
                            if(remainingData == 0){
                                std::cout << "No need to use another WCT" << std::endl;
                            } else {
                                std::cout << "Sending the remaining data " << remainingData << " to node using Wi-Fi Direct" << remainingTimeWD << "s" << std::endl;

                                InstallApplicationsWD (source,ipWD,remainingTimeWD);
                                //appIfTwo(nodes, sBandWidthW, sRemainingDTTW, remainingDTTWD);
                                ordered_queue.pop();
                            }
                        }
       }

}

       if(transferPossible == false)
       {
           ordered_queue.pop();
       }
       if (ordered_queue.size() != 0)
       {
           Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable>();
           x->SetAttribute("Min",DoubleValue(0));
           x->SetAttribute("Max",DoubleValue(20));
           int time = x->GetInteger();
           Simulator::Schedule(Seconds(time), &RoutingExperiment::GenerateTasks, this);
       }
}
std::vector<std::string> RoutingExperiment::Explode(const std::string& str, const char& ch){
    std::string next;
    std::vector<std::string> result;
    // For each character in the string
    for (std::string::const_iterator it = str.begin(); it != str.end(); it++) {
        // If we've hit the terminal character
        if (*it == ch) {
            // If we have some characters accumulated
            if (!next.empty()) {
                // Add them to the result vector
                result.push_back(next);
                next.clear();
            }
        } else {
            // Accumulate the next character into the sequence
            next += *it;
        }
    }
    if (!next.empty())
        result.push_back(next);
    return result;
}

Ipv4Address RoutingExperiment::GetSourceAddress(Ptr<const Packet> p)
{
    Ipv4Address src;
    // To get a header from Ptr<Packet> packet first, copy the packet
    Ptr<Packet> q = p->Copy();
    // Use indicator to search the packet
    PacketMetadata::ItemIterator metadataIterator = q->BeginItem();
    PacketMetadata::Item item;
    while (metadataIterator.HasNext())
    {
        item = metadataIterator.Next();
        NS_LOG_FUNCTION("item name: " << item.tid.GetName());
        // If we want to have a dsdv header
        if(item.tid.GetName() == "ns3::Ipv4Header")
        {
            Callback<ObjectBase *> constr = item.tid.GetConstructor();
            NS_ASSERT(!constr.IsNull());
            // Ptr<> and DynamicCast<> won't work here as all headers are from ObjectBase, not Object
            ObjectBase *instance = constr();
            NS_ASSERT(instance != 0);
            Ipv4Header* ipv4Header = dynamic_cast<Ipv4Header*>(instance);
            NS_ASSERT(ipv4Header != 0);
            ipv4Header->Deserialize(item.current);
            src = ipv4Header->GetSource();
            NS_LOG_DEBUG("Source IP ADDRESS FOUND from IPV4Header: " << src);
            break;
        }
        else if(item.tid.GetName() == "ns3::ArpHeader")
        {
            Callback<ObjectBase *> constr = item.tid.GetConstructor();
            NS_ASSERT(!constr.IsNull());
            // Ptr<> and DynamicCast<> won't work here as all headers are from ObjectBase, not Object
            ObjectBase *instance = constr();
            NS_ASSERT(instance != 0);
            ArpHeader* arpHeader = dynamic_cast<ArpHeader*>(instance);
            NS_ASSERT(arpHeader != 0);
            arpHeader->Deserialize(item.current);
            src = arpHeader->GetSourceIpv4Address();
            NS_LOG_DEBUG("Source IP ADDRESS FOUND from ARPHeader: " << src);
            break;
        }
        else
        {
            NS_LOG_DEBUG("NO SOURCE IP ADDRESS FOUND");
            //break;
        }
    }
    return src;
}

Ipv4Address RoutingExperiment::GetDestAddress(Ptr<const Packet> p)
{
    Ipv4Address dest;
    // To get a header from Ptr<Packet> packet first, copy the packet
    Ptr<Packet> q = p->Copy();
    // Use indicator to search the packet
    PacketMetadata::ItemIterator metadataIterator = q->BeginItem();
    PacketMetadata::Item item;
    while (metadataIterator.HasNext())
    {
        item = metadataIterator.Next();
        NS_LOG_FUNCTION("item name: " << item.tid.GetName());
        // If we want to have a dsdv header
        if(item.tid.GetName() == "ns3::Ipv4Header")
        {
            Callback<ObjectBase *> constr = item.tid.GetConstructor();
            NS_ASSERT(!constr.IsNull());
            // Ptr<> and DynamicCast<> won't work here as all headers are from ObjectBase, not Object
            ObjectBase *instance = constr();
            NS_ASSERT(instance != 0);
            Ipv4Header* ipv4Header = dynamic_cast<Ipv4Header*>(instance);
            NS_ASSERT(ipv4Header != 0);
            ipv4Header->Deserialize(item.current);
            dest = ipv4Header->GetDestination();
            NS_LOG_DEBUG("Destination IP ADDRESS FOUND from IPv4 Header: " << dest);
            break;
        }
        else if(item.tid.GetName() == "ns3::ArpHeader")
        {
            Callback<ObjectBase *> constr = item.tid.GetConstructor();
            NS_ASSERT(!constr.IsNull());
            // Ptr<> and DynamicCast<> won't work here as all headers are from ObjectBase, not Object
            ObjectBase *instance = constr();
            NS_ASSERT(instance != 0);
            ArpHeader* arpHeader = dynamic_cast<ArpHeader*>(instance);
            NS_ASSERT(arpHeader != 0);
            arpHeader->Deserialize(item.current);
            dest = arpHeader->GetDestinationIpv4Address();
            NS_LOG_DEBUG("Destination IP ADDRESS FOUND from ARPHeader: " << dest);
            break;
        }
        else
        {
            NS_LOG_DEBUG("NO Destination IP ADDRESS FOUND");
            //break;
        }
    }
    return dest;
}

void RoutingExperiment::txApp(Ptr<const Packet> pkt){
    NS_LOG_DEBUG (Simulator::Now().GetSeconds() << "\t Transmitting Application Packet: " << appPkt);
    appPkt++;
}

void RoutingExperiment::txAppWD(Ptr<const Packet> pkt){
    NS_LOG_DEBUG (Simulator::Now().GetSeconds() << "\t Transmitting Application Packet: " << appPktWD);
    appPktWD++;
}

static void
dataRate (uint64_t oldCurrRate, uint64_t newCurrRate)
{
    //std::cout<<"Data Rate is called: " << std::endl;
    //m_dataRate = newCurrRate;
    //m_rxDataRate = newCurrRate/1000000;

    NS_LOG_DEBUG (Simulator::Now ().GetSeconds () << "\t Data Rate Previous: " << oldCurrRate << "\t Data Rate Now: " << newCurrRate);
    //std::cout << Simulator::Now ().GetSeconds () << "\t Data Rate Now: " << newCurrRate << std::endl;
}

//Note: this is a promiscuous trace for all packet reception. This is also on physical layer, so packets still have WifiMacHeader
void
RoutingExperiment::Tx (std::string context, Ptr <const Packet> packet, uint16_t channelFreqMhz,  WifiTxVector txVector, MpduInfo aMpdu){
    //context will include info about the source of this event. Use string manipulation if you want to extract info.
    //std::cout << BOLD_CODE <<  context << END_CODE << std::endl;
    //Print the info.
    NS_LOG_DEBUG("Tx--------------------------------------------------");
    NS_LOG_DEBUG("Packet size : "<<packet->GetSize());
    NS_LOG_DEBUG("Context: "<< context);
    Ipv4Address src_ip = GetSourceAddress(packet);
    Ipv4Address des_ip = GetDestAddress(packet);
    std::cout<< src_ip << des_ip;
    if(packet->GetSize() >= 500){
        counterAppTX++;
    } else {
        counterTX++;
    }
    //NS_LOG_DEBUG(" Size = " << packet->GetSize()
      //      << " Freq = "<<channelFreqMhz
        //    << " Mode = " << txVector.GetMode()
          //  << " TransmissionDataRate = " << txVector.GetMode().GetDataRate(txVector)
            //<< " TX Counter: " << counterTX << "\t TX APP Counter: " << counterAppTX);
    m_txDataRate = txVector.GetMode().GetDataRate(txVector)/1000000;

    //We can also examine the WifiMacHeader
    WifiMacHeader hdr;
    if (packet->PeekHeader(hdr))
    {
      //  NS_LOG_DEBUG("\tDestination MAC : " << hdr.GetAddr1() << "\tSource MAC : " << hdr.GetAddr2());
    }
}

void
RoutingExperiment::TxWD (std::string context, Ptr <const Packet> packet, uint16_t channelFreqMhz,  WifiTxVector txVector,MpduInfo aMpdu){
    //context will include info about the source of this event. Use string manipulation if you want to extract info.
    //std::cout << BOLD_CODE <<  context << END_CODE << std::endl;
    //Print the info.
   NS_LOG_DEBUG("TxWD--------------------------------------------------");
   NS_LOG_DEBUG("Packet size : "<<packet->GetSize());
   NS_LOG_DEBUG("Context: "<< context);
   Ipv4Address src_ip = GetSourceAddress(packet);
   Ipv4Address des_ip = GetDestAddress(packet);
   std::cout<< src_ip << des_ip;
   if(packet->GetSize() >= 500){
        counterAppTXWD++;
    } else {
        counterTXWD++;
    }
    //NS_LOG_DEBUG("\tSize = " << packet->GetSize()
       //     << " Freq = "<<channelFreqMhz
       //     << " Mode = " << txVector.GetMode()
      //      << " TransmissionDataRate = " << txVector.GetMode().GetDataRate(txVector)
       //     << " TX Counter: " << counterTXWD << "\t TX APP Counter: " << counterAppTXWD);
    m_txDataRateWD = txVector.GetMode().GetDataRate(txVector)/1000000;

    //We can also examine the WifiMacHeader
    WifiMacHeader hdr;
    if (packet->PeekHeader(hdr))
    {
       // NS_LOG_DEBUG("\tDestination MAC : " << hdr.GetAddr1() << "\tSource MAC : " << hdr.GetAddr2());
    }
}

void
RoutingExperiment::RxWD (std::string context, Ptr <const Packet> packet, uint16_t channelFreqMhz,  WifiTxVector txVector, MpduInfo aMpdu, SignalNoiseDbm signalNoise){
    //context will include info about the source of this event. Use string manipulation if you want to extract info.
    //std::cout << BOLD_CODE <<  context << END_CODE << std::endl;
    //Print the info.
    //	Packet::EnablePrinting();
    //	Packet::EnableChecking();
    //	packet->Print(std::cout << "\nTime: " << Simulator::Now().GetSeconds() << "s, Packet Details are: ");
    NS_LOG_DEBUG("RxWD--------------------------------------------------");
    NS_LOG_DEBUG("Packet size : "<<packet->GetSize());
     NS_LOG_DEBUG("Context: "<< context);
    std::vector<std::string> result = Explode(context, '/');
    uint32_t nodeId = std::stoi(result[1]);
    uint32_t iface = std::stoi(result[3]);
    Ptr<Node> node = NodeList::GetNode(nodeId);
    Ptr<NetDevice> dev = node->GetDevice(iface);
    Ptr<Ipv4> ip = node->GetObject<Ipv4>();
    uint32_t ifaceId = ip->GetInterfaceForDevice(dev);
    Ipv4InterfaceAddress addr = ip->GetAddress(ifaceId, 0);
    Ipv4Address myAddress = addr.GetLocal();
    Ipv4Address src_ip = GetSourceAddress(packet);
    Ipv4Address des_ip = GetDestAddress(packet);
    //double timeConnected = 0.0;

    Vector neighborNodeLocation;

    int32_t nNodes = NodeList::GetNNodes ();

    for (int32_t i = 0; i < nNodes; ++i)
    {
        Ptr<Node> node = NodeList::GetNode (i);
        Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();

        int32_t ifIndex = ipv4->GetInterfaceForAddress (src_ip);
        if (ifIndex != -1)
        {
            Ptr<MobilityModel> neighborNodeModel = node->GetObject<MobilityModel>();
            neighborNodeLocation = neighborNodeModel->GetPosition();
        }
    }

    Vector myLocation;
    Ptr<MobilityModel> myMobilityModel = node->GetObject<MobilityModel>();
    myLocation = myMobilityModel->GetPosition();
    //NS_LOG_DEBUG("WD My Location: " << myLocation << ", Neighbor Node Location: " << neighborNodeLocation);
    //double distance = std::sqrt((myLocation.x-neighborNodeLocation.x) * (myLocation.x-neighborNodeLocation.x)+ (myLocation.y-neighborNodeLocation.y)
     //       * (myLocation.y-neighborNodeLocation.y));
   // NS_LOG_DEBUG("WD Distance among us is: " << distance);


    if(packet->GetSize() >= 500){
        counterAppRXWD++;
        //		NS_LOG_DEBUG("WIFI Direct Packet Received from: " << src_ip << ", Destination address in Packet: " << des_ip);
    } else {
        counterRXWD++;
    }

   // NS_LOG_DEBUG(myAddress <<" WD Received Packet from the Source Address: " << src_ip << ", With the Destination address in Packet: " << des_ip
    //        << ", Size = " << packet->GetSize()
    //        << ", Freq = "<<channelFreqMhz
    //        << ", Mode = " << txVector.GetMode()
    //        << ", ReceptionDataRate = " << txVector.GetMode().GetDataRate(txVector)
    //        << ", RX Counter: " << counterRXWD << ",\t RX APP Counter: " << counterAppRXWD);
    m_rxDataRateWD = txVector.GetMode().GetDataRate(txVector)/1000000;


    if(src_ip == Ipv4Address("102.102.102.102") || des_ip == Ipv4Address("102.102.102.102") || myAddress == Ipv4Address("102.102.102.102")){
        NS_LOG_DEBUG("This is RTS/CTS/ACK Packet!");
    }
//    else
//    {
//        if(des_ip != myAddress)
//        {
//            NS_LOG_DEBUG("This packet is for another destination, so don't insert it!");
//        }
//        else
//        {
//            RTableEntry rTableEntryWD;
//            bool permanentTableVerifier = m_rTableWD.LookupRoute (src_ip, rTableEntryWD);
//            NS_LOG_DEBUG("Is the route inside the table? " << permanentTableVerifier);
//            if (permanentTableVerifier == false)
//            {
//                NS_LOG_DEBUG ("Received New WD Route!");
//                RTableEntry newEntry(/*My Address=*/ myAddress, /*destination address=*/ src_ip,  /*time connected=*/0.0,/*time packet received=*/
//                        Simulator::Now (), /*time First packet received=*/ Simulator::Now(), /*my location=*/ myLocation, /*neighbor location=*/ neighborNodeLocation);
//                m_rTableWD.AddRoute (newEntry);
//                NS_LOG_DEBUG ("New Route added to WDtable");
//            }
//            else
//            {
//                rTableEntryWD.setMyAddress(myAddress);
//                rTableEntryWD.setDestAddress(src_ip);
//                timeConnected = Simulator::Now().GetSeconds() - rTableEntryWD.getTimeFirstPktRcvd().GetSeconds();
//                rTableEntryWD.setTimeConnected(timeConnected);
//                rTableEntryWD.setTimePktRcvd(Simulator::Now());
//                rTableEntryWD.setMyLocation(myLocation);
//                rTableEntryWD.setNeighborNodeLocation(neighborNodeLocation);
//                m_rTableWD.Update(rTableEntryWD);

//                NS_LOG_DEBUG ("Route updated to WD table");
//            }
//        }
//    }
//    NS_LOG_DEBUG("Values in the rTableWD are: ");
//    if(m_rTableWD.RTableSize() > 0)
//    {
//        m_rTableWD.GetListOfAllRoutes();
//        m_rTableWD.GetListofAllRoutes(myAddress);
//        //		std::map<Ipv4Address, RTableEntry> allRoutes;
//        //		m_rTableWD.GetListOfAllRoutes (allRoutes);

//    }
//    else
//    {
//        NS_LOG_DEBUG("RTableWD is Empty!");
//    }
    //We can also examine the WifiMacHeader
    WifiMacHeader hdr;
    if (packet->PeekHeader(hdr))
    {
        NS_LOG_DEBUG("\tDestination MAC : " << hdr.GetAddr1() << "\tSource MAC : " << hdr.GetAddr2());
    }
}

void
RoutingExperiment::Rx (std::string context, Ptr <const Packet> packet, uint16_t channelFreqMhz,  WifiTxVector txVector, MpduInfo aMpdu, SignalNoiseDbm signalNoise){
    //context will include info about the source of this event. Use string manipulation if you want to extract info.
    //std::cout << BOLD_CODE <<  context << END_CODE << std::endl;
    //Print the info.
    //	Packet::EnablePrinting();
    //		Packet::EnableChecking();
    //	packet->Print(std::cout << "\nTime: " << Simulator::Now().GetSeconds() << "s, Packet Details are: ");
   // NS_LOG_DEBUG("ContextW: " << context);
    NS_LOG_DEBUG("Rx--------------------------------------------------");
    NS_LOG_DEBUG("Packet size : "<<packet->GetSize());
     NS_LOG_DEBUG("Context: "<< context);
    std::vector<std::string> result = Explode(context, '/');
    uint32_t nodeId = std::stoi(result[1]);
    uint32_t iface = std::stoi(result[3]);
    Ptr<Node> node = NodeList::GetNode(nodeId);
    Ptr<NetDevice> dev = node->GetDevice(iface);
    Ptr<Ipv4> ip = node->GetObject<Ipv4>();
    uint32_t ifaceId = ip->GetInterfaceForDevice(dev);
    Ipv4InterfaceAddress addr = ip->GetAddress(ifaceId, 0);
    Ipv4Address myAddress = addr.GetLocal();
    Ipv4Address src_ip = GetSourceAddress(packet);
    Ipv4Address des_ip = GetDestAddress(packet);
    //double timeConnected = 0.0;

    Vector neighborNodeLocation;

    int32_t nNodes = NodeList::GetNNodes ();

    for (int32_t i = 0; i < nNodes; ++i)
    {
        Ptr<Node> node = NodeList::GetNode (i);
        Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();

        int32_t ifIndex = ipv4->GetInterfaceForAddress (src_ip);
        if (ifIndex != -1)
        {
            Ptr<MobilityModel> neighborNodeModel = node->GetObject<MobilityModel>();
            neighborNodeLocation = neighborNodeModel->GetPosition();
        }
    }

    Vector myLocation;
    Ptr<MobilityModel> myMobilityModel = node->GetObject<MobilityModel>();
    myLocation = myMobilityModel->GetPosition();
    //NS_LOG_DEBUG("W My Location: " << myLocation << ", Neighbor Node Location: " << neighborNodeLocation);
    //double distance = std::sqrt((myLocation.x-neighborNodeLocation.x) * (myLocation.x-neighborNodeLocation.x)+ (myLocation.y-neighborNodeLocation.y)
     //       * (myLocation.y-neighborNodeLocation.y));
   // NS_LOG_DEBUG("W Distance among us is: " << distance);


    if(packet->GetSize() >= 500){
        counterAppRX++;

    } else {
        counterRX++;
    }
    //
    //NS_LOG_DEBUG(myAddress << " W Received Packet from Source Address: " << src_ip << ", With Destination address in Packet: " << des_ip
     //       << ", Size = " << packet->GetSize()
     //       << ", Freq = "<<channelFreqMhz
     //       << ", Mode = " << txVector.GetMode()
     //       << ", ReceptionDataRate = " << txVector.GetMode().GetDataRate(txVector)
     //       << ", RX Counter: " << counterRX << ",\t RX APP Counter: " << counterAppRX);
    m_rxDataRate = txVector.GetMode().GetDataRate(txVector)/1000000;

    //insert into table

   if(src_ip == Ipv4Address("102.102.102.102") || des_ip == Ipv4Address("102.102.102.102") || myAddress == Ipv4Address("102.102.102.102"))
    {
        NS_LOG_DEBUG("This is RTS/CTS/ACK Packet!");
    }
//    else
//    {
//        if(des_ip != myAddress)
//        {
//            NS_LOG_DEBUG("This packet is for another destination, so don't insert it!");
//        }
//        else
//        {
//            RTableEntry rTableEntryW;
//            bool permanentTableVerifier = m_rTableW.LookupRoute (src_ip, rTableEntryW);
//            NS_LOG_DEBUG("Is the route inside the table? " << permanentTableVerifier);
//            if (permanentTableVerifier == false)
//            {
//                NS_LOG_DEBUG ("Received New WRoute!");
//                RTableEntry newEntry(/*My Address=*/ myAddress, /*destination address=*/ src_ip, /*time connected=*/0.0, /*time packet received=*/
//                        Simulator::Now (), /*time First packet received=*/ Simulator::Now (), /*my location=*/ myLocation, /*neighbor location=*/ neighborNodeLocation);
//                m_rTableW.AddRoute (newEntry);
//                NS_LOG_DEBUG ("New WRoute added to Wtable!");
//            }
//            else
//            {
//                rTableEntryW.setMyAddress(myAddress);
//                rTableEntryW.setDestAddress(src_ip);
//                timeConnected = Simulator::Now().GetSeconds() - rTableEntryW.getTimeFirstPktRcvd().GetSeconds();
//                rTableEntryW.setTimeConnected(timeConnected);
//                rTableEntryW.setTimePktRcvd(Simulator::Now());
//                rTableEntryW.setMyLocation(myLocation);
//                rTableEntryW.setNeighborNodeLocation(neighborNodeLocation);
//                m_rTableW.Update(rTableEntryW);
//                NS_LOG_DEBUG ("Route Updated in WTable!");
//            }
//        }
//    }

//    NS_LOG_DEBUG("Values in the rTableW are: ");
//    if(m_rTableW.RTableSize() > 0)
//    {
//        m_rTableW.GetListOfAllRoutes();
//        m_rTableW.GetListofAllRoutes(myAddress);
//        //				std::map<Ipv4Address, RTableEntry> allRoutes;
//        //				m_rTableW.GetListOfAllRoutes (allRoutes);
//        //				for (std::map<Ipv4Address, RTableEntry>::const_iterator i = allRoutes.begin (); i != allRoutes.end (); ++i)
//        //				{
//        //					NS_LOG_DEBUG ("The WTable routes are: " << "Source Address: " << i->second.getSourceAddress() << ", Destination Address: "
//        //							<< i->second.getDestAddress() << ", Current Location: " << i->second.getCurrLocation() << ", Last Location: " << i->second.getLastLocation()
//        //							<< ", Time Connected: " << i->second.getTimeConnected().GetSeconds() << "s, Time Last Packet Received at: "
//        //							<< i->second.getTimeLastPktRcvd().GetSeconds() << "s, Time First Packet Received at: " << i->second.getTimeFirstPktRcvd().GetSeconds() << "s");
//        //				}
//    }
//    else
//    {
//        NS_LOG_DEBUG("RTableW is Empty!");
//    }

    //We can also examine the WifiMacHeader
    WifiMacHeader hdr;
    if (packet->PeekHeader(hdr))
    {
   //     NS_LOG_DEBUG("\tDestination MAC : " << hdr.GetAddr1() << "\tSource MAC : " << hdr.GetAddr2());
    }
}

//void
//PrintRTable (Ptr<OutputStreamWrapper> stream, Time unit)	//prints the routing table stream.
//{
//  *stream->GetStream () << "Node: " << m_ipv4->GetObject<Node> ()->GetId ()
//                        << ", Time: " << unit
//                        << ", Routing table" << std::endl;
//  m_rTable.Print (stream);
//  *stream->GetStream () << std::endl;
//}

static inline std::string
PrintReceivedPacket (Ptr<Socket> socket, Ptr<Packet> packet, Address senderAddress)
{
    std::ostringstream oss;
    Ptr<Node> node = socket->GetNode();
    Ptr<Ipv4> ip = node->GetObject<Ipv4>();
    Ipv4Address address = ip->GetAddress(1,0).GetLocal();
    if (InetSocketAddress::IsMatchingType (senderAddress))
    {
        InetSocketAddress addr = InetSocketAddress::ConvertFrom (senderAddress);
        oss << Simulator::Now ().GetSeconds () << " , Node: " << node->GetId() << ", Address: " << address << " received one packet from " << addr.GetIpv4 ();
    }
    else
    {
        oss << Simulator::Now ().GetSeconds () << " received one packet!";
    }
    return oss.str ();
}

static inline std::string
PrintReceivedPacketWD (Ptr<Socket> socket, Ptr<Packet> packet, Address senderAddress)
{
    std::ostringstream oss;
    Ptr<Node> node = socket->GetNode();
    Ptr<Ipv4> ip = node->GetObject<Ipv4>();
    Ipv4Address address = ip->GetAddress(2,0).GetLocal();
    if (InetSocketAddress::IsMatchingType (senderAddress))
    {
        InetSocketAddress addr = InetSocketAddress::ConvertFrom (senderAddress);
        oss << Simulator::Now ().GetSeconds () << " , Node: " << node->GetId() << ", AddressWD: " << address<< " received one packet from " << addr.GetIpv4 ();
    }
    else
    {
        oss << Simulator::Now ().GetSeconds () << " received one packet!";
    }
    return oss.str ();
}

void
RoutingExperiment::ReceivePacket (Ptr<Socket> socket)
{
    NS_LOG_DEBUG("PacketW-----------------------------------------------------");
    Ptr<Packet> packet;
    Address senderAddress;
    //
    NS_LOG_DEBUG("Sender Address before while: " << senderAddress);
    while ((packet = socket->RecvFrom (senderAddress)))
    {
        //
        SeqTsHeader seqTsx;
        packet->RemoveHeader (seqTsx);
        currentSeqNo = seqTsx.GetSeq ();
        //m_rxDataRate = GetDRate(packet);

        bytesTotal += packet->GetSize ();
        packetsReceived += 1;
        NS_LOG_DEBUG("Bytes total received: " << bytesTotal );
        NS_LOG_DEBUG("Packets total received: " << packetsReceived);
        NS_LOG_UNCOND (PrintReceivedPacket (socket, packet, senderAddress));
        rcv = Simulator::Now().GetSeconds();
        sqhd = seqTsx.GetTs().GetSeconds();
        NS_LOG_DEBUG("Seq No " << currentSeqNo << " Packet Transmitted at: " << sqhd << "s, Packet Received at: " << rcv << "s" );//Just to check seq number and Tx time

        //if(rcv>sqhd)
        delay = rcv - sqhd; //delay calculation

        NS_LOG_DEBUG("Delay: " << delay << "s");

    }
    //  xx = rcv - sqhd;
    //  if(xx > 0.09)
    //	  throughput = (bytesTotal * 8.0) / (xx)/1024/1024;
    //  else
    //	  throughput = ((bytesTotal * 8.0)/ 1048576) * (xx);
    //  NS_LOG_DEBUG("Throughput: " << throughput);

}


void
RoutingExperiment::ReceivePacketWD (Ptr<Socket> socket)
{
    NS_LOG_DEBUG("PacketWD-----------------------------------------------------");
    Ptr<Packet> packet;
    Address senderAddress;

    while ((packet = socket->RecvFrom (senderAddress)))
    {

        SeqTsHeader seqTsx;
        packet->RemoveHeader (seqTsx);
        currentSeqNoWD = seqTsx.GetSeq ();
        //m_rxDataRate = GetDRate(packet);

        bytesTotalWD += packet->GetSize ();
        packetsReceivedWD += 1;
        NS_LOG_DEBUG("Bytes total received: " << bytesTotalWD );
        NS_LOG_DEBUG("Packets total received: " << packetsReceivedWD);
        NS_LOG_UNCOND (PrintReceivedPacketWD (socket, packet, senderAddress));
        rcvWD = Simulator::Now().GetSeconds();
        sqhdWD = seqTsx.GetTs().GetSeconds();
        NS_LOG_DEBUG("WD Seq No " << currentSeqNoWD << " Packet Transmitted at: " << sqhdWD << "s, Packet Received at: " << rcvWD << "s" );//Just to check seq number and Tx time

        // if(rcvWD>sqhdWD)
        delayWD = rcvWD - sqhdWD; //delay calculation

        NS_LOG_DEBUG("Delay: " << delayWD << "s");

    }
    //  xxWD = rcvWD - sqhdWD;
    //  if(xxWD > 0.09)
    //	  throughputWD = (bytesTotalWD * 8.0) / (xxWD)/1024/1024;
    //  else
    //	  throughputWD = ((bytesTotalWD * 8.0)/ 1048576) * (xxWD);
    //  NS_LOG_DEBUG("Throughput: " << throughputWD);

}

void
RoutingExperiment::ReceiveDiscovery(Ptr<Socket> socket)
{
    NS_LOG_DEBUG("DiscoveryW-----------------------------------------------------");
    uint32_t context = Simulator::GetContext();
    std::cout<< context;
    Ptr<Packet> packet;
    Address senderAddress;
    Ptr<Node> thisNode = NodeList::GetNode(context);

     while ((packet = socket->RecvFrom (senderAddress)))
     {
          DiscoveryPacketHeader header;
          packet->RemoveHeader(header);
          Ipv4Address src_ip = header.GetSource();
          Vector myLocation = thisNode->GetObject<MobilityModel>()->GetPosition();
          Vector neighborNodeLocation;

          int32_t nNodes = NodeList::GetNNodes ();

          for (int32_t i = 0; i < nNodes; ++i)
          {
              Ptr<Node> node = NodeList::GetNode (i);
              Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();

              int32_t ifIndex = ipv4->GetInterfaceForAddress (src_ip);
              if (ifIndex != -1)
              {
                  Ptr<MobilityModel> neighborNodeModel = node->GetObject<MobilityModel>();
                  neighborNodeLocation = neighborNodeModel->GetPosition();
              }
          }

          Ptr<DiscoveryApplication> app = DynamicCast<DiscoveryApplication>( NodeList::GetNode(context)->GetApplication(0));
          //app->ProcessDiscoveryPacket(header);
          uint16_t nextLoc = thisNode->GetObject<MarkovChainMobilityModel>()->GetNextLocation();
           uint16_t nextTime = thisNode->GetObject<MarkovChainMobilityModel>()->GetNextTime();
          if (header.GetNextLocation()== nextLoc)
          {
          RTableEntry rTableEntryW;
          bool permanentTableVerifier = m_rTableW.LookupRoute (header.GetSource(), rTableEntryW);
          NS_LOG_DEBUG("Is the route inside the table? " << permanentTableVerifier);
          if (permanentTableVerifier == false)
          {
              app->SendReplyPacketW(thisNode, thisNode->GetObject<Ipv4>()->GetAddress(1,0).GetLocal(), src_ip, nextLoc, nextTime);
              NS_LOG_DEBUG ("Received New WRoute!");
              RTableEntry newEntry(/*My Address=*/ thisNode->GetObject<Ipv4>()->GetAddress(1,0).GetLocal(), /*destination address=*/ header.GetSource(), /*time connected=*/0.0, /*time packet received=*/
                      Simulator::Now (), /*time First packet received=*/ Simulator::Now (), /*my location=*/ myLocation, /*neighbor location=*/ neighborNodeLocation,header.GetNextLocation());
              m_rTableW.AddRoute (newEntry);
              NS_LOG_DEBUG ("New WRoute added to Wtable!");
          }
          else
          {
              app->SendReplyPacketW(thisNode, thisNode->GetObject<Ipv4>()->GetAddress(1,0).GetLocal(), src_ip, nextLoc, nextTime);

              rTableEntryW.setDestAddress(src_ip);
              double timeConnected = Simulator::Now().GetSeconds() - rTableEntryW.getTimeFirstPktRcvd().GetSeconds();
              rTableEntryW.setTimeConnected(timeConnected);
              rTableEntryW.setTimePktRcvd(Simulator::Now());
              rTableEntryW.setMyLocation(myLocation);
              rTableEntryW.setNeighborNodeLocation(neighborNodeLocation);
              rTableEntryW.setNextLocation(nextLoc);
              m_rTableW.Update(rTableEntryW);
              NS_LOG_DEBUG ("Route Updated in WTable!");
          }
      }
      }

 }


void
RoutingExperiment::ReceiveDiscoveryWD(Ptr<Socket> socket)
{
    NS_LOG_DEBUG("DiscoveryWD-----------------------------------------------------");
    uint32_t context = Simulator::GetContext();
    std::cout<< context;
    Ptr<Packet> packet;
    Address senderAddress;
    Ptr<Node> thisNode = NodeList::GetNode(context);

     while ((packet = socket->RecvFrom (senderAddress)))
     {
          DiscoveryPacketHeader header;
          packet->RemoveHeader(header);
          Ipv4Address src_ip = header.GetSource();
          Vector myLocation = thisNode->GetObject<MobilityModel>()->GetPosition();
          Vector neighborNodeLocation;

          int32_t nNodes = NodeList::GetNNodes ();

          for (int32_t i = 0; i < nNodes; ++i)
          {
              Ptr<Node> node = NodeList::GetNode (i);
              Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();

              int32_t ifIndex = ipv4->GetInterfaceForAddress (src_ip);
              if (ifIndex != -1)
              {
                  Ptr<MobilityModel> neighborNodeModel = node->GetObject<MobilityModel>();
                  neighborNodeLocation = neighborNodeModel->GetPosition();
              }
          }

          Ptr<DiscoveryApplication> app = DynamicCast<DiscoveryApplication>( NodeList::GetNode(context)->GetApplication(0));
          //app->ProcessDiscoveryPacket(header);
          uint16_t nextLoc = thisNode->GetObject<MarkovChainMobilityModel>()->GetNextLocation();
          uint16_t nextTime = thisNode->GetObject<MarkovChainMobilityModel>()->GetNextTime();
          if (header.GetNextLocation()== nextLoc)
          {
          RTableEntry rTableEntryWD;
          bool permanentTableVerifier = m_rTableWD.LookupRoute (header.GetSource(), rTableEntryWD);
          NS_LOG_DEBUG("Is the route inside the table? " << permanentTableVerifier);
          if (permanentTableVerifier == false)
          {
              app->SendReplyPacketWD(thisNode, thisNode->GetObject<Ipv4>()->GetAddress(2,0).GetLocal(), src_ip, nextLoc, nextTime);
              NS_LOG_DEBUG ("Received New WRoute!");
              RTableEntry newEntry(/*My Address=*/ thisNode->GetObject<Ipv4>()->GetAddress(2,0).GetLocal(), /*destination address=*/ header.GetSource(), /*time connected=*/0.0, /*time packet received=*/
                      Simulator::Now (), /*time First packet received=*/ Simulator::Now (), /*my location=*/ myLocation, /*neighbor location=*/ neighborNodeLocation, header.GetNextLocation());
              m_rTableWD.AddRoute (newEntry);
              NS_LOG_DEBUG ("New WRoute added to Wtable!");
          }
          else
          {
              app->SendReplyPacketWD(thisNode, thisNode->GetObject<Ipv4>()->GetAddress(2,0).GetLocal(), src_ip, nextLoc, nextTime);

              rTableEntryWD.setDestAddress(src_ip);
              double timeConnected = Simulator::Now().GetSeconds() - rTableEntryWD.getTimeFirstPktRcvd().GetSeconds();
              rTableEntryWD.setTimeConnected(timeConnected);
              rTableEntryWD.setTimePktRcvd(Simulator::Now());
              rTableEntryWD.setMyLocation(myLocation);
              rTableEntryWD.setNeighborNodeLocation(neighborNodeLocation);
              rTableEntryWD.setNextLocation(nextLoc);
              m_rTableWD.Update(rTableEntryWD);
              NS_LOG_DEBUG ("Route Updated in WTable!");
          }
      }
      }

}

void
RoutingExperiment::ReceiveReply(Ptr<Socket> socket)
{
    NS_LOG_DEBUG("Reply-----------------------------------------------------");
    uint32_t context = Simulator::GetContext();
    std::cout<< context;
    Ptr<Packet> packet;
    Address senderAddress;
    Ptr<Node> thisNode = NodeList::GetNode(context);

     while ((packet = socket->RecvFrom (senderAddress)))
     {
         ReplyPacketHeader header;
         packet->RemoveHeader(header);
         Ipv4Address src_ip = header.GetSource();
         Vector myLocation = thisNode->GetObject<MobilityModel>()->GetPosition();
         Vector neighborNodeLocation;
        //uint16_t senderNode;
    int32_t nNodes = NodeList::GetNNodes ();

    for (int32_t i = 0; i < nNodes; ++i)
    {
        Ptr<Node> node = NodeList::GetNode (i);
        Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();

        int32_t ifIndex = ipv4->GetInterfaceForAddress (src_ip);
        if (ifIndex != -1)
        {
            //senderNode = i;
            Ptr<MobilityModel> neighborNodeModel = node->GetObject<MobilityModel>();
            neighborNodeLocation = neighborNodeModel->GetPosition();
        }
    }

    RTableEntry rTableEntryW;
    bool permanentTableVerifier = m_rTableW.LookupRoute (src_ip, rTableEntryW);
    NS_LOG_DEBUG("Is the route inside the table? " << permanentTableVerifier);
    if (permanentTableVerifier == false)
    {
        NS_LOG_DEBUG ("Received New WRoute!");
        RTableEntry newEntry(/*My Address=*/ thisNode->GetObject<Ipv4>()->GetAddress(1,0).GetLocal(), /*destination address=*/ header.GetSource(), /*time connected=*/0.0, /*time packet received=*/
        Simulator::Now (), /*time First packet received=*/ Simulator::Now (), /*my location=*/ myLocation, /*neighbor location=*/ neighborNodeLocation, header.GetNextLocation(), header.GetNextTimeInterval());
        Time lifeTime = TimeIntervalToTime(header.GetNextTimeInterval());
        Time myLifeTime = TimeIntervalToTime(thisNode->GetObject<MarkovChainMobilityModel>()->GetNextTime());
        newEntry.setlinkLifeTime(std::min(lifeTime,myLifeTime));
        m_rTableW.AddRoute (newEntry);
        NS_LOG_DEBUG ("New WRoute added to Wtable!");
    }
    else
    {
        uint16_t nextLoc = rTableEntryW.getNextLoc();
        uint16_t nextTime = rTableEntryW.getNextTime();
            if (header.GetNextLocation() == nextLoc)
            {
                if (header.GetNextTimeInterval() == nextTime)
                {

                }
                else
                    {
                        rTableEntryW.setNextTime(nextTime);
                        Time lifeTime = TimeIntervalToTime(header.GetNextTimeInterval());
                        Time myLifeTime = TimeIntervalToTime(thisNode->GetObject<MarkovChainMobilityModel>()->GetNextTime());
                        rTableEntryW.setlinkLifeTime(std::min(lifeTime,myLifeTime));
                        m_rTableW.Update(rTableEntryW);
                    }

            }
            else
            {
                rTableEntryW.setNextTime(nextTime);
                rTableEntryW.setNextLocation(nextLoc);
                Time lifeTime = TimeIntervalToTime(header.GetNextTimeInterval());
                Time myLifeTime = TimeIntervalToTime(thisNode->GetObject<MarkovChainMobilityModel>()->GetNextTime());
                rTableEntryW.setlinkLifeTime(std::min(lifeTime,myLifeTime));
                m_rTableW.Update(rTableEntryW);
            }

    }

     }
}
void
RoutingExperiment::ReceiveReplyWD(Ptr<Socket> socket)
{
    NS_LOG_DEBUG("ReplyWD-----------------------------------------------------");
    uint32_t context = Simulator::GetContext();
    std::cout<< context;
    Ptr<Packet> packet;
    Address senderAddress;
    Ptr<Node> thisNode = NodeList::GetNode(context);
    Vector myLocation = thisNode->GetObject<MobilityModel>()->GetPosition();
    Vector neighborNodeLocation;

     while ((packet = socket->RecvFrom (senderAddress)))
     {
         ReplyPacketHeader header;
         packet->RemoveHeader(header);
         Ipv4Address src_ip = header.GetSource();
//        uint16_t senderNode;
    int32_t nNodes = NodeList::GetNNodes ();

    for (int32_t i = 0; i < nNodes; ++i)
    {
        Ptr<Node> node = NodeList::GetNode (i);
        Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();

        int32_t ifIndex = ipv4->GetInterfaceForAddress (src_ip);
        if (ifIndex != -1)
        {
            //senderNode = i;
            Ptr<MobilityModel> neighborNodeModel = node->GetObject<MobilityModel>();
            neighborNodeLocation = neighborNodeModel->GetPosition();
        }
    }

    RTableEntry rTableEntryWD;
    bool permanentTableVerifier = m_rTableWD.LookupRoute (src_ip, rTableEntryWD);
    NS_LOG_DEBUG("Is the route inside the table? " << permanentTableVerifier);
    if (permanentTableVerifier == false)
    {
        NS_LOG_DEBUG ("Received New WRoute!");
        RTableEntry newEntry(/*My Address=*/ thisNode->GetObject<Ipv4>()->GetAddress(2,0).GetLocal(), /*destination address=*/ header.GetSource(), /*time connected=*/0.0, /*time packet received=*/
                Simulator::Now (), /*time First packet received=*/ Simulator::Now (), /*my location=*/ myLocation, /*neighbor location=*/ neighborNodeLocation, header.GetNextLocation(), header.GetNextTimeInterval());
        Time lifeTime = TimeIntervalToTime(header.GetNextTimeInterval());
        Time myLifeTime = TimeIntervalToTime(thisNode->GetObject<MarkovChainMobilityModel>()->GetNextTime());
        newEntry.setlinkLifeTime(std::min(lifeTime,myLifeTime));
        m_rTableWD.AddRoute (newEntry);
        NS_LOG_DEBUG ("New WRoute added to Wtable!");
    }
    else
    {
        uint16_t nextLoc = rTableEntryWD.getNextLoc();
        uint16_t nextTime = rTableEntryWD.getNextTime();
            if (header.GetNextLocation() == nextLoc)
            {
                if (header.GetNextTimeInterval() == nextTime)
                {

                }
                else
                    {
                        rTableEntryWD.setNextTime(nextTime);
                        Time lifeTime = TimeIntervalToTime(header.GetNextTimeInterval());
                        Time myLifeTime = TimeIntervalToTime(thisNode->GetObject<MarkovChainMobilityModel>()->GetNextTime());
                        rTableEntryWD.setlinkLifeTime(std::min(lifeTime,myLifeTime));
                        m_rTableWD.Update(rTableEntryWD);
                    }

            }
            else
            {
                rTableEntryWD.setNextTime(nextTime);
                rTableEntryWD.setNextLocation(nextLoc);
                Time lifeTime = TimeIntervalToTime(header.GetNextTimeInterval());
                Time myLifeTime = TimeIntervalToTime(thisNode->GetObject<MarkovChainMobilityModel>()->GetNextTime());
                rTableEntryWD.setlinkLifeTime(std::min(lifeTime,myLifeTime));
                m_rTableW.Update(rTableEntryWD);
            }

    }

     }
}

void
RoutingExperiment::CheckThroughput ()
{
    double mbs = (bytesTotal * 8.0) / 1000 / 1000;
    bytesTotal = 0;
    double mbsWD = (bytesTotalWD * 8.0) / 1000 / 1000;
    bytesTotalWD = 0;
    std::ofstream out (m_CSVfileName.c_str (), std::ios::app);

    out << (Simulator::Now ()).GetSeconds () << ","
            << mbs << ","
            << mbsWD << ","
            << currentSeqNo << ","
            << currentSeqNoWD << ","
            << packetsReceived << ","
            << packetsReceivedWD << ","
            << m_nSinks << ","
            << m_protocolName << ","
            << m_txp << ","
            << m_txDataRate << ","
            << m_txDataRateWD << ","
            << m_rxDataRate << ","
            << m_rxDataRateWD << ","
            << delay << ","
            << delayWD << ","
            << PhyTxDropCount << ","
            << PhyRxDropCount << ","
            << PhyTxDropCountWD << ","
            << PhyRxDropCountWD << ","
            << appPkt << ","
            << appPktWD << ","
            << counterAppTX << ","
            << counterAppTXWD << ","
            << counterAppRX << ","
            << counterAppRXWD << ","
            << counterTX << ","
            << counterTXWD << ","
            << counterRX << ","
            << counterRXWD << ""
            << std::endl;

    out.close ();

    packetsReceived = 0;
    currentSeqNo = 0;
    PhyTxDropCount = 0;
    PhyRxDropCount = 0;
    appPkt = 0;
    counterTX = 0;
    counterRX = 0;
    counterAppTX = 0;
    counterAppRX = 0;
    rcv = 0.0;
    sqhd = 0.0;
    delay = 0.0;
    rcvWD = 0.0;
    sqhdWD = 0.0;
    delayWD = 0.0;
    packetsReceivedWD = 0;
    currentSeqNoWD = 0;
    PhyTxDropCount = 0;
    PhyRxDropCount = 0;
    PhyTxDropCountWD = 0;
    PhyRxDropCountWD = 0;
    appPktWD = 0;
    counterTXWD = 0;
    counterRXWD = 0;
    counterAppTXWD = 0;
    counterAppRXWD = 0;

    Simulator::Schedule (Seconds (1.0), &RoutingExperiment::CheckThroughput, this);
}



Ptr<Socket>
RoutingExperiment::SetupPacketReceive (Ipv4Address addr, Ptr<Node> node)
{
    TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
    Ptr<Socket> sink = Socket::CreateSocket (node, tid);
    InetSocketAddress local = InetSocketAddress (addr, port);
    NS_LOG_DEBUG("Local Address W: " << local.GetIpv4());
    sink->Bind (local);
    sink->SetRecvCallback (MakeCallback (&RoutingExperiment::ReceivePacket, this));

    return sink;
}

Ptr<Socket>
RoutingExperiment::SetupPacketReceiveWD (Ipv4Address addr, Ptr<Node> node)
{
    TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
    Ptr<Socket> sink = Socket::CreateSocket (node, tid);
    InetSocketAddress local = InetSocketAddress (addr, portWD);
    NS_LOG_DEBUG("Local Address WD: " << local.GetIpv4());
    sink->Bind (local);
    sink->SetRecvCallback (MakeCallback (&RoutingExperiment::ReceivePacketWD, this));

    return sink;
}

Ptr<Socket>
RoutingExperiment:: SetupDiscoveryReceive (Ipv4Address addr, Ptr<Node> node)
{
    TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
    Ptr<Socket> sink = Socket::CreateSocket (node, tid);
    InetSocketAddress local = InetSocketAddress (addr, port + 1);
    //NS_LOG_DEBUG("Local Address W: " << local.GetIpv4());
    sink->Bind (local);
    sink->SetRecvCallback (MakeCallback (&RoutingExperiment::ReceiveDiscovery, this));

    return sink;
}

Ptr<Socket>
RoutingExperiment::SetupDiscoveryReceiveWD (Ipv4Address addr, Ptr<Node> node)
{
    TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
    Ptr<Socket> sink = Socket::CreateSocket (node, tid);
    InetSocketAddress local = InetSocketAddress (addr, portWD + 1);
    //NS_LOG_DEBUG("Local Address WD: " << local.GetIpv4());
    sink->Bind (local);
    sink->SetRecvCallback (MakeCallback (&RoutingExperiment::ReceiveDiscoveryWD, this));

    return sink;
}

Ptr<Socket>
RoutingExperiment::SetupReplyReceive (Ipv4Address addr, Ptr<Node> node)
{
    TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
    Ptr<Socket> sink = Socket::CreateSocket (node, tid);
    InetSocketAddress local = InetSocketAddress (addr, port + 2);
    //NS_LOG_DEBUG("Receive local Address WD: " << local.GetIpv4());
    sink->Bind (local);
    sink->SetRecvCallback (MakeCallback (&RoutingExperiment::ReceiveReply, this));

    return sink;
}

Ptr<Socket>
RoutingExperiment::SetupReplyReceiveWD (Ipv4Address addr, Ptr<Node> node)
{
    TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
    Ptr<Socket> sink = Socket::CreateSocket (node, tid);
    InetSocketAddress local = InetSocketAddress (addr, portWD + 2);
    //NS_LOG_DEBUG("Receive local Address WD: " << local.GetIpv4());
    sink->Bind (local);
    sink->SetRecvCallback (MakeCallback (&RoutingExperiment::ReceiveReplyWD, this));

    return sink;
}

std::string
RoutingExperiment::CommandSetup (int argc, char **argv)
{
    CommandLine cmd;
    cmd.AddValue ("CSVfileName", "The name of the CSV output file name", m_CSVfileName);
    cmd.AddValue ("traceMobility", "Enable mobility tracing", m_traceMobility);
    cmd.AddValue ("protocol", "1=OLSR;2=AODV;3=DSDV;4=DSR", m_protocol);
    cmd.Parse (argc, argv);
    return m_CSVfileName;
}

void
RoutingExperiment::MacTxDrop(Ptr<const Packet> p)
{
    NS_LOG_INFO("Packet Drop");
    MacTxDropCount++;
}

void
RoutingExperiment::PhyTxDrop(Ptr<const Packet> p)
{
    NS_LOG_INFO("Packet Drop");
    PhyTxDropCount++;
}
void
RoutingExperiment::PhyRxDrop(Ptr<const Packet> p, ns3::WifiPhyRxfailureReason reason)
{
    NS_LOG_INFO("Packet Drop and Reason is: " << reason);
    PhyRxDropCount++;
}

void
RoutingExperiment::PrintDrop()
{
    NS_LOG_DEBUG( Simulator::Now().GetSeconds() << "\t MAC TX Drop: " << MacTxDropCount << "\t PHY TX Drop: "<< PhyTxDropCount << "\t PHY RX Drop: " << PhyRxDropCount << "\n");
    Simulator::Schedule(Seconds(5.0), &RoutingExperiment::PrintDrop,this);
}

void
RoutingExperiment::PrintDropWD()
{
    NS_LOG_DEBUG( Simulator::Now().GetSeconds() << "\t WDMAC TX Drop: " << MacTxDropCountWD << "\t WDPHY TX Drop: "<< PhyTxDropCountWD << "\t WDPHY RX Drop: " << PhyRxDropCountWD << "\n");
    Simulator::Schedule(Seconds(5.0), &RoutingExperiment::PrintDropWD, this);
}

void
RoutingExperiment::MacTxDropWD(Ptr<const Packet> p)
{
    NS_LOG_INFO("Packet Drop WD");
    MacTxDropCountWD++;
}

void
RoutingExperiment::PhyTxDropWD(Ptr<const Packet> p)
{
    NS_LOG_INFO("Packet Drop WD");
    PhyTxDropCountWD++;
}
void
RoutingExperiment::PhyRxDropWD(Ptr<const Packet> p, ns3::WifiPhyRxfailureReason reason)
{
    NS_LOG_INFO("Packet Drop WD and Reason is: " << reason);
    PhyRxDropCountWD++;
}



void
RoutingExperiment::LinkLifeTimer()
{
    m_rTableW.GetInActiveRoutes();
    m_rTableWD.GetInActiveRoutes();
    Simulator::Schedule(Seconds(1.0), &RoutingExperiment::LinkLifeTimer, this);
}

void
RoutingExperiment::CourseChange (std::string context, Ptr<const MobilityModel> model)
{
    Vector position = model->GetPosition();
    NS_LOG_DEBUG (context << " x = " << position.x << ", y = " << position.y);
    //Simulator::Schedule(Seconds(1.0), &RoutingExperiment::CourseChange, this);
}

int
main (int argc, char *argv[])
{
    LogComponentEnable ("ManetRoutingCompare", LOG_LEVEL_DEBUG);
    RoutingExperiment experiment;
    std::string CSVfileName = experiment.CommandSetup (argc,argv);

    //blank out the last output file and write the column headers
    std::ofstream out (CSVfileName.c_str ());
    out << "SimulationSecond," <<
            "Throughput," <<
            "ThroughputWD," <<
            "TotalPacketsSent," <<
            "TotalPacketsSentWD," <<
            "PacketsReceived," <<
            "PacketsReceivedWD," <<
            "NumberOfSinks," <<
            "RoutingProtocol," <<
            "TransmissionPower," <<
            "TxDataRate," <<
            "TxDataRateWD," <<
            "RxDataRate," <<
            "RxDataRateWD," <<
            "Delay," <<
            "DelayWD," <<
            "TxDrop," <<
            "RxDrop," <<
            "TxDropWD," <<
            "RxDropWD," <<
            "BytesTotal," <<
            "BytesTotalWD," <<
            "NumOfTxAppPkts," <<
            "NumOfTxAppPktsWD," <<
            "NumOfRxAppPkts," <<
            "NumOfRxAppPktsWD," <<
            "TxPackets," <<
            "TxPacketsWD," <<
            "RxPackets," <<
            "RxPacketsWD" <<
            std::endl;
    out.close ();

    uint32_t nSinks = 10;
    double txp = 7.5;

    experiment.Run (nSinks, txp, CSVfileName);
}

void
RoutingExperiment::Run (int nSinks, double txp, std::string CSVfileName)
{
    Packet::EnablePrinting ();
    m_nSinks = nSinks;
    m_txp = txp;
    m_CSVfileName = CSVfileName;

    uint32_t nWifis = 10;
    double m_dataStart = 0.01;
    double TotalTime = 180.0;
    //std::string rate ("2Mbps");
    //  std::string phyMode ("ErpOfdmRate36Mbps");
    //  std::string phyModeWD ("ErpOfdmRate54Mbps");
    std::string tr_name ("manet-routing-compare");
    int nodeSpeed = 30; //in m/s
    int nodePause = 0.5; //in s
    std::string rtslimit = "2200";

    m_protocolName = "protocol";

    //  Config::SetDefault  ("ns3::OnOffApplication::PacketSize",StringValue ("1024"));
    //  Config::SetDefault ("ns3::OnOffApplication::DataRate",  StringValue (rate));

    // turn off RTS/CTS for frames below 2200

    Config::SetDefault ("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue (rtslimit));
    //Set Non-unicastMode rate to unicast mode
    // Config::SetDefault ("ns3::WifiRemoteStationManager::NonUnicastMode",StringValue (phyMode));


    adhocNodes.Create (nWifis);

    // setting up wifi phy and channel using helpers
    WifiHelper wifi, wifiD;
    wifi.SetStandard (WIFI_PHY_STANDARD_80211b);
    wifiD.SetStandard(WIFI_PHY_STANDARD_80211n_5GHZ);

    YansWifiPhyHelper wifiPhy =  YansWifiPhyHelper::Default ();
    YansWifiPhyHelper wifiPhyWD =  YansWifiPhyHelper::Default ();
    YansWifiChannelHelper wifiChannelW, wifiChannelWD;//, wifiChannelLTE;
    wifiChannelW.SetPropagationDelay ("ns3::ConstantSpeedPropagationDelayModel");
    wifiChannelW.AddPropagationLoss ("ns3::FriisPropagationLossModel");
    wifiPhy.SetChannel (wifiChannelW.Create ());

    wifiChannelWD.SetPropagationDelay ("ns3::ConstantSpeedPropagationDelayModel");
    wifiChannelWD.AddPropagationLoss ("ns3::FriisPropagationLossModel");
    wifiPhyWD.SetChannel (wifiChannelWD.Create ());
    // Add a mac and disable rate control
    WifiMacHelper wifiMac;
    wifiMac.SetType ("ns3::AdhocWifiMac");
    //"DataMode", StringValue ("ErpOfdmRate54Mbps"), "ControlMode", StringValue("ErpOfdmRate54Mbps"),
    //wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager", "NonUnicastMode", StringValue ("DsssRate11Mbps"));
    wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager", "DataMode", StringValue ("DsssRate11Mbps"), "ControlMode",
            StringValue ("DsssRate11Mbps"));
    wifiD.SetRemoteStationManager ("ns3::MinstrelHtWifiManager", "NonUnicastMode", StringValue ("ErpOfdmRate36Mbps"));

    wifiPhy.Set ("TxPowerStart",DoubleValue (txp));
    wifiPhy.Set ("TxPowerEnd", DoubleValue (txp));

    wifiPhyWD.Set ("TxPowerStart",DoubleValue (txp));
    wifiPhyWD.Set ("TxPowerEnd", DoubleValue (txp));

    NetDeviceContainer adhocDevices = wifi.Install (wifiPhy, wifiMac, adhocNodes);
    NetDeviceContainer adhocDevicesWD = wifiD.Install (wifiPhyWD, wifiMac, adhocNodes);

    MobilityHelper mobility;
//	mobility.SetMobilityModel ("ns3::MarkovChainMobilityModel",
//	  "Bounds", BoxValue (Box (0, 1000, 0, 1000, 0, 10)),
//	  "TimeStep", TimeValue (Seconds (0.1)),
//	  "Alpha", DoubleValue (0.85),
//	  "MeanVelocity", StringValue ("ns3::UniformRandomVariable[Min=80|Max=120]"),
//	  "MeanDirection", StringValue ("ns3::UniformRandomVariable[Min=0|Max=6.283185307]"),
//	  "MeanPitch", StringValue ("ns3::UniformRandomVariable[Min=0.05|Max=0.05]"),
//	  "NormalVelocity", StringValue ("ns3::NormalRandomVariable[Mean=0.0|Variance=0.0|Bound=0.0]"),
//	  "NormalDirection", StringValue ("ns3::NormalRandomVariable[Mean=0.0|Variance=0.2|Bound=0.4]"),
//	  "NormalPitch", StringValue ("ns3::NormalRandomVariable[Mean=0.0|Variance=0.02|Bound=0.04]"));
//	mobility.SetPositionAllocator ("ns3::RandomBoxPositionAllocator",
//	  "X", StringValue ("ns3::UniformRandomVariable[Min=0|Max=1000]"),
//	  "Y", StringValue ("ns3::UniformRandomVariable[Min=0|Max=1000]"),
//	  "Z", StringValue ("ns3::UniformRandomVariable[Min=0|Max=10]"));

    int64_t streamIndex = 0; // used to get consistent mobility across scenarios

    ObjectFactory pos;
    pos.SetTypeId ("ns3::RandomBoxPositionAllocator");
    pos.Set ("X", StringValue ("ns3::UniformRandomVariable[Min=0|Max=200.0]"));
    pos.Set ("Y", StringValue ("ns3::UniformRandomVariable[Min=0|Max=200.0]"));
    pos.Set ("Z", StringValue ("ns3::UniformRandomVariable[Min=0.0|Max=100.0]"));

    Ptr<PositionAllocator> taPositionAlloc = pos.Create ()->GetObject<PositionAllocator> ();
    streamIndex += taPositionAlloc->AssignStreams (streamIndex);

    std::stringstream ssSpeed;
    //ssSpeed << "ns3::UniformRandomVariable[Min=0.0|Max=" << nodeSpeed << "]";
     ssSpeed << "ns3::ConstantRandomVariable[Constant=" << nodeSpeed << "]";
    std::stringstream ssPause;
    ssPause << "ns3::ConstantRandomVariable[Constant=" << nodePause << "]";

    mobility.SetMobilityModel("ns3::MarkovChainMobilityModel",
            "BoundsOne",BoxValue (Box (0.0, 200.0, 0.0, 200.0, 0.0, 100.0)),
            "BoundsTwo",BoxValue (Box (150.0, 475.0, 300.0, 600.0, 0.0, 100.0)),
            "BoundsThree",BoxValue (Box (300.0, 600.0, 0.0, 200.0, 0.0, 100.0)),
            "BoundsFour",BoxValue (Box (525.0, 800.0, 300.0, 600.0, 0.0, 100.0)),
            "BoundsFive",BoxValue (Box (700.0, 1000.0, 0.0, 200.0, 0.0, 100.0)),
            "Speed", StringValue (ssSpeed.str ()),
            "Pause", StringValue (ssPause.str ()),
            "PositionAllocator", PointerValue (taPositionAlloc));
    mobility.SetPositionAllocator (taPositionAlloc);
    mobility.Install (adhocNodes);
    streamIndex += mobility.AssignStreams (adhocNodes, streamIndex);
    NS_UNUSED (streamIndex); // From this point, streamIndex is unused);

    //  AodvHelper aodv;
    //  OlsrHelper olsr;
    //  DsdvHelper dsdv;
    //  DsrHelper dsr;
    //  DsrMainHelper dsrMain;
    //  Ipv4ListRoutingHelper list;
    //  InternetStackHelper internet;
    //
    //  switch (m_protocol)
    //    {
    //    case 1:
    //      list.Add (olsr, 100);
    //      m_protocolName = "OLSR";
    //      break;
    //    case 2:
    //      list.Add (aodv, 100);
    //      m_protocolName = "AODV";
    //      break;
    //    case 3:
    //      list.Add (dsdv, 100);
    //      m_protocolName = "DSDV";
    //      break;
    //    case 4:
    //      m_protocolName = "DSR";
    //      break;
    //    default:
    //      NS_FATAL_ERROR ("No such protocol:" << m_protocol);
    //    }
    //
    //  if (m_protocol < 4)
    //    {
    //      internet.SetRoutingHelper (list);
    //      internet.Install (adhocNodes);
    //    }
    //  else if (m_protocol == 4)
    //    {
    //      internet.Install (adhocNodes);
    //      dsrMain.Install (dsr, adhocNodes);
    //    }

    InternetStackHelper stack;
    stack.Install (adhocNodes);

    NS_LOG_INFO ("assigning ip address");

    Ipv4AddressHelper addressAdhoc, addressAdhocWD;
    addressAdhoc.SetBase ("10.1.1.0", "255.255.255.0");
    addressAdhocWD.SetBase ("10.1.2.0", "255.255.255.0");
    Ipv4InterfaceContainer adhocInterfaces, adhocInterfacesWD;
    adhocInterfaces = addressAdhoc.Assign (adhocDevices);
    adhocInterfacesWD = addressAdhocWD.Assign(adhocDevicesWD);

    for(uint16_t i=0;i < nWifis; i++)
    {
        Ptr<Node> node = NodeList::GetNode (i);
        Ipv4Address nodeAddress = node->GetObject<Ipv4> ()->GetAddress (1, 0).GetLocal ();
        Ipv4Address nodeAddressWD = node->GetObject<Ipv4> ()->GetAddress (2, 0).GetLocal ();
        m_interfaceMap.insert(std::pair<Ipv4Address, Ipv4Address>(nodeAddress, nodeAddressWD));
    }
    //  Ipv4GlobalRoutingHelper routingHelper;
    //
    //  routingHelper.PopulateRoutingTables();
    //
    //  routingHelper.RecomputeRoutingTables();
    //
    //  Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper> ((tr_name + ".routes"), std::ios::out);
    //  PrintRTable(routingStream,Seconds(55.0));
//    ObjectFactory factory;
//    factory.SetTypeId("ns3::DiscoveryApplication");
    for(uint16_t i=0;i < nWifis; i++)
    {
        Ptr<Node> node = NodeList::GetNode (i);
        Ptr<DiscoveryApplication> app = Create<DiscoveryApplication> ();
        Ipv4Address nodeAddress = node->GetObject<Ipv4> ()->GetAddress (1, 0).GetLocal ();
        Ipv4Address nodeAddressWD = node->GetObject<Ipv4> ()->GetAddress (2, 0).GetLocal ();
        Ipv4Address broadCast1 = node->GetObject<Ipv4>()->GetAddress(1,0).GetBroadcast();
        Ipv4Address broadCast2 = node->GetObject<Ipv4>()->GetAddress(2,0).GetBroadcast();
        //NS_LOG_DEBUG("Broadcast Address W: " << broadCast1);
        //NS_LOG_DEBUG("Broadcast Address WD: " << broadCast2);
        node->AddApplication(app);
        app->Setup(InetSocketAddress(broadCast1, port + 1), InetSocketAddress(broadCast2, portWD + 1), Seconds(1),10, 81);
        DiscoverySink = SetupDiscoveryReceive (broadCast1, node);
        DiscoverySinkWD = SetupDiscoveryReceiveWD (broadCast2, node);
        ReplySink = SetupReplyReceive(nodeAddress, node);
        ReplySinkWD = SetupReplyReceiveWD(nodeAddressWD, node);
    }


    for (uint32_t i = 0; i <= m_nSinks - 1; i++ )
    {
        Ptr<Node> node = NodeList::GetNode (i);
        Ipv4Address nodeAddress = node->GetObject<Ipv4> ()->GetAddress (1, 0).GetLocal();
        Ipv4Address nodeAddressWD = node->GetObject<Ipv4> ()->GetAddress (2, 0).GetLocal ();
        sink = SetupPacketReceive (nodeAddress, node);
        sinkWD = SetupPacketReceiveWD (nodeAddressWD, node);
    }

//    for (uint32_t clientNode = 0; clientNode <= nWifis - 1; clientNode++ )
//    {
//        for (uint32_t j = 0; j <= m_nSinks - 1; j++ )
//        {
//            OnOffHelper onoff1 ("ns3::UdpSocketFactory", Address (InetSocketAddress (adhocInterfaces.GetAddress (j), port)));
//            onoff1.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
//            onoff1.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
//            onoff1.SetAttribute ("DataRate", StringValue ("2Kbps"));
//            OnOffHelper onoffWD1 ("ns3::UdpSocketFactory", Address (InetSocketAddress (adhocInterfacesWD.GetAddress (j), portWD)));
//            onoffWD1.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
//            onoffWD1.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
//            onoffWD1.SetAttribute ("DataRate", StringValue ("2Kbps"));
//            //            OnOffHelper onoff2 ("ns3::UdpSocketFactory", Address (InetSocketAddress (adhocInterfaces.GetAddress (j), port)));
//            //            onoff2.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
//            //            onoff2.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
//            //            onoff2.SetAttribute ("DataRate", StringValue ("4Mbps"));
//            //            OnOffHelper onoff3 ("ns3::UdpSocketFactory", Address (InetSocketAddress (adhocInterfaces.GetAddress (j), port)));
//            //            onoff3.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
//            //            onoff3.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
//            //            onoff3.SetAttribute ("DataRate", StringValue ("8Mbps"));

//            if (j != clientNode)
//            {
//                ApplicationContainer apps1 = onoff1.Install (adhocNodes.Get (clientNode));
//                ApplicationContainer appsWD1 = onoffWD1.Install (adhocNodes.Get (clientNode));
//                //                ApplicationContainer apps2 = onoff2.Install (adhocNodes.Get (clientNode));
//                //                ApplicationContainer apps3 = onoff3.Install (adhocNodes.Get (clientNode));
//                Ptr<UniformRandomVariable> var = CreateObject<UniformRandomVariable> ();
//                apps1.Start (Seconds (var->GetValue (m_dataStart, m_dataStart + 0.1)));
//                appsWD1.Start (Seconds (var->GetValue (m_dataStart, m_dataStart + 0.1)));
//                //                apps2.Start (Seconds (var->GetValue (m_dataStart + 20.0, m_dataStart + 20.1)));
//                //                apps3.Start (Seconds (var->GetValue (m_dataStart + 45.0, m_dataStart + 45.1)));
//                apps1.Stop (Seconds (TotalTime));
//                appsWD1.Stop (Seconds (TotalTime));
//                //                apps2.Stop (Seconds (TotalTime));
//                //                apps3.Stop (Seconds (TotalTime));
//            }
//        }
//    }

    populate_queue<deadline_priority_queue>(ordered_queue);
    NS_LOG_INFO ("Configure Tracing.");
    //tr_name = tr_name + "_" + m_protocolName +"_" + nodes + "nodes_" + sNodeSpeed + "speed_" + sNodePause + "pause_" + sRate + "rate";

    AsciiTraceHelper ascii;
    Ptr<OutputStreamWrapper> osw = ascii.CreateFileStream ( (tr_name + ".tr").c_str());
    wifiPhy.EnableAsciiAll (osw);
    wifiPhy.EnablePcap("manet-routing-compare", adhocDevices);
    MobilityHelper::EnableAsciiAll (ascii.CreateFileStream (tr_name + ".mob"));

    Ptr<FlowMonitor> flowmon;
    FlowMonitorHelper flowmonHelper;
    flowmon = flowmonHelper.InstallAll ();

    NS_LOG_INFO ("Run Simulation.");

    CheckThroughput ();

    for(uint16_t i =0 ;i< NodeList::GetNNodes(); i++)
    {
        Ptr<Node> n= NodeList::GetNode(i);
        NS_LOG_DEBUG("Node "<<i<<" has "<<n->GetNApplications()<< " applivations");
    }

    Config::ConnectWithoutContext("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/RemoteStationManager/$ns3::MinstrelHtWifiManager/Rate", MakeCallback (&dataRate));
    Config::ConnectWithoutContext("/NodeList/*/DeviceList/0/$ns3::WifiNetDevice/Mac/MacTxDrop", MakeCallback(&RoutingExperiment::MacTxDrop, this));
    Config::ConnectWithoutContext("/NodeList/*/DeviceList/0/$ns3::WifiNetDevice/Phy/PhyRxDrop", MakeCallback(&RoutingExperiment::PhyRxDrop, this));
    Config::ConnectWithoutContext("/NodeList/*/DeviceList/0/$ns3::WifiNetDevice/Phy/PhyTxDrop", MakeCallback(&RoutingExperiment::PhyTxDrop, this));
    Config::ConnectWithoutContext("/NodeList/*/DeviceList/1/$ns3::WifiNetDevice/Mac/MacTxDrop", MakeCallback(&RoutingExperiment::MacTxDropWD, this));
    Config::ConnectWithoutContext("/NodeList/*/DeviceList/1/$ns3::WifiNetDevice/Phy/PhyRxDrop", MakeCallback(&RoutingExperiment::PhyRxDropWD, this));
    Config::ConnectWithoutContext("/NodeList/*/DeviceList/1/$ns3::WifiNetDevice/Phy/PhyTxDrop", MakeCallback(&RoutingExperiment::PhyTxDropWD, this));
    Config::ConnectWithoutContext("/NodeList/*/ApplicationList/0/$ns3::DiscoveryApplication/Tx", MakeCallback(&RoutingExperiment::txApp, this));
    Config::ConnectWithoutContext("/NodeList/*/ApplicationList/0/$ns3::DiscoveryApplication/Tx", MakeCallback(&RoutingExperiment::txAppWD, this));
    Config::Connect("/NodeList/*/DeviceList/0/$ns3::WifiNetDevice/Phy/MonitorSnifferTx", MakeCallback(&RoutingExperiment::Tx, this));
    Config::Connect("/NodeList/*/DeviceList/0/$ns3::WifiNetDevice/Phy/MonitorSnifferRx", MakeCallback(&RoutingExperiment::Rx, this));
    Config::Connect("/NodeList/*/DeviceList/1/$ns3::WifiNetDevice/Phy/MonitorSnifferTx", MakeCallback(&RoutingExperiment::TxWD, this));
    Config::Connect("/NodeList/*/DeviceList/1/$ns3::WifiNetDevice/Phy/MonitorSnifferRx", MakeCallback(&RoutingExperiment::RxWD, this));
    Config::Connect("/NodeList/*/$ns3::MobilityModel/CourseChange", MakeCallback (&RoutingExperiment::CourseChange, this));
    Simulator::Schedule(Seconds(5.0), &RoutingExperiment::PrintDrop, this);
    Simulator::Schedule(Seconds(5.0), &RoutingExperiment::PrintDropWD, this);
    Simulator::Schedule(Seconds(1.0), &RoutingExperiment::LinkLifeTimer, this);
    Simulator::Schedule(Seconds(20.0), &RoutingExperiment::StartTaskGeneration, this);

    Simulator::Stop (Seconds (TotalTime));

    //	AnimationInterface anim("anim.xml");

    Simulator::Run ();
    //StartTaskGeneration();
    PrintDrop();
    PrintDropWD();

    // Print per flow statistics
    flowmon->CheckForLostPackets ();
    Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier> (flowmonHelper.GetClassifier ());
    std::map<FlowId, FlowMonitor::FlowStats> stats = flowmon->GetFlowStats ();

    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 " << iter->first << " (" << t.sourceAddress << " -> " << t.destinationAddress << ")\n");
        NS_LOG_UNCOND( "  Tx Bytes:   " << iter->second.txBytes << "\n");
        NS_LOG_UNCOND( "  Rx Bytes:   " << iter->second.rxBytes << "\n");
        NS_LOG_UNCOND( "  Throughput: " << iter->second.rxBytes * 8.0 / (TotalTime - m_dataStart) / 1000 / 1000 << " Mbps\n");
        NS_LOG_UNCOND( "  Rx Segments: " << iter->second.rxPackets << "\n");
        NS_LOG_UNCOND( "  Lost packets: " << iter->second.lostPackets << "\n");
        NS_LOG_UNCOND( "  Packet Delivery Fraction: " << (float) iter->second.rxBytes / iter->second.txBytes * 100 << " %\n");
        NS_LOG_UNCOND( "  Average End-to-end Delay: " << iter->second.delaySum / iter->second.rxPackets / 1000 << " us\n");
        NS_LOG_UNCOND( "  Throughput: " << iter->second.rxBytes * 8.0 / (iter->second.timeLastRxPacket.GetSeconds()-iter->second.timeFirstTxPacket.GetSeconds()) / 1024 / 1024 << " Mbps");

    }


    flowmon->SerializeToXmlFile ((tr_name + ".flowmon").c_str(), true, true);

    Simulator::Destroy ();
}


//  OnOffHelper onoff1 ("ns3::UdpSocketFactory",Address ());
//  onoff1.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
//  onoff1.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
//  onoff1.SetAttribute ("DataRate", StringValue ("2Mbps"));
//
//  OnOffHelper onoff2 ("ns3::UdpSocketFactory",Address ());
//  onoff2.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
//  onoff2.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
//  onoff2.SetAttribute ("DataRate", StringValue ("4Mbps"));
//
//
//  for (int i = 0; i < nSinks; i++)
//    {
//      Ptr<Socket> sink = SetupPacketReceive (adhocInterfaces.GetAddress (i), adhocNodes.Get (i));
//      //sink->TraceConnectWithoutContext("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/RemoteStationManager/$ns3::MinstrelHtWifiManager/Rate", MakeCallback (&dataRate));
//      AddressValue remoteAddress (InetSocketAddress (adhocInterfaces.GetAddress (i), port));
//      onoff1.SetAttribute ("Remote", remoteAddress);
//      onoff2.SetAttribute ("Remote", remoteAddress);
//
//      Ptr<UniformRandomVariable> var = CreateObject<UniformRandomVariable> ();
//      ApplicationContainer temp = onoff1.Install (adhocNodes.Get (i + nSinks));
//      ApplicationContainer temp1 = onoff2.Install (adhocNodes.Get (i + nSinks));
//      temp.Start (Seconds (var->GetValue (1.0,1.2)));
//      temp1.Start (Seconds (var->GetValue (35.0, 35.2)));
//
//      temp.Stop (Seconds (TotalTime));
//      temp1.Stop (Seconds (TotalTime));
//    }

//  Address sinkAddress (InetSocketAddress (adhocInterfaces.GetAddress (1), port)); // interface of n1
//  PacketSinkHelper packetSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), port));
//  Ptr<Socket> ns3UdpSocket1;
//  for (int i = 0; i < nSinks; i++)
//     {
//	  	  ApplicationContainer sinkApps1 = packetSinkHelper.Install (adhocNodes.Get (i + nSinks)); //n2 as sink
//	  	  sinkApps1.Start (Seconds (0.));
//	  	  sinkApps1.Stop (Seconds (TotalTime));
//	  	  ns3UdpSocket1 = Socket::CreateSocket (adhocNodes.Get (i), UdpSocketFactory::GetTypeId ()); //source at n0
//
//  // Create UDP application at n0
//  Ptr<DiscoveryApplication> app1 = CreateObject<DiscoveryApplication> ();
//  app1->Setup (ns3UdpSocket1, sinkAddress1, 1024, 10000, DataRate ("2Mbps"));
//  adhocNodes.Get (0)->AddApplication (app1);
//  app1->SetStartTime (Seconds (1.));
//  app1->SetStopTime (Seconds (TotalTime));
//
//
//
//  ApplicationContainer sinkApps2 = packetSinkHelper1.Install (adhocNodes.Get (1)); //n2 as sink
//  sinkApps2.Start (Seconds (20.));
//  sinkApps2.Stop (Seconds (TotalTime));
//
//  Ptr<Socket> ns3UdpSocket2 = Socket::CreateSocket (adhocNodes.Get (0), UdpSocketFactory::GetTypeId ()); //source at n0
//
//  // Create UDP application at n0
//  Ptr<DiscoveryApplication> app2 = CreateObject<DiscoveryApplication> ();
//  app2->Setup (ns3UdpSocket2, sinkAddress2, 1024, 10000, DataRate ("4Mbps"));
//  adhocNodes.Get (0)->AddApplication (app2);
//  app2->SetStartTime (Seconds (21.));
//  app2->SetStopTime (Seconds (TotalTime));

//  std::stringstream ss;
//  ss << nWifis;
//  std::string nodes = ss.str ();
//
//  std::stringstream ss2;
//  ss2 << nodeSpeed;
//  std::string sNodeSpeed = ss2.str ();
//
//  std::stringstream ss3;
//  ss3 << nodePause;
//  std::string sNodePause = ss3.str ();
//
//  std::stringstream ss4;
//  ss4 << rate;
//  std::string sRate = ss4.str ();
