/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
 * CANN Open Software License Agreement Version 2.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 */

#include "qs_interface_process.h"
#include <securec.h>
#include <unistd.h>
#include "common/bqs_log.h"
#include "common/bqs_util.h"
#include "msprof_manager.h"
#include "queue_schedule_hal_interface_ref.h"
#include "common/bqs_feature_ctrl.h"

namespace bqs {
// 查询bind信息间隔
constexpr uint32_t QUERY_BIND_HOST_PID_INTERVBALE = 10000U;
// 查询bind超时时间
#ifndef aicpusd_UT
constexpr uint32_t QUERY_BIND_HOST_PID_TIME = 120000000U;
#else
constexpr uint32_t QUERY_BIND_HOST_PID_TIME = 100000U;
#endif
// bind结果的日志输出周期
constexpr uint32_t QUERY_BIND_HOST_PID_LOG_INTERVAL = 1000U;
QueueScheduleInterface& QueueScheduleInterface::GetInstance()
{
    static QueueScheduleInterface instance;
    return instance;
}

/**
 * @ingroup QueueScheduleInterface
 * @brief it use to initialize aicpu schedule.
 * @param [in] params InitQsParams
 * @return BQS_STATUS_OK: success, other: error code
 */
int32_t QueueScheduleInterface::InitQueueScheduler(const InitQsParams& params)
{
    const std::unique_lock<std::mutex> lk(mutexForInit_);
    BQS_LOG_INFO("Start up BqsInterface.numaFlag[%d]", params.numaFlag);
    if (bqs::GetRunContext() != bqs::RunContext::HOST) {
        const auto initCpuInfoRet = BindCpuUtils::GetDevCpuInfo(
            params.deviceId, aiCpuIds_, ctrlCpuIds_, coreNumPerDev_, aicpuNum_, aicpuBaseId_);
        if (initCpuInfoRet != BQS_STATUS_OK) {
            return static_cast<int32_t>(initCpuInfoRet);
        }
    }
    if (params.numaFlag) {
        const auto initCpuInfoRet = BindCpuUtils::GetDevCpuInfo(
            params.deviceIdExtra, aiCpuIdsExtra_, ctrlCpuIdsExtra_, coreNumPerDevExtra_, aicpuNumExtra_,
            aicpuBaseIdExtra_);
        if (initCpuInfoRet != BQS_STATUS_OK) {
            BQS_LOG_ERROR("GetDevCpuInfo error");
            return static_cast<int32_t>(initCpuInfoRet);
        }
    }

    queueSchedule_.reset(new (std::nothrow) QueueSchedule(params));
    if (queueSchedule_ == nullptr) {
        BQS_LOG_ERROR("Fail to allocate QueueSchedule");
        return static_cast<int32_t>(BQS_STATUS_INNER_ERROR);
    }
    BQS_LOG_INFO(
        "Prepare bind process with host process, deviceId[%u], RunContext is [%u]", params.deviceId,
        static_cast<uint32_t>(bqs::GetRunContext()));
    // bind process with hostpid in process mode
    if ((bqs::GetRunContext() != bqs::RunContext::HOST) && (params.starter != bqs::QsStartType::START_BY_DEPLOYER) &&
        (params.runMode != QueueSchedulerRunMode::MULTI_THREAD)) {
        const auto bindRet = CheckBindHostPid(params.pid);
        if (bindRet != bqs::BQS_STATUS_OK) {
            BQS_LOG_ERROR("BindHostPid failed");
            return static_cast<int32_t>(bindRet);
        }
    }
    // init profiling
    const bool profFlag = params.profFlag;
    BqsMsprofManager::GetInstance().InitBqsMsprofManager(profFlag, params.profCfgData);

    const bqs::BqsStatus bsqStatus = queueSchedule_->StartQueueSchedule();
    if (bsqStatus != bqs::BQS_STATUS_OK) {
        BQS_LOG_ERROR("QueueSchedule start failed, ret=%d", static_cast<int32_t>(bsqStatus));
        return static_cast<int32_t>(bsqStatus);
    }
    return static_cast<int32_t>(BQS_STATUS_OK);
}

void QueueScheduleInterface::WaitForStop()
{
    if (queueSchedule_ != nullptr) {
        queueSchedule_->WaitForStop();
    }
}

/**
 * @ingroup QueueScheduleInterface
 * @brief it use to destroy all model.
 */
int32_t QueueScheduleInterface::Destroy() const
{
    if (queueSchedule_ != nullptr) {
        queueSchedule_->Destroy();
    }
    return static_cast<int32_t>(BQS_STATUS_OK);
}

uint32_t QueueScheduleInterface::GetAicpuPhysIndex(uint32_t deviceId, const uint32_t aicpuLogIndex) const
{
    if (FeatureCtrl::IsAosCore()) {
        // connot overflow
        return ((aicpuBaseId_ + aicpuNum_) * deviceId) + aicpuBaseId_ + aicpuLogIndex;
    }

    if (aicpuLogIndex >= aiCpuIds_.size()) {
        BQS_LOG_WARN(
            "Get aicpu index not success, aicpuLogIndex[%u] invalid, valid range is [0, %zu)", aicpuLogIndex,
            aiCpuIds_.size());
        return 0U;
    }

    if (FeatureCtrl::BindCpuOnlyOneDevice()) {
        // 只有一个device,vf切分到同一容器时会产生多个deviceId。
        return aiCpuIds_[aicpuLogIndex];
    }
    return (coreNumPerDev_ * deviceId) + aiCpuIds_[aicpuLogIndex];
}

uint32_t QueueScheduleInterface::GetExtraAicpuPhysIndex(uint32_t deviceId, const uint32_t aicpuLogIndex) const
{
#ifdef _AOSCORE_
    // connot overflow
    return ((aicpuBaseIdExtra_ + aicpuNumExtra_) * deviceId) + aicpuBaseIdExtra_ + aicpuLogIndex;
#else
    if (aicpuLogIndex >= aiCpuIdsExtra_.size()) {
        BQS_LOG_ERROR(
            "Get extra aicpu index error, aicpuLogIndex[%u] invalid, valid range is [0, %zu)", aicpuLogIndex,
            aiCpuIdsExtra_.size());
        return 0U;
    }
#ifdef BIND_CPU_ONLY_ONE_DEVICE
    // 只有一个device,vf切分到同一容器时会产生多个deviceId。
    return aiCpuIdsExtra_[aicpuLogIndex];
#else
    return (coreNumPerDevExtra_ * deviceId) + aiCpuIdsExtra_[aicpuLogIndex];
#endif
#endif
}

int32_t QueueScheduleInterface::CheckBindHostPid(const uint32_t selfHostPid) const
{
    BQS_LOG_RUN_INFO("Start query process host pid");
    if (&drvQueryProcessHostPid == nullptr) {
        BQS_LOG_INFO("drvQueryProcessHostPid does not exist");
        return bqs::BQS_STATUS_OK;
    }
    drvError_t ret = DRV_ERROR_NONE;
    unsigned int hostpid = 0;
    unsigned int cpType = DEVDRV_PROCESS_CPTYPE_MAX;
    const int pid = static_cast<int>(getpid());
    for (uint32_t i = 0; i < QUERY_BIND_HOST_PID_TIME / QUERY_BIND_HOST_PID_INTERVBALE; i++) {
        ret = drvQueryProcessHostPid(pid, nullptr, nullptr, &hostpid, &cpType);
        if ((i % QUERY_BIND_HOST_PID_LOG_INTERVAL) == 0U) {
            BQS_LOG_RUN_INFO(
                "Query process host pid end, ret=%d, hostpid=%u, expect=%u, cpType=%u", static_cast<int32_t>(ret),
                hostpid, selfHostPid, cpType);
        }
        if (ret == DRV_ERROR_NO_PROCESS) {
            BQS_LOG_INFO("call drvQueryProcessHostPid try again");
            (void)usleep(QUERY_BIND_HOST_PID_INTERVBALE);
            continue;
        }
        if (ret != DRV_ERROR_NONE) {
            BQS_LOG_ERROR("call drvQueryProcessHostPid failed, ret[%d]", static_cast<int32_t>(ret));
            return bqs::BQS_STATUS_DRIVER_ERROR;
        }
        BQS_LOG_INFO("call drvQueryProcessHostPid result, hostpid[%d], cpType[%d]", hostpid, cpType);
        if (selfHostPid == static_cast<uint32_t>(hostpid)) {
            BQS_LOG_RUN_INFO("call drvQueryProcessHostPid success, hostpid[%d]", hostpid);
            return bqs::BQS_STATUS_OK;
        } else {
            BQS_LOG_ERROR(
                "CheckBindHostPid failed, hostpid not right. ret[%d], pid[%d], hostpid[%d], cpType[%d]",
                static_cast<int32_t>(ret), pid, hostpid, cpType);
            return bqs::BQS_STATUS_DRIVER_ERROR;
        }
    }
    BQS_LOG_ERROR(
        "CheckBindHostPid failed, try timeout. ret[%d], pid[%d], hostpid[%d], cpType[%d]", static_cast<int32_t>(ret),
        pid, hostpid, cpType);
    return bqs::BQS_STATUS_DRIVER_ERROR;
}

void QueueScheduleInterface::ReportAbnormal() const
{
    if (queueSchedule_ != nullptr) {
        queueSchedule_->ReportAbnormal();
    }
}
} // namespace bqs