/*
 * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
 *
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * Author: Manuel Requena <manuel.requena@cttc.es>
 *         Nicola Baldo <nbaldo@cttc.es>
 */

#include "ns3/config-store.h"
#include "ns3/core-module.h"
#include "ns3/lte-module.h"
#include "ns3/mobility-module.h"
#include "ns3/network-module.h"
#include "ns3/radio-bearer-stats-calculator.h"

#include <iomanip>
#include <string>

using namespace ns3;

/**
 * This simulation script creates two eNodeBs and drops randomly several UEs in
 * a disc around them (same number on both). The number of UEs , the radius of
 * that disc and the distance between the eNodeBs can be configured.
 */
int
main(int argc, char* argv[])
{
    double enbDist = 100.0;
    double radius = 50.0;
    uint32_t numUes = 1;
    double simTime = 1.0;

    CommandLine cmd(__FILE__);
    cmd.AddValue("enbDist", "distance between the two eNBs", enbDist);
    cmd.AddValue("radius", "the radius of the disc where UEs are placed around an eNB", radius);
    cmd.AddValue("numUes", "how many UEs are attached to each eNB", numUes);
    cmd.AddValue("simTime", "Total duration of the simulation (in seconds)", simTime);
    cmd.Parse(argc, argv);

    ConfigStore inputConfig;
    inputConfig.ConfigureDefaults();

    // parse again so you can override default values from the command line
    cmd.Parse(argc, argv);

    // determine the string tag that identifies this simulation run
    // this tag is then appended to all filenames

    UintegerValue runValue;
    GlobalValue::GetValueByName("RngRun", runValue);

    std::ostringstream tag;
    tag << "_enbDist" << std::setw(3) << std::setfill('0') << std::fixed << std::setprecision(0)
        << enbDist << "_radius" << std::setw(3) << std::setfill('0') << std::fixed
        << std::setprecision(0) << radius << "_numUes" << std::setw(3) << std::setfill('0')
        << numUes << "_rngRun" << std::setw(3) << std::setfill('0') << runValue.Get();

    Ptr<LteHelper> lteHelper = CreateObject<LteHelper>();

    lteHelper->SetAttribute("PathlossModel", StringValue("ns3::FriisSpectrumPropagationLossModel"));

    // Create Nodes: eNodeB and UE
    NodeContainer enbNodes;
    NodeContainer ueNodes1;
    NodeContainer ueNodes2;
    enbNodes.Create(2);
    ueNodes1.Create(numUes);
    ueNodes2.Create(numUes);

    // Position of eNBs
    Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
    positionAlloc->Add(Vector(0.0, 0.0, 0.0));
    positionAlloc->Add(Vector(enbDist, 0.0, 0.0));
    MobilityHelper enbMobility;
    enbMobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
    enbMobility.SetPositionAllocator(positionAlloc);
    enbMobility.Install(enbNodes);

    // Position of UEs attached to eNB 1
    MobilityHelper ue1mobility;
    ue1mobility.SetPositionAllocator("ns3::UniformDiscPositionAllocator",
                                     "X",
                                     DoubleValue(0.0),
                                     "Y",
                                     DoubleValue(0.0),
                                     "rho",
                                     DoubleValue(radius));
    ue1mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
    ue1mobility.Install(ueNodes1);

    // Position of UEs attached to eNB 2
    MobilityHelper ue2mobility;
    ue2mobility.SetPositionAllocator("ns3::UniformDiscPositionAllocator",
                                     "X",
                                     DoubleValue(enbDist),
                                     "Y",
                                     DoubleValue(0.0),
                                     "rho",
                                     DoubleValue(radius));
    ue2mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
    ue2mobility.Install(ueNodes2);

    // Create Devices and install them in the Nodes (eNB and UE)
    NetDeviceContainer enbDevs;
    NetDeviceContainer ueDevs1;
    NetDeviceContainer ueDevs2;
    enbDevs = lteHelper->InstallEnbDevice(enbNodes);
    ueDevs1 = lteHelper->InstallUeDevice(ueNodes1);
    ueDevs2 = lteHelper->InstallUeDevice(ueNodes2);

    // Attach UEs to a eNB
    lteHelper->Attach(ueDevs1, enbDevs.Get(0));
    lteHelper->Attach(ueDevs2, enbDevs.Get(1));

    // Activate a data radio bearer each UE
    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
    EpsBearer bearer(q);
    lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
    lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);

    Simulator::Stop(Seconds(simTime));

    // Insert RLC Performance Calculator
    std::string dlOutFname = "DlRlcStats";
    dlOutFname.append(tag.str());
    std::string ulOutFname = "UlRlcStats";
    ulOutFname.append(tag.str());

    lteHelper->EnableMacTraces();
    lteHelper->EnableRlcTraces();

    Simulator::Run();
    Simulator::Destroy();
    return 0;
}
