/**
 * 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 "profiling.h"
#include "mmpa/mmpa_api.h"
#include "common/log_inner.h"
#include "common/json_parser.h"
#include "profiling_manager.h"

namespace {
    const std::string ACL_PROF_CONFIG_NAME = "profiler";
    std::mutex g_aclProfMutex;
    constexpr uint64_t ACL_PROF_ACL_API = 0x0001U;
    constexpr uint32_t START_PROFILING = 1U;
    constexpr uint32_t STOP_PROFILING = 2U;

    static aclError ProfInnerStart(const rtProfCommandHandle_t *const profilerConfig)
    {
        ACL_LOG_INFO("start to execute ProfInnerStart");
        if (!acl::AclProfilingManager::GetInstance().AclProfilingIsRun()) {
            const aclError ret = acl::AclProfilingManager::GetInstance().Init();
            if (ret != ACL_SUCCESS) {
                ACL_LOG_INNER_ERROR("[Init][ProfilingManager]start acl profiling module failed,"" errorCode = %d",
                    ret);
                return ret;
            }
        }
        (void)acl::AclProfilingManager::GetInstance().AddDeviceList(profilerConfig->devIdList, profilerConfig->devNums);
        ACL_LOG_INFO("successfully execute ProfInnerStart");
        return ACL_SUCCESS;
    }

    // inner interface for stoping profiling
    static aclError ProfInnerStop(const rtProfCommandHandle_t *const profilerConfig)
    {
        ACL_LOG_INFO("start to execute ProfInnerStop");
        if (!acl::AclProfilingManager::GetInstance().IsDeviceListEmpty()) {
            (void)acl::AclProfilingManager::GetInstance().RemoveDeviceList(profilerConfig->devIdList,
                profilerConfig->devNums);
        }

        if ((acl::AclProfilingManager::GetInstance().IsDeviceListEmpty()) &&
            (acl::AclProfilingManager::GetInstance().AclProfilingIsRun())) {
            const aclError ret = acl::AclProfilingManager::GetInstance().UnInit();
            if (ret != ACL_SUCCESS) {
                ACL_LOG_INNER_ERROR("[Uninit][ProfilingManager]stop acl failed, errorCode = %d", ret);
                return ret;
            }
        }
        ACL_LOG_INFO("successfully execute ProfInnerStop");
        return ACL_SUCCESS;
    }

    aclError ProcessProfData(void *const data, const uint32_t len)
    {
        ACL_LOG_INFO("start to execute ProcessProfData");
        const std::lock_guard<std::mutex> locker(g_aclProfMutex);
        ACL_REQUIRES_NOT_NULL(data);
        constexpr size_t commandLen = sizeof(rtProfCommandHandle_t);
        if (len < commandLen) {
            ACL_LOG_INNER_ERROR("[Check][Len]len[%u] is invalid, it should not be smaller than %zu", len, commandLen);
            return ACL_ERROR_INVALID_PARAM;
        }
        rtProfCommandHandle_t *const profilerConfig = static_cast<rtProfCommandHandle_t *>(data);
        aclError ret = ACL_SUCCESS;
        const uint64_t profSwitch = profilerConfig->profSwitch;
        const uint32_t type = profilerConfig->type;
        if (((profSwitch & ACL_PROF_ACL_API) != 0U) && (type == START_PROFILING)) {
            ret = ProfInnerStart(profilerConfig);
        }
        if (((profSwitch & ACL_PROF_ACL_API) != 0U) && (type == STOP_PROFILING)) {
            ret = ProfInnerStop(profilerConfig);
        }

        return ret;
    }
}

namespace acl {
    aclError AclProfiling::HandleProfilingCommand(const std::string &config, const bool configFileFlag,
        const bool noValidConfig)
    {
        ACL_LOG_INFO("start to execute HandleProfilingCommand");
        int32_t ret = MSPROF_ERROR_NONE;
        if (noValidConfig) {
            ret = MsprofInit(MSPROF_CTRL_INIT_DYNA, nullptr, 0U);
            if (ret != MSPROF_ERROR_NONE) {
                ACL_LOG_CALL_ERROR("[Init][Profiling]init profiling with nullptr failed, profiling errorCode = %d",
                    ret);
                return ACL_SUCCESS;
            }
        } else {
            if (configFileFlag) {
                ret = MsprofInit(MSPROF_CTRL_INIT_ACL_JSON,
                                 const_cast<char_t *>(config.c_str()),
                                 static_cast<uint32_t>(config.size()));
                if (ret != MSPROF_ERROR_NONE) {
                    ACL_LOG_CALL_ERROR("[Init][Profiling]handle json config of profiling failed, profiling "
                        "result = %d", ret);
                    return ACL_ERROR_INVALID_PARAM;
                }
            } else {
                ret = MsprofInit(MSPROF_CTRL_INIT_ACL_ENV,
                                 const_cast<char_t *>(config.c_str()),
                                 static_cast<uint32_t>(config.size()));
                if (ret != MSPROF_ERROR_NONE) {
                    ACL_LOG_CALL_ERROR("[Init][Profiling]handle env config of profiling failed, profiling "
                        "result = %d", ret);
                    return ACL_ERROR_INVALID_PARAM;
                }
            }
        }
        ACL_LOG_INFO("set profiling config successfully");
        return ACL_SUCCESS;
    }

    bool AclProfiling::GetProfilingConfigFile(std::string &fileName)
    {
        const char_t *environment = nullptr;
        MM_SYS_GET_ENV(MM_ENV_PROFILER_SAMPLECONFIG, environment);
        if (environment != nullptr) {
            ACL_LOG_INFO("get profiling config envValue[%s]", environment);
            fileName = environment;
            return true;
        }
        ACL_LOG_INFO("no profiling config file.");
        return false;
    }

    aclError AclProfiling::HandleProfilingConfig(const char_t *const configPath)
    {
        ACL_LOG_INFO("start to execute HandleProfilingConfig");
        std::string strConfig;
        bool configFileFlag = true; // flag of using config flie mode or not
        bool noValidConfig = false;
        aclError ret = ACL_SUCCESS;
        std::string envValue;
        // use profiling config from env variable if exist
        if ((GetProfilingConfigFile(envValue)) && (!envValue.empty())) {
            configFileFlag = false;
            ACL_LOG_INFO("start to use profiling config by env mode");
        }

        // use profiling config from acl.json if exist
        if (configFileFlag) {
            if ((configPath == nullptr) || (strnlen(configPath, 255U) == 0U)) {
                ACL_LOG_INFO("configPath is null, no need to do profiling.");
                noValidConfig = true;
                ret = HandleProfilingCommand(strConfig, configFileFlag, noValidConfig);
                if (ret != ACL_SUCCESS) {
                    ACL_LOG_INNER_ERROR("[Handle][Command]handle profiling command failed, errorCode = %d", ret);
                }
                return ret;
            }
            bool found = false;
            ret = acl::JsonParser::GetJsonCtxByKey(configPath, strConfig, ACL_PROF_CONFIG_NAME, found);
            if ((ret != ACL_SUCCESS) || (!found)) {
                ACL_LOG_INFO("can not parse profiling config from file[%s], errorCode = %d", configPath, ret);
                noValidConfig = true;
            }

            if (strConfig.empty()) {
                ACL_LOG_INFO("profiling config file[%s] is empty", configPath);
                noValidConfig = true;
            }

            ACL_LOG_INFO("start to use profiling config by config file mode");
        }

        ACL_LOG_INFO("ParseJsonFromFile ok in HandleProfilingConfig");
        ret = HandleProfilingCommand(strConfig, configFileFlag, noValidConfig);
        if (ret != ACL_SUCCESS) {
            ACL_LOG_INNER_ERROR("[Handle][Command]handle profiling command failed, errorCode = %d", ret);
            return ret;
        }

        ACL_LOG_INFO("set HandleProfilingConfig success");
        return ret;
    }
    aclError AclProfCtrlHandle(uint32_t dataType, void *data, uint32_t dataLen)
    {
        ACL_REQUIRES_NOT_NULL(data);

        if (dataType == RT_PROF_CTRL_SWITCH) {
            const aclError ret = ProcessProfData(data, dataLen);
            if (ret != ACL_SUCCESS) {
                ACL_LOG_INNER_ERROR("[Process][ProfSwitch]failed to call ProcessProfData, result is %u", ret);
                return ret;
            }
        } else {
            ACL_LOG_INFO("get unsupported dataType %u while processing profiling data", dataType);
        }
        return ACL_SUCCESS;
    }
}