//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//

#include "task.hpp"

#include <sstream>

#include <gnb/app/task.hpp>
#include <gnb/sctp/task.hpp>

namespace nr::gnb
{

// 切换清理定时器
static constexpr const int TIMER_ID_HANDOVER_CLEANUP = 3001;
static constexpr const int TIMER_PERIOD_HANDOVER_CLEANUP = 5000; // 5秒检查一次

NgapTask::NgapTask(TaskBase *base) : m_base{base}, m_ueNgapIdCounter{}, m_ueIdCounter{100}, m_downlinkTeidCounter{}, m_isInitialized{}
{
    m_logger = base->logBase->makeUniqueLogger("ngap");
}

void NgapTask::onStart()
{
    for (auto &amfConfig : m_base->config->amfConfigs)
        createAmfContext(amfConfig);
    if (m_amfCtx.empty())
        m_logger->warn("No AMF configuration is provided");

    for (auto &amfCtx : m_amfCtx)
    {
        auto msg = std::make_unique<NmGnbSctp>(NmGnbSctp::CONNECTION_REQUEST);
        msg->clientId = amfCtx.second->ctxId;
        msg->localAddress = m_base->config->ngapIp;
        msg->localPort = 0;
        msg->remoteAddress = amfCtx.second->address;
        msg->remotePort = amfCtx.second->port;
        msg->ppid = sctp::PayloadProtocolId::NGAP;
        msg->associatedTask = this;
        m_base->sctpTask->push(std::move(msg));
    }
    
    // 启动切换清理定时器
    setTimer(TIMER_ID_HANDOVER_CLEANUP, TIMER_PERIOD_HANDOVER_CLEANUP);
}

void NgapTask::onLoop()
{
    auto msg = take();
    if (!msg)
        return;

    switch (msg->msgType)
    {
    case NtsMessageType::TIMER_EXPIRED: {
        auto &w = dynamic_cast<NmTimerExpired &>(*msg);
        if (w.timerId == TIMER_ID_HANDOVER_CLEANUP) {
            checkAndCleanupExpiredHandovers();
            setTimer(TIMER_ID_HANDOVER_CLEANUP, TIMER_PERIOD_HANDOVER_CLEANUP);
        }
        break;
    }
    case NtsMessageType::GNB_RRC_TO_NGAP: {
        auto &w = dynamic_cast<NmGnbRrcToNgap &>(*msg);
        switch (w.present)
        {
        case NmGnbRrcToNgap::INITIAL_NAS_DELIVERY: {
            handleInitialNasTransport(w.ueId, w.pdu, w.rrcEstablishmentCause, w.sTmsi);
            break;
        }
        case NmGnbRrcToNgap::UPLINK_NAS_DELIVERY: {
            handleUplinkNasTransport(w.ueId, w.pdu);
            break;
        }
        case NmGnbRrcToNgap::RADIO_LINK_FAILURE: {
            handleRadioLinkFailure(w.ueId);
            break;
        }
        case NmGnbRrcToNgap::HANDOVER_TRIGGER: {
            // 处理来自RRC的切换触发
            triggerHandover(w.ueId, w.targetCellId, w.targetGnbId);
            break;
        }
        case NmGnbRrcToNgap::HANDOVER_REQUEST_ACK: {
            // 处理来自RRC的切换请求确认
            sendHandoverRequestAcknowledge(w.ueId, w.sourceToTargetContainer);
            break;
        }
        case NmGnbRrcToNgap::HANDOVER_REQUEST_FAILURE: {
            // 处理来自RRC的切换请求失败
            m_logger->err("RRC reported handover request failure for UE {}", w.ueId);
            // 清理切换上下文
            auto *ueCtx = findUeContext(w.ueId);
            if (ueCtx) {
                // 删除为切换创建的UE上下文
                deleteUeContext(w.ueId);
                m_logger->info("Cleaned up UE context {} due to handover failure", w.ueId);
            }
            break;
        }
        case NmGnbRrcToNgap::HANDOVER_RRC_COMPLETE: {
            // UE已在目标侧完成RRC重配置，正式发起Path Switch
            sendPathSwitchRequest(w.ueId);
            break;
        }
        }
        break;
    }
    case NtsMessageType::GNB_SCTP: {
        auto &w = dynamic_cast<NmGnbSctp &>(*msg);
        switch (w.present)
        {
        case NmGnbSctp::ASSOCIATION_SETUP:
            handleAssociationSetup(w.clientId, w.associationId, w.inStreams, w.outStreams);
            break;
        case NmGnbSctp::RECEIVE_MESSAGE:
            handleSctpMessage(w.clientId, w.stream, w.buffer);
            break;
        case NmGnbSctp::ASSOCIATION_SHUTDOWN:
            handleAssociationShutdown(w.clientId);
            break;
        default:
            m_logger->unhandledNts(*msg);
            break;
        }
        break;
    }
    default: {
        m_logger->unhandledNts(*msg);
        break;
    }
    }
}

void NgapTask::onQuit()
{
    for (auto &i : m_ueCtx)
        delete i.second;
    for (auto &i : m_amfCtx)
        delete i.second;
    m_ueCtx.clear();
    m_amfCtx.clear();
}

} // namespace nr::gnb
