/**
  * By Luis Miguel
  */
#include <fstream>
#include "ns3/core-module.h"
#include "ns3/simulator-module.h"
#include "ns3/node-module.h"
#include "ns3/helper-module.h"
#include "ns3/global-routing-module.h"
#include "ns3/wifi-module.h"
#include "ns3/mobility-module.h"
#include <time.h>

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("WirelessMeshNetwork");

/* TOPOLOGY:

     WIRED CSMA             WIRELESS CHANNEL
   ______________
   O            O  ))          (( O ))           (( O
   N3           N0                N1                N2  

*/

int main (int argc, char *argv[])
{
  clock_t start;
  clock_t finish;
  double diff;
  
  double simtime=2.0;
  uint16_t port = 200;
  //uint32_t nPackets = 1;
  uint32_t nNodes = 3;
  uint32_t nGateways = 1;
  uint32_t nClients = 1;
  uint32_t nOtherNodes=nNodes-nGateways-nClients;
  uint32_t seed = 1;
  RandomVariable::UseGlobalSeed(seed,seed,seed,seed,seed,seed);
  CommandLine cmd;
  cmd.Parse (argc, argv);
  
/*
  LogComponentEnable("AdhocWifiMac", LogLevel(LOG_LEVEL_ALL | LOG_PREFIX_FUNC | LOG_PREFIX_TIME)); 
  LogComponentEnable("MacLow", LogLevel(LOG_LEVEL_ALL | LOG_PREFIX_FUNC | LOG_PREFIX_TIME));
  LogComponentEnable("ArpL3Protocol", LogLevel(LOG_LEVEL_ALL | LOG_PREFIX_FUNC | LOG_PREFIX_TIME));
  */
  
  //Gateways to the Internet
  NodeContainer gwNodes;
  gwNodes.Create (nGateways);
  
  //All Mesh Nodes
  NodeContainer meshNodes;
  meshNodes.Add (gwNodes);
  meshNodes.Create (nOtherNodes);
  
  //Mesh Clients
  NodeContainer clientNodes;
  clientNodes.Create(nClients);
  
  // Finish adding rest of ALL Nodes
  meshNodes.Add (clientNodes);

  
  //The supper fast CSMA Network (the Internet)
  NodeContainer csmaNodes;
  csmaNodes.Create (1); //One internet server
  csmaNodes.Add(gwNodes);
  
  //Keep our global sink
  NodeContainer internetNodes;
  internetNodes.Add(csmaNodes.Get (0));
  
  //INTERNET SETUP
  CsmaHelper csma;
  csma.SetChannelAttribute ("DataRate", 
        DataRateValue (DataRate (DataRate(5000000000ull))));
        // 5Tbits (so csma not bottleneck)

  NetDeviceContainer csmaDevices;
  csmaDevices = csma.Install(csmaNodes);
  
  //MESH SETUP
  Ptr<WifiChannel> channel = CreateObject<WifiChannel> ();
  channel->SetPropagationDelayModel (
    CreateObject<ConstantSpeedPropagationDelayModel> ());

  Ptr<LogDistancePropagationLossModel> log =
    CreateObject<LogDistancePropagationLossModel> ();

  log->SetReferenceModel (CreateObject<FriisPropagationLossModel> ());

  channel->SetPropagationLossModel(log);

  WifiHelper wifi;
  wifi.SetPhy("ns3::WifiPhy");
  wifi.SetMac("ns3::AdhocWifiMac");
  wifi.SetRemoteStationManager ("ns3::ArfWifiManager","RtsCtsThreshold",StringValue("0"));
  NetDeviceContainer wifiDevices;
  wifiDevices = wifi.Install (meshNodes, channel);

  MobilityHelper mobility;
  
  Ptr<ListPositionAllocator> posAlloc = 
    CreateObject<ListPositionAllocator> ();
  double x = 0.0;
  for (uint32_t i = 0; i < nNodes; ++i)
    {
      posAlloc->Add (Vector (x, 0.0, 0.0));
      x += 90.0;
    }
  
  mobility.SetPositionAllocator(posAlloc);
  mobility.SetMobilityModel("ns3::StaticMobilityModel");

  mobility.Install(meshNodes);
  
  
  InternetStackHelper stack;
  stack.Install (meshNodes);
  stack.Install (internetNodes);
  
  Ipv4AddressHelper address;

  address.SetBase("192.168.0.0","255.255.255.0");
  Ipv4InterfaceContainer wifiInterfaces;
  wifiInterfaces = address.Assign (wifiDevices);
  
  address.SetBase("172.16.0.0","255.255.255.0");
  Ipv4InterfaceContainer csmaInterfaces;
  csmaInterfaces = address.Assign (csmaDevices);

  
  NS_LOG_INFO ("Enabling OLSR routing on all backbone nodes");
  OlsrHelper olsr;
  olsr.Install(meshNodes);
  
  GlobalRouteManager::PopulateRoutingTables ();
 
  OnOffHelper onoff ("ns3::UdpSocketFactory",InetSocketAddress(csmaInterfaces.GetAddress (0),port));
  onoff.SetAttribute ("OnTime", RandomVariableValue (ConstantVariable (simtime)));
  onoff.SetAttribute ("OffTime", RandomVariableValue (ConstantVariable (0)));
  onoff.SetAttribute ("DataRate", DataRateValue (DataRate (600000000ull)));
  onoff.SetAttribute ("PacketSize", UintegerValue (512));

  PacketSinkHelper sink ("ns3::UdpSocketFactory",InetSocketAddress(Ipv4Address::GetAny (), port));

  ApplicationContainer clientApps = onoff.Install (clientNodes);
  NS_LOG_INFO("Number of applications are: " << clientApps.GetN());
  
  clientApps.Start(Seconds(0));
  
  clientApps.Stop (Seconds(simtime));
  
  
  ApplicationContainer internetApps = sink.Install(internetNodes);
  internetApps.Start (Seconds (0));
  internetApps.Stop (Seconds (simtime));
  
  Simulator::Stop (Seconds (simtime));
  
  
  NS_LOG_INFO ("Configure Tracing.");
  std::ofstream ascii;
  ascii.open ("wifi-mesh.tr");
  WifiHelper::EnableAsciiAll (ascii);
  CsmaHelper::EnableAsciiAll (ascii);
  
  
  CsmaHelper::EnablePcapAll("wifi-mesh");
  WifiHelper::EnablePcapAll("wifi-mesh");

  start = clock();
  Simulator::Run();
  finish = clock();
  diff = ((double)finish-(double)start)/ CLOCKS_PER_SEC;
  std::cout << "Done in " << diff << " secs" << std::endl;
  Simulator::Destroy();
  return 0;
}

