/* -------------------------------------------------------------------------
 *  This file is part of the MindStudio project.
 * Copyright (c) 2025 Huawei Technologies Co.,Ltd.
 *
 * MindStudio is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * 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 FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * ------------------------------------------------------------------------- */


#include "hal_helper.h"
#include <dlfcn.h>
#include "log.h"
#include "ascend_helper.h"
#include "filesystem.h"
using namespace Utility;

namespace Common {
HalHelper &HalHelper::Instance(void)
{
    static HalHelper instance;
    return instance;
}

HalHelper::HalHelper()
{
    std::string halSo = GetSoFromEnvVar("libascend_hal.so");
    if (halSo.empty() || !CheckInputFileValid(halSo, "so")) {
        LogWarn("Can't find valid libascend_hal.so, please check your LD_LIBRARY_PATH");
        return;
    }
    handleHal_ = dlopen(halSo.c_str(), RTLD_LAZY);
    if (handleHal_ == nullptr) {
        return;
    }
    halGetDeviceInfo_ = (halGetDeviceInfoFunc) dlsym(handleHal_, "halGetDeviceInfo");
}

HalHelper::~HalHelper()
{
    if (handleHal_ != nullptr) {
        dlclose(handleHal_);
        handleHal_ = nullptr;
    }
    if (handleDcmi_ != nullptr) {
        dlclose(handleDcmi_);
        handleDcmi_ = nullptr;
    }
}

ChipType HalHelper::GetPlatformType(void) const
{
    static ChipType chipType = ChipType::END_TYPE;
    if (chipType != ChipType::END_TYPE) {
        return chipType;
    }
    if (halGetDeviceInfo_ == nullptr) {
        LogError("Fail to get soc platform because of null ptr");
        return ChipType::END_TYPE;
    }
    int64_t versionInfo = 0;
    int ret = halGetDeviceInfo_(0, MODULE_TYPE_SYSTEM, INFO_TYPE_VERSION, &versionInfo);
    if (ret != DRV_ERROR_NONE) {
        LogError("Fail to get soc platform because of error code:%d.", ret);
        return ChipType::END_TYPE;
    }
 
    uint32_t platformType = (static_cast<uint64_t>(versionInfo) >> 8) & 0xff;
    if ((platformType > static_cast<uint32_t>(ChipType::ASCEND310B) &&
        platformType < static_cast<uint32_t>(ChipType::ASCEND950)) ||
        platformType >= static_cast<uint32_t>(ChipType::END_TYPE)) {
        LogError("Fail to get soc platform because of invalid type:%u.", platformType);
        return ChipType::END_TYPE;
    }
    chipType = static_cast<ChipType>(platformType);
    return chipType;
}

bool HalHelper::GetAicoreFreq(int64_t &freq) const
{
    if (halGetDeviceInfo_ == nullptr) {
        return false;
    }
    freq = 0;
    int ret = halGetDeviceInfo_(0, MODULE_TYPE_AICORE, INFO_TYPE_FREQUE, &freq);
    if (ret != DRV_ERROR_NONE || freq == 0) {
        return false;
    }
    return true;
}

bool HalHelper::GetTaskSchedulerFreq(int64_t &freq) const
{
    if (halGetDeviceInfo_ == nullptr) {
        return false;
    }
    freq = 0;
    int ret = halGetDeviceInfo_(0, MODULE_TYPE_SYSTEM, INFO_TYPE_DEV_OSC_FREQUE, &freq);
    if (ret != DRV_ERROR_NONE || freq == 0) {
        return false;
    }
    return true;
}

bool HalHelper::GetAiCoreNum(int64_t &aiCoreNum) const
{
    if (halGetDeviceInfo_ == nullptr) {
        return false;
    }
    aiCoreNum = 0;
    int ret = halGetDeviceInfo_(0, MODULE_TYPE_AICORE, INFO_TYPE_CORE_NUM, &aiCoreNum);
    if (ret != DRV_ERROR_NONE || aiCoreNum == 0) {
        return false;
    }
    return true;
}

bool HalHelper::IsSupportPlatform(void) const
{
    ChipType chipType = GetPlatformType();
    return IsSupportPlatform(chipType);
}

bool HalHelper::IsSupportPlatform(ChipType chipType) const
{
    return chipType == ChipType::ASCEND910B || chipType == ChipType::ASCEND310P || chipType == ChipType::ASCEND310B
        || chipType == ChipType::ASCEND950;
}

bool HalHelper::DcmiInit()
{
    using DcmiInitFunc = int(*)();
    if (handleDcmi_ == nullptr) {
        return false;
    }
    DcmiInitFunc dcmiInit = (DcmiInitFunc) dlsym(handleDcmi_, "dcmi_init");
    if (dcmiInit == nullptr) {
        LogDebug("Can not get device dcmi info function.");
        dlclose(handleDcmi_);
        handleDcmi_ = nullptr;
        return false;
    }
    int ret = dcmiInit();
    return ret == 0;
}

bool HalHelper::GetCardIdDeviceIdFromLogicId(int *cardId, int *chipId, unsigned int logicId) const
{
    using DcmiGetCardIdDeviceIdFunc = int(*)(int *, int *, int);
    if (handleDcmi_ == nullptr) {
        LogDebug("Can not get device id info.");
        return false;
    }
    DcmiGetCardIdDeviceIdFunc dcmiGetCardIdDeviceId = (DcmiGetCardIdDeviceIdFunc) dlsym(handleDcmi_,
        "dcmi_get_card_id_device_id_from_logicid");
    if (dcmiGetCardIdDeviceId == nullptr) {
        LogDebug("Can not get device id info function.");
        return false;
    }
    int ret = dcmiGetCardIdDeviceId(cardId, chipId, logicId);
    if (ret != 0) {
        LogDebug("Can not get card and device id. Error code is %d", ret);
        return false;
    }
    return true;
}

bool HalHelper::SetGmType(int cardId, int deviceId, dcmi_gm_product_info_t &gmInfo) const
{
    using DcmiGetDeviceGmInfoFunc = int(*)(int, int, dcmi_gm_product_info_t*);
    if (handleDcmi_ == nullptr) {
        LogDebug("Can not get device gm info.");
        return false;
    }
    DcmiGetDeviceGmInfoFunc dcmiGetDeviceGmInfo = (DcmiGetDeviceGmInfoFunc) dlsym(handleDcmi_,
        "dcmi_get_device_hbm_product_info");
    if (dcmiGetDeviceGmInfo  == nullptr) {
        LogDebug("Can not get device gm info function.");
        return false;
    }
    int ret = dcmiGetDeviceGmInfo(cardId, deviceId, &gmInfo);
    if (ret != 0) {
        LogDebug("Can not get gm.Error code is %d", ret);
        return false;
    }
    LogDebug("The gm of device %d is %d", deviceId, gmInfo.manufacturer_id);
    return true;
}

void HalHelper::CheckGmType()
{
    ChipType chipType = GetPlatformType();
    if (handleDcmi_ != nullptr || chipType != ChipType::ASCEND910B) {
        return;
    }
    std::string dcmiSo = GetSoFromEnvVar("libdcmi.so");
    if (dcmiSo.empty() || !CheckInputFileValid(dcmiSo, "so")) {
        LogWarn("Can't find valid libdcmi.so, will use default gm type value");
        return;
    }
    handleDcmi_ = dlopen(dcmiSo.c_str(), RTLD_LAZY | RTLD_LOCAL);
    gmType_ = GmType::DEFAULT;
    if (!DcmiInit()) {
        LogWarn("Failed to get gm info, the default parameters will be used.");
        return;
    }
    int cardId = 0;
    int chipId = 0;
    if (!GetCardIdDeviceIdFromLogicId(&cardId, &chipId, deviceId_)) {
        return;
    }
    dcmi_gm_product_info_t gmInfo;
    if (!SetGmType(cardId, chipId, gmInfo)) {
        return;
    }
    if (GM_PRODUCT.find(gmInfo.manufacturer_id) != GM_PRODUCT.end()) {
        gmType_ = static_cast<GmType>(gmInfo.manufacturer_id);
    }
}

void HalHelper::SetCurrentDeviceId(unsigned int deviceId)
{
    deviceId_ = deviceId;
}

int HalHelper::GetCurrentDeviceId() const
{
    return deviceId_;
}

GmType HalHelper::GetGmType()
{
    CheckGmType();
    return gmType_;
}
}  // namespace Common