/*
 * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
 *
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * Author: Jaume Nin <jnin@cttc.es>
 */

#include "lte-stats-calculator.h"

#include <ns3/config.h>
#include <ns3/log.h>
#include <ns3/lte-enb-net-device.h>
#include <ns3/lte-enb-rrc.h>
#include <ns3/lte-ue-net-device.h>
#include <ns3/lte-ue-rrc.h>

namespace ns3
{

NS_LOG_COMPONENT_DEFINE("LteStatsCalculator");

NS_OBJECT_ENSURE_REGISTERED(LteStatsCalculator);

LteStatsCalculator::LteStatsCalculator()
    : m_dlOutputFilename(""),
      m_ulOutputFilename("")
{
    // Nothing to do here
}

LteStatsCalculator::~LteStatsCalculator()
{
    // Nothing to do here
}

TypeId
LteStatsCalculator::GetTypeId()
{
    static TypeId tid = TypeId("ns3::LteStatsCalculator")
                            .SetParent<Object>()
                            .SetGroupName("Lte")
                            .AddConstructor<LteStatsCalculator>();
    return tid;
}

void
LteStatsCalculator::SetUlOutputFilename(std::string outputFilename)
{
    m_ulOutputFilename = outputFilename;
}

std::string
LteStatsCalculator::GetUlOutputFilename()
{
    return m_ulOutputFilename;
}

void
LteStatsCalculator::SetDlOutputFilename(std::string outputFilename)
{
    m_dlOutputFilename = outputFilename;
}

std::string
LteStatsCalculator::GetDlOutputFilename()
{
    return m_dlOutputFilename;
}

bool
LteStatsCalculator::ExistsImsiPath(std::string path)
{
    return m_pathImsiMap.find(path) != m_pathImsiMap.end();
}

void
LteStatsCalculator::SetImsiPath(std::string path, uint64_t imsi)
{
    NS_LOG_FUNCTION(this << path << imsi);
    m_pathImsiMap[path] = imsi;
}

uint64_t
LteStatsCalculator::GetImsiPath(std::string path)
{
    return m_pathImsiMap.find(path)->second;
}

bool
LteStatsCalculator::ExistsCellIdPath(std::string path)
{
    return m_pathCellIdMap.find(path) != m_pathCellIdMap.end();
}

void
LteStatsCalculator::SetCellIdPath(std::string path, uint16_t cellId)
{
    NS_LOG_FUNCTION(this << path << cellId);
    m_pathCellIdMap[path] = cellId;
}

uint16_t
LteStatsCalculator::GetCellIdPath(std::string path)
{
    return m_pathCellIdMap.find(path)->second;
}

uint64_t
LteStatsCalculator::FindImsiFromEnbRlcPath(std::string path)
{
    NS_LOG_FUNCTION(path);
    // Sample path input:
    // /NodeList/#NodeId/DeviceList/#DeviceId/LteEnbRrc/UeMap/#C-RNTI/DataRadioBearerMap/#LCID/LteRlc/RxPDU

    // We retrieve the UeManager associated to the C-RNTI and perform the IMSI lookup
    std::string ueMapPath = path.substr(0, path.find("/DataRadioBearerMap"));
    Config::MatchContainer match = Config::LookupMatches(ueMapPath);

    if (match.GetN() != 0)
    {
        Ptr<Object> ueInfo = match.Get(0);
        NS_LOG_LOGIC("FindImsiFromEnbRlcPath: " << path << ", "
                                                << ueInfo->GetObject<UeManager>()->GetImsi());
        return ueInfo->GetObject<UeManager>()->GetImsi();
    }
    else
    {
        NS_FATAL_ERROR("Lookup " << ueMapPath << " got no matches");
    }
    return 0; // Silence compiler warning about lack of return value
}

uint64_t
LteStatsCalculator::FindImsiFromUePhy(std::string path)
{
    NS_LOG_FUNCTION(path);
    // Sample path input:
    // /NodeList/#NodeId/DeviceList/#DeviceId/LteUePhy

    // We retrieve the UeInfo associated to the C-RNTI and perform the IMSI lookup
    std::string ueRlcPath = path.substr(0, path.find("/LteUePhy"));
    ueRlcPath += "/LteUeRrc";
    Config::MatchContainer match = Config::LookupMatches(ueRlcPath);

    if (match.GetN() != 0)
    {
        Ptr<Object> ueRrc = match.Get(0);
        return ueRrc->GetObject<LteUeRrc>()->GetImsi();
    }
    else
    {
        NS_FATAL_ERROR("Lookup " << ueRlcPath << " got no matches");
    }
    return 0;
}

uint64_t
LteStatsCalculator::FindImsiFromLteNetDevice(std::string path)
{
    NS_LOG_FUNCTION(path);
    // Sample path input:
    // /NodeList/#NodeId/DeviceList/#DeviceId/

    // We retrieve the Imsi associated to the LteUeNetDevice
    Config::MatchContainer match = Config::LookupMatches(path);

    if (match.GetN() != 0)
    {
        Ptr<Object> ueNetDevice = match.Get(0);
        NS_LOG_LOGIC("FindImsiFromLteNetDevice: "
                     << path << ", " << ueNetDevice->GetObject<LteUeNetDevice>()->GetImsi());
        return ueNetDevice->GetObject<LteUeNetDevice>()->GetImsi();
    }
    else
    {
        NS_FATAL_ERROR("Lookup " << path << " got no matches");
    }
    return 0; // Silence compiler warning about lack of return value
}

uint16_t
LteStatsCalculator::FindCellIdFromEnbRlcPath(std::string path)
{
    NS_LOG_FUNCTION(path);
    // Sample path input:
    // /NodeList/#NodeId/DeviceList/#DeviceId/LteEnbRrc/UeMap/#C-RNTI/DataRadioBearerMap/#LCID/LteRlc/RxPDU

    // We retrieve the CellId associated to the Enb
    std::string enbNetDevicePath = path.substr(0, path.find("/LteEnbRrc"));
    Config::MatchContainer match = Config::LookupMatches(enbNetDevicePath);
    if (match.GetN() != 0)
    {
        Ptr<Object> enbNetDevice = match.Get(0);
        NS_LOG_LOGIC("FindCellIdFromEnbRlcPath: "
                     << path << ", " << enbNetDevice->GetObject<LteEnbNetDevice>()->GetCellId());
        return enbNetDevice->GetObject<LteEnbNetDevice>()->GetCellId();
    }
    else
    {
        NS_FATAL_ERROR("Lookup " << enbNetDevicePath << " got no matches");
    }
    return 0; // Silence compiler warning about lack of return value
}

uint64_t
LteStatsCalculator::FindImsiFromEnbMac(std::string path, uint16_t rnti)
{
    NS_LOG_FUNCTION(path << rnti);

    // /NodeList/#NodeId/DeviceList/#DeviceId/LteEnbMac/DlScheduling
    std::ostringstream oss;
    std::string p = path.substr(0, path.find("/LteEnbMac"));
    oss << rnti;
    p += "/LteEnbRrc/UeMap/" + oss.str();
    uint64_t imsi = FindImsiFromEnbRlcPath(p);
    NS_LOG_LOGIC("FindImsiFromEnbMac: " << path << ", " << rnti << ", " << imsi);
    return imsi;
}

uint16_t
LteStatsCalculator::FindCellIdFromEnbMac(std::string path, uint16_t rnti)
{
    NS_LOG_FUNCTION(path << rnti);
    // /NodeList/#NodeId/DeviceList/#DeviceId/LteEnbMac/DlScheduling
    std::ostringstream oss;
    std::string p = path.substr(0, path.find("/LteEnbMac"));
    oss << rnti;
    p += "/LteEnbRrc/UeMap/" + oss.str();
    uint16_t cellId = FindCellIdFromEnbRlcPath(p);
    NS_LOG_LOGIC("FindCellIdFromEnbMac: " << path << ", " << rnti << ", " << cellId);
    return cellId;
}

uint64_t
LteStatsCalculator::FindImsiForEnb(std::string path, uint16_t rnti)
{
    NS_LOG_FUNCTION(path << rnti);
    uint64_t imsi = 0;
    if (path.find("/DlPhyTransmission"))
    {
        // /NodeList/0/DeviceList/0/LteEnbPhy/DlPhyTransmission/LteEnbRrc/UeMap/1
        std::ostringstream oss;
        std::string p = path.substr(0, path.find("/LteEnbPhy"));
        oss << rnti;
        p += "/LteEnbRrc/UeMap/" + oss.str();
        imsi = FindImsiFromEnbRlcPath(p);
        NS_LOG_LOGIC("FindImsiForEnb[Tx]: " << path << ", " << rnti << ", " << imsi);
    }
    else if (path.find("/UlPhyReception"))
    {
        std::string p = path.substr(0, path.find("/LteUePhy"));
        imsi = FindImsiFromLteNetDevice(p);
        NS_LOG_LOGIC("FindImsiForEnb[Rx]: " << path << ", " << rnti << ", " << imsi);
    }
    return imsi;
}

uint64_t
LteStatsCalculator::FindImsiForUe(std::string path, uint16_t rnti)
{
    NS_LOG_FUNCTION(path << rnti);
    uint64_t imsi = 0;
    if (path.find("/UlPhyTransmission"))
    {
        std::string p = path.substr(0, path.find("/LteUePhy"));
        imsi = FindImsiFromLteNetDevice(p);
        NS_LOG_LOGIC("FindImsiForUe[Tx]: " << path << ", " << rnti << ", " << imsi);
    }
    else if (path.find("/DlPhyReception"))
    {
        // /NodeList/0/DeviceList/0/LteEnbPhy/LteSpectrumPhy
        std::ostringstream oss;
        std::string p = path.substr(0, path.find("/LteEnbPhy"));
        oss << rnti;
        p += "/LteEnbRrc/UeMap/" + oss.str();
        imsi = FindImsiFromEnbRlcPath(p);
        NS_LOG_LOGIC("FindImsiForUe[Rx]: " << path << ", " << rnti << ", " << imsi);
    }
    return imsi;
}

} // namespace ns3
