
#include <fstream>
#include "ns3/core-module.h"
#include "ns3/internet-module.h"
#include "ns3/csma-module.h"
#include "ns3/applications-module.h"
#include "ns3/ipv6-header.h"
#include "ns3/point-to-point-module.h"
#include "ns3/wifi-module.h"
#include "ns3/mobility-module.h"

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("My net Example");

int main (int argc, char **argv)
{
  bool verbose = false;
  uint32_t nWiFi = 10;


  CommandLine cmd;
  cmd.AddValue ("verbose", "log components", verbose);
  cmd.Parse (argc, argv);

  if (verbose)
    {
    }

  NS_LOG_INFO ("Create nodes.");


  // Create Nodes

  Ptr<Node> h0 = CreateObject<Node> ();
  Names::Add ("SrcNode", h0);
  Ptr<Node> h1 = CreateObject<Node> ();
  Names::Add ("DesNode", h1);
  Ptr<Node> r0 = CreateObject<Node> ();
  Names::Add ("Router 0", r0);
  Ptr<Node> r1 = CreateObject<Node> ();
  Names::Add ("Router 1", r1);
  Ptr<Node> r2 = CreateObject<Node> ();
  Names::Add ("Router 2", r2);
  Ptr<Node> r3 = CreateObject<Node> ();
  Names::Add ("Router 3", r3);
  Ptr<Node> wAP1 = CreateObject<Node> ();
  Names::Add ("AccessPoint 1", wAP1);




  // Create Node containers and assigne 2 Ptr<> param to this conatiners
  NodeContainer h0r0 (h0, r0);
  NodeContainer h1r3 (h1, r3);// interface (r2)=1 --> h1, (r2)=2,3 --> r1,r3
  NodeContainer r0r1 (r0, r1);
  NodeContainer r1r2 (r1, r2);
  NodeContainer r2r3 (r2, r3);
  NodeContainer r0r3 (r0, r3);
  NodeContainer r0r2 (r0, r2);
  NodeContainer r1r3 (r1, r3);
  NodeContainer wAP1r0 (wAP1, r0);

  NodeContainer routers (r0, r1, r2, r3); // Routers container
  routers.Add(wAP1);// add wAP1 as a router for domain1
 
  NodeContainer hosts;
  hosts.Add(h0);
  hosts.Add(h1);
  
  // Create channels and assign a default attributes
  NS_LOG_INFO ("Create channels.");
  PointToPointHelper p2p;
  p2p.SetChannelAttribute("Delay", TimeValue (MilliSeconds(2)));
  p2p.SetDeviceAttribute("Mtu", UintegerValue (1500));
  p2p.SetDeviceAttribute("DataRate", DataRateValue (5000000));


  // Assign a channels for the net device containers for each nodeContainer

  NetDeviceContainer ndc1 = p2p.Install (h0r0);
  NetDeviceContainer ndc2 = p2p.Install (h1r3);
  NetDeviceContainer ndc3 = p2p.Install (r0r1);
  NetDeviceContainer ndc4 = p2p.Install (r1r2);
  NetDeviceContainer ndc5 = p2p.Install (r2r3);
  NetDeviceContainer ndc6 = p2p.Install (r0r3);
  NetDeviceContainer ndc7 = p2p.Install (r0r2);
  NetDeviceContainer ndc8 = p2p.Install (r1r3);
  
  //////////

  NetDeviceContainer ndc9 = p2p.Install(wAP1r0);





  
  NodeContainer D1StaNodes;  // create Domain1 nodes

  for (uint32_t i= 0;i < nWiFi; i++){
	  Ptr<Node> D1Sta_i = CreateObject<Node> ();
	  D1StaNodes.Add(D1Sta_i);
  } // Create Object of node and add it to the node list.

  NodeContainer D1AP = wAP1r0.Get(0); // create NC (AP) which contain the 1st element of wAP1r0


  // wifi_Channel for wAP1 DOMAIN_1

  YansWifiChannelHelper channel1 = YansWifiChannelHelper::Default ();
  YansWifiPhyHelper phy1 = YansWifiPhyHelper::Default ();
  phy1.SetChannel (channel1.Create ());

  WifiHelper wifi1 = WifiHelper::Default ();
  wifi1.SetRemoteStationManager ("ns3::AarfWifiManager");

  NqosWifiMacHelper mac1 = NqosWifiMacHelper::Default ();

  Ssid ssid1 = Ssid ("Domain_1-ssid"); // wireless net name.
  mac1.SetType ("ns3::StaWifiMac",
               "Ssid", SsidValue (ssid1),
               "ActiveProbing", BooleanValue (false));// changed to true


  // creat a wireless net devices container contains all created station nodes

  NetDeviceContainer staDevices1;
  staDevices1 = wifi1.Install (phy1, mac1, D1StaNodes);

  mac1.SetType ("ns3::ApWifiMac", "Ssid", SsidValue (ssid1));

// AP device container
  NetDeviceContainer apDevices1;
  apDevices1 = wifi1.Install (phy1, mac1, D1AP);

  // create and configure the mobility of wwifi nodes

  MobilityHelper mobility1;

  mobility1.SetPositionAllocator ("ns3::GridPositionAllocator",
                                 "MinX", DoubleValue (-20.0),
                                 "MinY", DoubleValue (-20.0),
                                 "DeltaX", DoubleValue (10.0),
                                 "DeltaY", DoubleValue (10.0),
                                 "GridWidth", UintegerValue (5),
                                 "LayoutType", StringValue ("RowFirst"));

  mobility1.SetMobilityModel ("ns3::RandomWalk2dMobilityModel",
                             "Bounds", RectangleValue (Rectangle (-100, 30, -50, 50)));
  mobility1.Install (D1StaNodes);
// rectangle (min X, Max X, min Y, Max y)
  mobility1.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
  mobility1.Install (D1AP);



hosts.Add(D1StaNodes); // add Domain1 wifi station nodes to hosts nodeContainer

  // Create RIP ng for IPV6 (1)

  RipNgHelper ripNgRouting;
  
  ripNgRouting.ExcludeInterface(r0, 1);
  ripNgRouting.ExcludeInterface(r3, 1);
  //ripNgRouting.ExcludeInterface(r0,5);
 
  // Create routers List
  Ipv6ListRoutingHelper listRH;
  listRH.Add(ripNgRouting, 0);

  // internet stack helper

  // Install Interet stack helper in the node container
  NS_LOG_INFO ("Create IPv6 Internet Stack");

  InternetStackHelper internetv6;
  internetv6.SetIpv4StackInstall (false);
  internetv6.SetRoutingHelper (listRH);
  internetv6.Install (routers);

  InternetStackHelper internetV6Hosts;
  internetV6Hosts.SetIpv4StackInstall(false);
  internetV6Hosts.Install(hosts);




  // Assign ipv6 address for each netDeviceContainer
  Ipv6AddressHelper ipv6;

  ipv6.SetBase (Ipv6Address ("2001:1::"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i1 = ipv6.Assign (ndc1);
  i1.SetForwarding (1, true);// true== Router, false == host
  i1.SetDefaultRouteInAllNodes (1);
  /**
   * \brief Set the default route for all the devices (except the router itself).
   * \param router the default router index, H0_index = 0; R0_Index=1
   */


  ipv6.SetBase (Ipv6Address ("2001:2::"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i2 = ipv6.Assign (ndc2);
  i2.SetForwarding (1, true);
  i2.SetDefaultRouteInAllNodes (1);

  ipv6.SetBase (Ipv6Address ("2001:0:7:"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i9 = ipv6.Assign (ndc9); // wAP1---RO
  i9.SetForwarding (1, true);
  i9.SetForwarding(0, true); // link with AP


  ipv6.SetBase (Ipv6Address ("2001:3::"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i10; // Domain1 AP1+D1StaNodes
  i10 = ipv6.Assign (apDevices1);
  i10 = ipv6.Assign (staDevices1);
  i10.SetForwarding (0, true);
  i10.SetDefaultRouteInAllNodes (0);



 




// Between two routers eachone pointed to the other as a defoult route.
  ipv6.SetBase (Ipv6Address ("2001:0:1:"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i3 = ipv6.Assign (ndc3);
  i3.SetForwarding (0, true);// 1-->0
  i3.SetForwarding (1, true); // 0-->1


  ipv6.SetBase (Ipv6Address ("2001:0:2:"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i4 = ipv6.Assign (ndc4);
  i4.SetForwarding (0, true);
  i4.SetForwarding (1, true);


  ipv6.SetBase (Ipv6Address ("2001:0:3:"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i5 = ipv6.Assign (ndc5);
  i5.SetForwarding (0, true);
  i5.SetForwarding (1, true);


  ipv6.SetBase (Ipv6Address ("2001:0:4:"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i6 = ipv6.Assign (ndc6);
  i6.SetForwarding (0, true);
  i6.SetForwarding (1, true);


  ipv6.SetBase (Ipv6Address ("2001:0:5:"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i7 = ipv6.Assign (ndc7);
  i7.SetForwarding (0, true);
  i7.SetForwarding (1, true);


  ipv6.SetBase (Ipv6Address ("2001:0:6:"), Ipv6Prefix (64));
  Ipv6InterfaceContainer i8 = ipv6.Assign (ndc8);
  i8.SetForwarding (0, true);
  i8.SetForwarding (1, true);





  NS_LOG_INFO ("Create Applications.");


  // Create the OnOff application to send UDP datagrams

  uint16_t port1 = 9;   

  
  OnOffHelper onoff ("ns3::UdpSocketFactory",
                     Address (Inet6SocketAddress (i10.GetAddress(3, 1), port1)));
  onoff.SetConstantRate (DataRate ("600b/s"));

  ApplicationContainer apps = onoff.Install(h0);
  apps.Start (Seconds (5.0));
  apps.Stop (Seconds (100.0));

  // Create a packet sink to receive these packets
  PacketSinkHelper sink ("ns3::UdpSocketFactory",
                         Address (Inet6SocketAddress (Ipv6Address::GetAny (), port1)));
  apps = sink.Install (D1StaNodes.Get(3));
  apps.Start (Seconds (4.0));





  // Create the OnOff application to send UDP datagrams  sitting
  uint16_t port2 = 30;   // Discard port (RFC 863)

  // Create a flow from n3 to n1, starting at time 1.1 seconds
  OnOffHelper onoff2 ("ns3::UdpSocketFactory",
                     Address (Inet6SocketAddress (i1.GetAddress(0, 1), port2)));
  onoff2.SetConstantRate (DataRate ("1600b/s"));

  ApplicationContainer apps2 = onoff2.Install(D1StaNodes.Get(6));
  apps2.Start (Seconds (5.0));
  apps2.Stop (Seconds (200.0));

  // Create a packet sink to receive these packets
  PacketSinkHelper sink2 ("ns3::UdpSocketFactory",
                         Address (Inet6SocketAddress (Ipv6Address::GetAny (), port2)));
  apps2 = sink2.Install (h0);
  apps2.Start (Seconds (5.0));











  /**
     * \brief Get the address for the specified index.
     * \param i interface index
     * \param j address index, generally index 0 is the link-local address
     * \return IPv6 address
     */

  //AsciiTraceHelper ascii;
  //p2p.EnableAsciiAll (ascii.CreateFileStream ("mynet1.tr"));
  //p2p.EnablePcapAll ("mynet1", true);

  NS_LOG_INFO ("Run Simulation.");
  Simulator::Run ();
  Simulator::Destroy ();
  NS_LOG_INFO ("Done.");
}

