 /* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
 /*
 *   Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
 *   Copyright (c) 2015, NYU WIRELESS, Tandon School of Engineering, New York University
 *
 *   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: Marco Miozzo <marco.miozzo@cttc.es>
 *           Nicola Baldo  <nbaldo@cttc.es>
 *
 *   Modified by: Marco Mezzavilla < mezzavilla@nyu.edu>
 *              Sourjya Dutta <sdutta@nyu.edu>
 *              Russell Ford <russell.ford@nyu.edu>
 *              Menglei Zhang <menglei@nyu.edu>
 */


#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/mobility-module.h"
#include "ns3/config-store.h"
#include "ns3/mmwave-helper.h"
#include <ns3/buildings-helper.h>
#include "ns3/global-route-manager.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/internet-module.h"
#include "ns3/applications-module.h"
#include "ns3/log.h"
#include "ns3/point-to-point-helper.h"
#include "ns3/mmwave-point-to-point-epc-helper.h"

using namespace ns3;

int
main (int argc, char *argv[])
{

  CommandLine cmd;
  cmd.Parse (argc, argv);
//  LogComponentEnable ("MmWaveSpectrumPhy", LOG_LEVEL_INFO);
  //LogComponentEnable ("MmWaveUePhy", LOG_LEVEL_INFO);
  //LogComponentEnable ("MmWaveEnbPhy", LOG_LEVEL_INFO);
  //LogComponentEnable ("MmWavePhy", LOG_LEVEL_INFO);

//  LogComponentEnable ("MmWaveUeMac", LOG_LEVEL_INFO);
  //LogComponentEnable ("MmWaveEnbMac", LOG_LEVEL_INFO);
//  LogComponentEnable ("MmWaveRrMacScheduler", LOG_LEVEL_INFO);

  //LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL);
  //LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL);
  //LogComponentEnable("PropagationLossModel",LOG_LEVEL_ALL);
  //LogComponentEnable("mmWaveInterference",LOG_LEVEL_ALL);
  //LogComponentEnable("MmWaveBeamforming",LOG_LEVEL_ALL);
  //LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
  //LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);

  bool harqEnabled = true;
  bool rlcAmEnabled = true;

  //Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (48000));
    Config::SetDefault ("ns3::PointToPointNetDevice::Mtu", UintegerValue (48000));
    Config::SetDefault ("ns3::VirtualNetDevice::Mtu", UintegerValue (48000));

    Config::SetDefault ("ns3::LteRlcUm::MaxTxBufferSize", UintegerValue (1024 * 1024 * 1024));
    Config::SetDefault ("ns3::LteRlcUmLowLat::MaxTxBufferSize", UintegerValue (1024 * 1024 * 1024));
    //Config::SetDefault ("ns3::UdpSocket::SndBufSize", UintegerValue (1131072000 * 24));
    Config::SetDefault ("ns3::UdpSocket::RcvBufSize", UintegerValue (1131072000 * 24));
    //Config::SetDefault ("ns3::UdpSocket::SndBufSize", UintegerValue (1131072000 * 24));
    Config::SetDefault ("ns3::MmWaveHelper::RlcAmEnabled", BooleanValue(rlcAmEnabled));
    Config::SetDefault ("ns3::MmWaveHelper::HarqEnabled", BooleanValue(harqEnabled));
    Config::SetDefault ("ns3::MmWaveFlexTtiMacScheduler::HarqEnabled", BooleanValue(true));
    Config::SetDefault ("ns3::MmWaveFlexTtiMaxWeightMacScheduler::HarqEnabled", BooleanValue(true));
    Config::SetDefault ("ns3::MmWaveFlexTtiMacScheduler::HarqEnabled", BooleanValue(true));
    Config::SetDefault ("ns3::MmWaveBeamforming::LongTermUpdatePeriod", TimeValue (MilliSeconds (100.0)));
    Config::SetDefault ("ns3::LteRlcAm::PollRetransmitTimer", TimeValue(MilliSeconds(0.4)));
    Config::SetDefault ("ns3::LteRlcAm::ReorderingTimer", TimeValue(MilliSeconds(2.0)));
    Config::SetDefault ("ns3::LteRlcAm::StatusProhibitTimer", TimeValue(MilliSeconds(1.0)));
    Config::SetDefault ("ns3::LteRlcAm::ReportBufferStatusTimer", TimeValue(MilliSeconds(4.0)));
    Config::SetDefault ("ns3::LteRlcAm::MaxTxBufferSize", UintegerValue (200 *1024 * 1024));

  Config::SetDefault ("ns3::MmWave3gppPropagationLossModel::ChannelCondition", StringValue("n"));
      Config::SetDefault ("ns3::MmWave3gppPropagationLossModel::Scenario", StringValue("InH-OfficeOpen"));
      Config::SetDefault ("ns3::MmWave3gppPropagationLossModel::OptionalNlos", BooleanValue(true));
      Config::SetDefault ("ns3::MmWave3gppPropagationLossModel::Shadowing", BooleanValue(false)); // enable or disable the shadowing effect
      Config::SetDefault ("ns3::MmWave3gppBuildingsPropagationLossModel::UpdateCondition", BooleanValue(true)); // enable or disable the LOS/NLOS update when the UE moves
      Config::SetDefault ("ns3::AntennaArrayModel::AntennaHorizontalSpacing", DoubleValue(1));
      Config::SetDefault ("ns3::AntennaArrayModel::AntennaVerticalSpacing", DoubleValue(1));
      Config::SetDefault ("ns3::MmWave3gppChannel::UpdatePeriod", TimeValue(MilliSeconds(100))); // interval after which the channel for a moving user is updated,
                                                                                                 // with spatial consistency procedure. If 0, spatial consistency is not used
      Config::SetDefault ("ns3::MmWave3gppChannel::CellScan", BooleanValue(true)); // Set true to use cell scanning method, false to use the default power method.
      Config::SetDefault ("ns3::MmWave3gppChannel::Blockage", BooleanValue(false)); // use blockage or not
      Config::SetDefault ("ns3::MmWave3gppChannel::PortraitMode", BooleanValue(false)); // use blockage model with UT in portrait mode
      //Config::SetDefault ("ns3::MmWave3gppChannel::NumNonselfBlocking", IntegerValue(0)); // number of non-self blocking obstacles
      //Config::SetDefault ("ns3::MmWavePhyMacCommon::CenterFreq", DoubleValue(60e9)); //
      Config::SetDefault("ns3::MmWave3gppPropagationLossModel::Frequency", DoubleValue(60e9));
      //Config::SetDefault("ns3::MmWaveAmc::Ber", DoubleValue(0.002));
      Config::SetDefault("ns3::MmWaveUePhy::TxPower", DoubleValue(16));
      Config::SetDefault("ns3::MmWaveEnbPhy::TxPower", DoubleValue(16));



double hBS = 1.5; //base station antenna height in meters;
double hUT = 1.5; //user antenna height in meters;

  Ptr<MmWaveHelper> ptr_mmWave = CreateObject<MmWaveHelper> ();
  //ptr_mmWave->SetAttribute ("PathlossModel", StringValue ("ns3::MmWave3gppPropagationLossModel"));
  //ptr_mmWave->SetAttribute ("ChannelModel", StringValue ("ns3::MmWave3gppChannel"));

  Ptr<MmWavePointToPointEpcHelper> epcHelper = CreateObject<MmWavePointToPointEpcHelper> ();
  ptr_mmWave->SetEpcHelper(epcHelper);
  ptr_mmWave->Initialize();


  NodeContainer enbNodes;
  NodeContainer ueNodes;
  enbNodes.Create (1);
  ueNodes.Create (1);

  Ptr<ListPositionAllocator> enbPositionAlloc = CreateObject<ListPositionAllocator> ();
  enbPositionAlloc->Add (Vector (0.0, 0.0, hBS));

  MobilityHelper enbmobility;
  enbmobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
  enbmobility.SetPositionAllocator(enbPositionAlloc);
  enbmobility.Install (enbNodes);


  /*MobilityHelper uemobility;

  uemobility.SetMobilityModel ("ns3::ConstantVelocityMobilityModel");
  uemobility.Install (ueNodes);



  double speed = 2;

  ueNodes.Get (0)->GetObject<MobilityModel> ()->SetPosition (Vector (1.0, 0.0, hUT)); // (x, y, z) in m
  ueNodes.Get (0)->GetObject<ConstantVelocityMobilityModel> ()->SetVelocity (Vector (speed, 0, 0)); // move UE1 along the y axis*/

  MobilityHelper uemobility;
  Ptr<ListPositionAllocator> uePositionAlloc = CreateObject<ListPositionAllocator> ();
  uePositionAlloc->Add (Vector (90.0, 0.0, 1.5));
  uemobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
  uemobility.SetPositionAllocator(uePositionAlloc);
  uemobility.Install (ueNodes);


  NetDeviceContainer enbNetDev = ptr_mmWave->InstallEnbDevice (enbNodes);
  NetDeviceContainer ueNetDev = ptr_mmWave->InstallUeDevice (ueNodes);

  Ptr<Node> pgw = epcHelper->GetPgwNode();
  NodeContainer remoteHostContainer;
  remoteHostContainer.Create(1);
  Ptr<Node> remoteHost = remoteHostContainer.Get(0);

  InternetStackHelper internet;
  internet.Install (remoteHostContainer);

  PointToPointHelper p2ph;

  p2ph.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("100Gb/s")));
  p2ph.SetDeviceAttribute ("Mtu", UintegerValue (48000));
  p2ph.SetChannelAttribute ("Delay", TimeValue (MicroSeconds (0.010)));
  NetDeviceContainer internetDevices = p2ph.Install (pgw, remoteHost);
  Ipv4AddressHelper ipv4h;
  ipv4h.SetBase ("1.0.0.0", "255.0.0.0");
  Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign (internetDevices);
  Ipv4StaticRoutingHelper ipv4RoutingHelper;
  Ptr<Ipv4StaticRouting> remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject<Ipv4> ());
  remoteHostStaticRouting->AddNetworkRouteTo (Ipv4Address ("7.0.0.0"), Ipv4Mask ("255.0.0.0"), 1);
  internet.Install (ueNodes);
  Ipv4InterfaceContainer ueIpIface;
  ueIpIface = epcHelper->AssignUeIpv4Address (NetDeviceContainer (ueNetDev));

  uint16_t dlPort = 1234;
  ApplicationContainer clientApps;
  ApplicationContainer serverApps;

  double simTime = 1;
  double payloadSize = 1500;

  for (uint32_t u = 0; u < ueNodes.GetN (); ++u)
    {
      Ptr<Node> ueNode = ueNodes.Get (u);
      // Set the default gateway for the UE
      Ptr<Ipv4StaticRouting> ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject<Ipv4> ());
      ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1);

      UdpServerHelper dlPacketSinkHelper (dlPort);
      serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get(u)));

      UdpClientHelper dlClient (ueIpIface.GetAddress (u), dlPort);
      dlClient.SetAttribute ("Interval", TimeValue (MicroSeconds(1)));
      dlClient.SetAttribute ("MaxPackets", UintegerValue(0xFFFFFFFF));
      dlClient.SetAttribute ("PacketSize", UintegerValue (payloadSize));
      clientApps.Add (dlClient.Install (remoteHost));
    }



  serverApps.Start(Seconds(0.1));
  clientApps.Start(Seconds(0.11));
  serverApps.Stop(Seconds(simTime));
  clientApps.Stop(Seconds(simTime));

  ptr_mmWave->AttachToClosestEnb (ueNetDev, enbNetDev);
  ptr_mmWave->EnableTraces();

  // Activate a data radio bearer
  enum EpsBearer::Qci q = EpsBearer::GBR_ULTRA_LOW_LAT;
  EpsBearer bearer (q);
  ptr_mmWave->ActivateDataRadioBearer (ueNetDev, bearer);





  Simulator::Stop (Seconds (simTime));
  Simulator::Run ();
  Simulator::Destroy ();

  double thr = 0.0;

    uint32_t totalPackets = DynamicCast<UdpServer>(serverApps.Get(0))->GetReceived();
    thr = totalPackets * payloadSize * 8 / ((simTime - 0.11) * 1e6);

    std::cout << thr << " Mbit/s Total: " << totalPackets << std::endl;
  return 0;
}
