/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <cstdlib>
#include <time.h>
/*Custom Headers*/
#include "ns3/address.h"
#include "ns3/animation-interface.h"
#include "ns3/ipv4-address-helper.h"
#include "ns3/packet-sink-helper.h"
#include "ns3/csma-helper.h"
#include "ns3/on-off-helper.h"
#include "ns3/application-container.h"
#include "ns3/bridge-helper.h"
#include "ns3/network-module.h"
#include "ns3/core-module.h"
#include "ns3/global-route-manager.h"
#include "ns3/internet-module.h"
#include "ns3/bridge-module.h"
#include "ns3/packet-loss-counter.h"

NS_LOG_COMPONENT_DEFINE ("assignment");

using namespace std;
using namespace ns3;

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

  /* Configuration. */

  /* Build nodes. */
  NodeContainer router_0;
  router_0.Create (1);
  NodeContainer term_0;
  term_0.Create (20);
  NodeContainer term_1;
  term_1.Create (20);
  NodeContainer term_3;
  term_3.Create(1);
  CsmaHelper csma1;
  csma1.SetChannelAttribute ("DataRate", DataRateValue (100000000));
  csma1.SetChannelAttribute ("Delay",  TimeValue (MilliSeconds (0)));
  
  CsmaHelper csma_hub_1;
  csma_hub_1.SetChannelAttribute ("DataRate", DataRateValue (100000000));
  csma_hub_1.SetChannelAttribute ("Delay",  TimeValue (MilliSeconds (100)));
  
  CsmaHelper csma_hub_3;
  csma_hub_3.SetChannelAttribute ("DataRate", DataRateValue (100000000));
  csma_hub_3.SetChannelAttribute ("Delay",  TimeValue (MilliSeconds (0)));
  
  /* Build link net device container. */
  NodeContainer all_hub_0;
  all_hub_0.Add (router_0);
  all_hub_0.Add (term_0);
  NetDeviceContainer ndc_hub_0 = csma1.Install (all_hub_0);
  NodeContainer all_hub_1;
  all_hub_1.Add (router_0);
  all_hub_1.Add (term_1);
  NetDeviceContainer ndc_hub_1 = csma_hub_1.Install (all_hub_1);
  NodeContainer all_hub_3;
  all_hub_3.Add (router_0);
  all_hub_3.Add (term_3);
  NetDeviceContainer ndc_hub_3 = csma_hub_3.Install (all_hub_3);

  /* Install the IP stack. */
  InternetStackHelper internetStackH;
  internetStackH.Install (router_0);
  internetStackH.Install (term_0);
  internetStackH.Install (term_1);
  internetStackH.Install (term_3);

  /* IP assign. */
  Ipv4AddressHelper ipv4;
  ipv4.SetBase ("10.0.0.0", "255.255.255.0");
  Ipv4InterfaceContainer iface_ndc_hub_0 = ipv4.Assign (ndc_hub_0);
  ipv4.SetBase ("10.0.1.0", "255.255.255.0");
  Ipv4InterfaceContainer iface_ndc_hub_1 = ipv4.Assign (ndc_hub_1);
  ipv4.SetBase ("10.0.2.0", "255.255.255.0");
  Ipv4InterfaceContainer iface_ndc_hub_3 = ipv4.Assign (ndc_hub_3);

  /* Generate Route. */
  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
   
  /* Generate Application. */
  uint16_t port_tcp_1 = 80;
  Address sinkLocalAddress_tcp_1 (InetSocketAddress (Ipv4Address::GetAny (), port_tcp_1));
  PacketSinkHelper sinkHelper_tcp_1 ("ns3::TcpSocketFactory", sinkLocalAddress_tcp_1);
  ApplicationContainer sinkApp_tcp_1 = sinkHelper_tcp_1.Install (term_3);
  sinkApp_tcp_1.Start (Seconds (0.0));
  sinkApp_tcp_1.Stop (Seconds (40.0));
  OnOffHelper clientHelper_tcp_1 ("ns3::TcpSocketFactory", Address ());
  clientHelper_tcp_1.SetAttribute ("OnTime", StringValue("ns3::ConstantRandomVariable[Constant=1]"));
  clientHelper_tcp_1.SetAttribute ("OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
  ApplicationContainer clientApps_tcp_1;
  AddressValue remoteAddress_tcp_1 (InetSocketAddress (iface_ndc_hub_3.GetAddress (1), port_tcp_1));
  clientHelper_tcp_1.SetAttribute ("Remote", remoteAddress_tcp_1);
  clientApps_tcp_1.Add(clientHelper_tcp_1.Install (term_0));
  clientApps_tcp_1.Start (Seconds (0.0));
  clientApps_tcp_1.Stop (Seconds (40.0));
  uint16_t port_tcp_2 = 80;
  Address sinkLocalAddress_tcp_2 (InetSocketAddress (Ipv4Address::GetAny (), port_tcp_2));
  PacketSinkHelper sinkHelper_tcp_2 ("ns3::TcpSocketFactory", sinkLocalAddress_tcp_2);
  ApplicationContainer sinkApp_tcp_2 = sinkHelper_tcp_2.Install (term_3);
  sinkApp_tcp_2.Start (Seconds (0.0));
  sinkApp_tcp_2.Stop (Seconds (40.0));
  OnOffHelper clientHelper_tcp_2 ("ns3::TcpSocketFactory", Address ());
   
  clientHelper_tcp_2.SetAttribute ("OnTime", StringValue("ns3::ConstantRandomVariable[Constant=1]"));
  clientHelper_tcp_2.SetAttribute ("OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
  ApplicationContainer clientApps_tcp_2;
  AddressValue remoteAddress_tcp_2 (InetSocketAddress (iface_ndc_hub_3.GetAddress (1), port_tcp_2));
  clientHelper_tcp_2.SetAttribute ("Remote", remoteAddress_tcp_2);
  clientApps_tcp_2.Add (clientHelper_tcp_2.Install (term_1));
  clientApps_tcp_2.Start (Seconds (0.0));
  clientApps_tcp_2.Stop (Seconds (40.0));
  
  /* Simulation Statistics. */
  NS_LOG_INFO ("SIMULATION STATISTICS"); 
  //Packet Loss Counter
  AnimationInterface theAnim("theAnim.xml"); /*Erase this comment to get a GUI output of the topology*/
  NS_LOG_INFO ("STARTING SIMULATION"); 
  Simulator::Run ();
  Simulator::Destroy ();
  NS_LOG_INFO ("SIMULATION ENDED");
}
