/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "data_connection_manager.h"

#include "cellular_data_utils.h"
#include "core_manager_inner.h"
#include "radio_event.h"
#include "networkslice_client.h"

namespace OHOS {
namespace Telephony {

DataConnectionManager::DataConnectionManager(int32_t slotId) : StateMachine("DataConnectionManager"), slotId_(slotId)
{
    connectionMonitor_ = std::make_shared<DataConnectionMonitor>(slotId);
    if (connectionMonitor_ == nullptr) {
        TELEPHONY_LOGE("Slot%{public}d: connectionMonitor_ is null", slotId_);
        return;
    }
}

DataConnectionManager::~DataConnectionManager()
{
    if (connectionMonitor_ != nullptr) {
        connectionMonitor_->RemoveAllEvents();
    }
}

void DataConnectionManager::Init()
{
    ccmDefaultState_ = std::make_shared<CcmDefaultState>(*this, "CcmDefaultState");
    if (ccmDefaultState_ == nullptr) {
        TELEPHONY_LOGE("Slot%{public}d: ccmDefaultState_ is null", slotId_);
        return;
    }
    StateMachine::SetOriginalState(ccmDefaultState_);
    StateMachine::Start();
}

void DataConnectionManager::AddConnectionStateMachine(const std::shared_ptr<CellularDataStateMachine> &stateMachine)
{
    std::lock_guard<std::mutex> lock(stateMachineMutex_);
    if (stateMachine != nullptr) {
        stateMachines_.push_back(stateMachine);
    }
}

void DataConnectionManager::RemoveConnectionStateMachine(const std::shared_ptr<CellularDataStateMachine> &stateMachine)
{
    if (stateMachine == nullptr) {
        TELEPHONY_LOGE("Slot%{public}d: stateMachine is null", slotId_);
        return;
    }
    std::lock_guard<std::mutex> lock(stateMachineMutex_);
    for (std::vector<std::shared_ptr<CellularDataStateMachine>>::const_iterator iter =
        stateMachines_.begin(); iter != stateMachines_.end(); iter++) {
        if (*iter.base() == stateMachine) {
            stateMachines_.erase(iter);
            break;
        }
    }
}

std::vector<std::shared_ptr<CellularDataStateMachine>> DataConnectionManager::GetAllConnectionMachine()
{
    std::lock_guard<std::mutex> lock(stateMachineMutex_);
    return stateMachines_;
}

void DataConnectionManager::AddActiveConnectionByCid(const std::shared_ptr<CellularDataStateMachine> &stateMachine)
{
    std::lock_guard<std::mutex> lock(activeConnectionMutex_);
    if (stateMachine != nullptr) {
        cidActiveConnectionMap_[stateMachine->GetCid()] = stateMachine;
    }
}

std::shared_ptr<CellularDataStateMachine> DataConnectionManager::GetActiveConnectionByCid(int32_t cid)
{
    std::lock_guard<std::mutex> lock(activeConnectionMutex_);
    std::map<int32_t, std::shared_ptr<CellularDataStateMachine>>::const_iterator it = cidActiveConnectionMap_.find(cid);
    if (it != cidActiveConnectionMap_.end()) {
        return it->second;
    }
    return nullptr;
}

std::map<int32_t, std::shared_ptr<CellularDataStateMachine>> DataConnectionManager::GetActiveConnection()
{
    std::lock_guard<std::mutex> lock(activeConnectionMutex_);
    return cidActiveConnectionMap_;
}

bool DataConnectionManager::IsBandwidthSourceModem() const
{
    return bandwidthSourceModem_;
}

bool DataConnectionManager::isNoActiveConnection()
{
    std::lock_guard<std::mutex> lock(activeConnectionMutex_);
    if (cidActiveConnectionMap_.empty()) {
        return true;
    }
    return false;
}

void DataConnectionManager::RemoveActiveConnectionByCid(int32_t cid)
{
    std::lock_guard<std::mutex> lock(activeConnectionMutex_);
    if (cidActiveConnectionMap_.find(cid) != cidActiveConnectionMap_.end()) {
        cidActiveConnectionMap_.erase(cid);
    }
}

void DataConnectionManager::StartStallDetectionTimer()
{
    if (connectionMonitor_ != nullptr) {
        connectionMonitor_->StartStallDetectionTimer();
    }
}

void DataConnectionManager::StopStallDetectionTimer()
{
    if (connectionMonitor_ != nullptr) {
        connectionMonitor_->StopStallDetectionTimer();
    }
}

void DataConnectionManager::RegisterRadioObserver()
{
    if (stateMachineEventHandler_ == nullptr) {
        TELEPHONY_LOGE("stateMachineEventHandler_ is nullptr!");
        return;
    }
    CoreManagerInner &coreInner = CoreManagerInner::GetInstance();
    coreInner.RegisterCoreNotify(slotId_, stateMachineEventHandler_, RadioEvent::RADIO_CONNECTED, nullptr);
    coreInner.RegisterCoreNotify(slotId_, stateMachineEventHandler_, RadioEvent::RADIO_DATA_CALL_LIST_CHANGED, nullptr);
    coreInner.RegisterCoreNotify(
        slotId_, stateMachineEventHandler_, RadioEvent::RADIO_LINK_CAPABILITY_CHANGED, nullptr);
    coreInner.RegisterCoreNotify(slotId_, stateMachineEventHandler_, RadioEvent::RADIO_NETWORKSLICE_URSP_RPT, nullptr);
    coreInner.RegisterCoreNotify(slotId_, stateMachineEventHandler_,
        RadioEvent::RADIO_NETWORKSLICE_ALLOWEDNSSAI_RPT, nullptr);
    coreInner.RegisterCoreNotify(slotId_, stateMachineEventHandler_,
        RadioEvent::RADIO_NETWORKSLICE_EHPLMN_RPT, nullptr);
}

void DataConnectionManager::UnRegisterRadioObserver() const
{
    if (stateMachineEventHandler_ == nullptr) {
        TELEPHONY_LOGE("stateMachineEventHandler_ is nullptr!");
        return;
    }
    CoreManagerInner &coreInner = CoreManagerInner::GetInstance();
    coreInner.UnRegisterCoreNotify(slotId_, stateMachineEventHandler_, RadioEvent::RADIO_CONNECTED);
    coreInner.UnRegisterCoreNotify(slotId_, stateMachineEventHandler_, RadioEvent::RADIO_DATA_CALL_LIST_CHANGED);
    coreInner.UnRegisterCoreNotify(slotId_, stateMachineEventHandler_, RadioEvent::RADIO_LINK_CAPABILITY_CHANGED);
    coreInner.UnRegisterCoreNotify(slotId_, stateMachineEventHandler_, RadioEvent::RADIO_NETWORKSLICE_URSP_RPT);
    coreInner.UnRegisterCoreNotify(slotId_, stateMachineEventHandler_, RadioEvent::RADIO_NETWORKSLICE_ALLOWEDNSSAI_RPT);
    coreInner.UnRegisterCoreNotify(slotId_, stateMachineEventHandler_, RadioEvent::RADIO_NETWORKSLICE_EHPLMN_RPT);
}

void CcmDefaultState::StateBegin()
{
    connectManager_.RegisterRadioObserver();
}

void CcmDefaultState::StateEnd()
{
    connectManager_.UnRegisterRadioObserver();
}

bool CcmDefaultState::StateProcess(const AppExecFwk::InnerEvent::Pointer &event)
{
    if (event == nullptr) {
        TELEPHONY_LOGE("event is null");
        return false;
    }
    int32_t id = event->GetInnerEventId();
    switch (id) {
        case RadioEvent::RADIO_CONNECTED:
            TELEPHONY_LOGI("Radio is connected");
            break;
        case RadioEvent::RADIO_DATA_CALL_LIST_CHANGED:
            RadioDataCallListChanged(event);
            break;
        case RadioEvent::RADIO_LINK_CAPABILITY_CHANGED:
            RadioLinkCapabilityChanged(event);
            break;
        case RadioEvent::RADIO_NETWORKSLICE_URSP_RPT:
            RadioNetworkSliceUrspRpt(event);
            break;
        case RadioEvent::RADIO_NETWORKSLICE_ALLOWEDNSSAI_RPT:
            RadioNetworkSliceAllowedNssaiRpt(event);
            break;
        case RadioEvent::RADIO_NETWORKSLICE_EHPLMN_RPT:
            RadioNetworkSliceEhplmnRpt(event);
            break;
        default:
            TELEPHONY_LOGE("handle nothing!");
            return false;
    }
    return true;
}

void CcmDefaultState::RadioDataCallListChanged(const AppExecFwk::InnerEvent::Pointer &event)
{
    std::shared_ptr<DataCallResultList> infos = event->GetSharedObject<DataCallResultList>();
    if (infos == nullptr) {
        TELEPHONY_LOGE("setupDataCallResultInfo is null");
        return;
    }
    std::map<int32_t, std::shared_ptr<CellularDataStateMachine>> idActiveConnectionMap =
        connectManager_.GetActiveConnection();
    UpdateNetworkInfo(event);
    for (const std::pair<const int32_t, std::shared_ptr<CellularDataStateMachine>>& it : idActiveConnectionMap) {
        if (it.second == nullptr) {
            TELEPHONY_LOGI("The activation item is null(%{public}d)", it.first);
            continue;
        }
        int32_t cid = it.second->GetCid();
        for (size_t i = 0; i < infos->dcList.size(); ++i) {
            if (infos->dcList[i].cid == cid && infos->dcList[i].active <= 0) {
                auto object = std::make_shared<SetupDataCallResultInfo>();
                object->cid = cid;
                object->reason = infos->dcList[i].reason;
                object->retryTime = infos->dcList[i].retryTime;
                object->retryScene = static_cast<int32_t>(RetryScene::RETRY_SCENE_MODEM_DEACTIVATE);
                TELEPHONY_LOGI("add to retry (modem dend): cid=%{public}d, cause=%{public}d", cid, object->reason);
                AppExecFwk::InnerEvent::Pointer event =
                    AppExecFwk::InnerEvent::Get(CellularDataEventCode::MSG_SM_LOST_CONNECTION, object);
                it.second->SendEvent(event);
                break;
            }
        }
    }
}

void CcmDefaultState::RadioLinkCapabilityChanged(const AppExecFwk::InnerEvent::Pointer &event)
{
    std::shared_ptr<DataLinkCapability> linkCapability = event->GetSharedObject<DataLinkCapability>();
    if (linkCapability == nullptr) {
        TELEPHONY_LOGE("linkCapability is null");
        return;
    }
    if (connectManager_.IsBandwidthSourceModem()) {
        std::map<int32_t, std::shared_ptr<CellularDataStateMachine>> idActiveConnectionMap =
            connectManager_.GetActiveConnection();
        for (const std::pair<const int32_t, std::shared_ptr<CellularDataStateMachine>> &it : idActiveConnectionMap) {
            if (it.second == nullptr) {
                TELEPHONY_LOGI("The activation item is null(%{public}d)", it.first);
                continue;
            }
            AppExecFwk::InnerEvent::Pointer smEvent =
                AppExecFwk::InnerEvent::Get(CellularDataEventCode::MSG_SM_LINK_CAPABILITY_CHANGED, linkCapability);
            it.second->SendEvent(smEvent);
        }
    }
}

void CcmDefaultState::UpdateNetworkInfo(const AppExecFwk::InnerEvent::Pointer &event)
{
    std::shared_ptr<DataCallResultList> infos = event->GetSharedObject<DataCallResultList>();
    if (infos == nullptr) {
        TELEPHONY_LOGE("dataCallResultList is null");
        return;
    }
    for (SetupDataCallResultInfo &it : infos->dcList) {
        std::shared_ptr<CellularDataStateMachine> dataConnect = connectManager_.GetActiveConnectionByCid(it.cid);
        if (dataConnect == nullptr) {
            TELEPHONY_LOGD("get active connection by cid is :=  %{public}d flag:=  %{public}d ", it.cid, it.flag);
            continue;
        }
        dataConnect->UpdateNetworkInfoIfInActive(it);
    }
}

void DataConnectionManager::BeginNetStatistics()
{
    if (connectionMonitor_ != nullptr) {
        connectionMonitor_->BeginNetStatistics();
    }
}

void DataConnectionManager::EndNetStatistics()
{
    if (connectionMonitor_ != nullptr) {
        connectionMonitor_->EndNetStatistics();
    }
}

void DataConnectionManager::UpdateCallState(int32_t state)
{
    if (connectionMonitor_ != nullptr) {
        connectionMonitor_->UpdateCallState(state);
    }
}

int32_t DataConnectionManager::GetDataRecoveryState()
{
    if (connectionMonitor_ != nullptr) {
        return static_cast<int32_t>(connectionMonitor_->GetDataRecoveryState());
    }
    return -1;
}

int32_t DataConnectionManager::GetSlotId() const
{
    return slotId_;
}

int32_t DataConnectionManager::GetDataFlowType()
{
    if (connectionMonitor_ == nullptr) {
        TELEPHONY_LOGE("Slot%{public}d: connection monitor is null", slotId_);
        return static_cast<int32_t>(CellDataFlowType::DATA_FLOW_TYPE_NONE);
    }
    CellDataFlowType flowType = connectionMonitor_->GetDataFlowType();
    return static_cast<int32_t>(flowType);
}

void DataConnectionManager::SetDataFlowType(CellDataFlowType dataFlowType)
{
    if (connectionMonitor_ == nullptr) {
        TELEPHONY_LOGE("Slot%{public}d: connection monitor is null", slotId_);
        return;
    }
    connectionMonitor_->SetDataFlowType(dataFlowType);
}

void DataConnectionManager::GetDefaultBandWidthsConfig()
{
    OperatorConfig operatorConfig;
    CoreManagerInner::GetInstance().GetOperatorConfigs(slotId_, operatorConfig);
    if (operatorConfig.boolValue.find(KEY_BANDWIDTH_SOURCE_USE_MODEM_BOOL) != operatorConfig.boolValue.end()) {
        bandwidthSourceModem_ = operatorConfig.boolValue[KEY_BANDWIDTH_SOURCE_USE_MODEM_BOOL];
    }
    if (operatorConfig.boolValue.find(KEY_UPLINK_BANDWIDTH_NR_NSA_USE_LTE_VALUE_BOOL) !=
        operatorConfig.boolValue.end()) {
        uplinkUseLte_ = operatorConfig.boolValue[KEY_UPLINK_BANDWIDTH_NR_NSA_USE_LTE_VALUE_BOOL];
    }
    std::vector<std::string> linkBandwidthVec;
    if (operatorConfig.stringArrayValue.find(KEY_BANDWIDTH_STRING_ARRAY) != operatorConfig.stringArrayValue.end()) {
        linkBandwidthVec = operatorConfig.stringArrayValue[KEY_BANDWIDTH_STRING_ARRAY];
    }
    if (linkBandwidthVec.empty()) {
        linkBandwidthVec = CellularDataUtils::Split(DEFAULT_BANDWIDTH_CONFIG, ";");
    }
    std::lock_guard<std::mutex> lock(bandwidthConfigMutex_);
    bandwidthConfigMap_.clear();
    for (std::string temp : linkBandwidthVec) {
        std::vector<std::string> linkBandwidths = CellularDataUtils::Split(temp, ":");
        if (linkBandwidths.size() == VALID_VECTOR_SIZE) {
            std::string key = linkBandwidths.front();
            std::string linkUpDownBandwidth = linkBandwidths.back();
            std::vector<std::string> upDownBandwidthValue = CellularDataUtils::Split(linkUpDownBandwidth, ",");
            if (upDownBandwidthValue.size() == VALID_VECTOR_SIZE) {
                LinkBandwidthInfo linkBandwidthInfo;
                linkBandwidthInfo.downBandwidth = (atoi)(upDownBandwidthValue.front().c_str());
                linkBandwidthInfo.upBandwidth = (atoi)(upDownBandwidthValue.back().c_str());
                bandwidthConfigMap_.emplace(key, linkBandwidthInfo);
            }
        }
    }
    TELEPHONY_LOGI("Slot%{public}d: BANDWIDTH_CONFIG_MAP size is %{public}zu", slotId_, bandwidthConfigMap_.size());
    UpdateBandWidthsUseLte();
}

void DataConnectionManager::UpdateBandWidthsUseLte()
{
    if (!uplinkUseLte_) {
        return;
    }
    std::map<std::string, LinkBandwidthInfo>::iterator iter = bandwidthConfigMap_.find("LTE");
    if (iter != bandwidthConfigMap_.end()) {
        LinkBandwidthInfo lteLinkBandwidthInfo = iter->second;
        TELEPHONY_LOGI("Slot%{public}d: name is %{public}s upBandwidth = %{public}u downBandwidth = %{public}u",
            slotId_, iter->first.c_str(), lteLinkBandwidthInfo.upBandwidth, lteLinkBandwidthInfo.downBandwidth);
        iter = bandwidthConfigMap_.find("NR_NSA");
        if (iter != bandwidthConfigMap_.end()) {
            iter->second.upBandwidth = lteLinkBandwidthInfo.upBandwidth;
        }
        iter = bandwidthConfigMap_.find("NR_NSA_MMWAVE");
        if (iter != bandwidthConfigMap_.end()) {
            iter->second.upBandwidth = lteLinkBandwidthInfo.upBandwidth;
        }
    }
}

void DataConnectionManager::GetDefaultTcpBufferConfig()
{
    char tcpBufferConfig[MAX_BUFFER_SIZE] = {0};
    GetParameter(CONFIG_TCP_BUFFER, DEFAULT_TCP_BUFFER_CONFIG, tcpBufferConfig, MAX_BUFFER_SIZE);
    std::vector<std::string> tcpBufferVec = CellularDataUtils::Split(tcpBufferConfig, ";");
    std::lock_guard<std::mutex> lock(tcpBufferConfigMutex_);
    tcpBufferConfigMap_.clear();
    for (std::string tcpBuffer : tcpBufferVec) {
        std::vector<std::string> str = CellularDataUtils::Split(tcpBuffer, ":");
        if (str.size() < VALID_VECTOR_SIZE) {
            TELEPHONY_LOGE("Invalid tcp buffer config format: %{public}s", tcpBuffer.c_str());
            continue;
        }
        tcpBufferConfigMap_.emplace(str.front(), str.back());
    }
    TELEPHONY_LOGI("Slot%{public}d: TCP_BUFFER_CONFIG_MAP size is %{public}zu", slotId_, tcpBufferConfigMap_.size());
}

LinkBandwidthInfo DataConnectionManager::GetBandwidthsByRadioTech(const int32_t radioTech)
{
    LinkBandwidthInfo linkBandwidthInfo;
    CoreManagerInner &coreInner = CoreManagerInner::GetInstance();
    NrState nrState = coreInner.GetNrState(slotId_);
    FrequencyType frequencyType = coreInner.GetFrequencyType(slotId_);
    std::string radioTechName = CellularDataUtils::ConvertRadioTechToRadioName(radioTech);
    if (radioTech == (int32_t)RadioTech::RADIO_TECHNOLOGY_LTE &&
        (nrState == NrState::NR_NSA_STATE_DUAL_CONNECTED || nrState == NrState::NR_NSA_STATE_CONNECTED_DETECT)) {
        if (frequencyType == FrequencyType::FREQ_TYPE_MMWAVE) {
            radioTechName = "NR_NSA_MMWAVE";
        } else {
            radioTechName = "NR_NSA";
        }
    }
    if (radioTechName == "NR") {
        radioTechName = "NR_SA";
    }
    TELEPHONY_LOGD("Slot%{public}d: accessRadioName is %{private}s", slotId_, radioTechName.c_str());
    std::lock_guard<std::mutex> lock(bandwidthConfigMutex_);
    std::map<std::string, LinkBandwidthInfo>::iterator iter = bandwidthConfigMap_.find(radioTechName);
    if (iter != bandwidthConfigMap_.end()) {
        linkBandwidthInfo = iter->second;
        TELEPHONY_LOGI("Slot%{public}d: name is %{private}s upBandwidth = %{public}u downBandwidth = %{public}u",
            slotId_, iter->first.c_str(), linkBandwidthInfo.upBandwidth, linkBandwidthInfo.downBandwidth);
    }
    return linkBandwidthInfo;
}

std::string DataConnectionManager::GetTcpBufferByRadioTech(const int32_t radioTech)
{
    std::string tcpBuffer = "";
    CoreManagerInner &coreInner = CoreManagerInner::GetInstance();
    NrState nrState = coreInner.GetNrState(slotId_);
    std::string radioTechName = CellularDataUtils::ConvertRadioTechToRadioName(radioTech);
    if ((radioTech == (int32_t)RadioTech::RADIO_TECHNOLOGY_LTE ||
        radioTech == (int32_t)RadioTech::RADIO_TECHNOLOGY_LTE_CA) &&
        (nrState == NrState::NR_NSA_STATE_DUAL_CONNECTED || nrState == NrState::NR_NSA_STATE_CONNECTED_DETECT)) {
        radioTechName = "NR";
    }
    std::lock_guard<std::mutex> lock(tcpBufferConfigMutex_);
    std::map<std::string, std::string>::iterator iter = tcpBufferConfigMap_.find(radioTechName);
    if (iter != tcpBufferConfigMap_.end()) {
        tcpBuffer = iter->second;
    }
    return tcpBuffer;
}

void DataConnectionManager::IsNeedDoRecovery(bool needDoRecovery) const
{
    if (connectionMonitor_ != nullptr) {
        connectionMonitor_->IsNeedDoRecovery(needDoRecovery);
    }
}

void DataConnectionManager::HandleScreenStateChanged(bool isScreenOn) const
{
    if (connectionMonitor_ == nullptr) {
        TELEPHONY_LOGE("Slot%{public}d: connection monitor is null", slotId_);
        return;
    }
    connectionMonitor_->HandleScreenStateChanged(isScreenOn);
}

void CcmDefaultState::RadioNetworkSliceUrspRpt(const AppExecFwk::InnerEvent::Pointer &event)
{
    if (system::GetBoolParameter("persist.netmgr_ext.networkslice", false) == false) {
        return;
    }
    std::shared_ptr<NetworkSliceUrspInfo> networkSliceUrspInfo = event->GetSharedObject<NetworkSliceUrspInfo>();
    if (networkSliceUrspInfo == nullptr) {
        TELEPHONY_LOGE("networkSliceClient is null");
        return;
    }
    std::vector<uint8_t> buffer = networkSliceUrspInfo->urspInfo;
    DelayedSingleton<NetManagerStandard::NetworkSliceClient>::GetInstance()->SetNetworkSliceUePolicy(buffer);
}

void CcmDefaultState::RadioNetworkSliceAllowedNssaiRpt(const AppExecFwk::InnerEvent::Pointer &event)
{
    if (system::GetBoolParameter("persist.netmgr_ext.networkslice", false) == false) {
        return;
    }
    std::shared_ptr<NetworkSliceAllowedNssaiInfo> networkSliceAllowedNssaiInfo
        = event->GetSharedObject<NetworkSliceAllowedNssaiInfo>();
    if (networkSliceAllowedNssaiInfo == nullptr) {
        TELEPHONY_LOGE("networkSliceClient is null");
        return;
    }
    std::vector<uint8_t> buffer = networkSliceAllowedNssaiInfo->allowednssaiInfo;
    DelayedSingleton<NetManagerStandard::NetworkSliceClient>::GetInstance()->NetworkSliceAllowedNssaiRpt(buffer);
}

void CcmDefaultState::RadioNetworkSliceEhplmnRpt(const AppExecFwk::InnerEvent::Pointer &event)
{
    if (system::GetBoolParameter("persist.netmgr_ext.networkslice", false) == false) {
        return;
    }
    std::shared_ptr<NetworkSliceEhplmnInfo> networkSliceEhplmnInfo
        = event->GetSharedObject<NetworkSliceEhplmnInfo>();
    if (networkSliceEhplmnInfo == nullptr) {
        TELEPHONY_LOGE("networkSliceClient is null");
        return;
    }
    std::vector<uint8_t> buffer = networkSliceEhplmnInfo->ehplmnInfo;
    DelayedSingleton<NetManagerStandard::NetworkSliceClient>::GetInstance()->NetworkSliceEhplmnRpt(buffer);
}

} // namespace Telephony
} // namespace OHOS