/* -------------------------------------------------------------------------
 * 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 "driver_prof_api.h"
#include "securec.h"
#include "event_report.h"
#include "runtime_prof_api.h"
#include "kernel_event_trace.h"
#include "ascend_hal.h"

namespace MemScope {

static tagDrvError HalGetDeviceInfo(uint32_t deviceId, int32_t moduleType, int32_t infoType, int64_t* value)
{
    using HalGetDeviceInfoFunc = tagDrvError(*)(uint32_t, int32_t, int32_t, int64_t*);
    static auto vallina = reinterpret_cast<HalGetDeviceInfoFunc>(GetSymbol("halGetDeviceInfo"));
    if (vallina == nullptr) {
        LOG_ERROR("halGetDeviceInfo api get failed");
        return DRV_ERROR_NOT_SUPPORT;
    }

    return vallina(deviceId, moduleType, infoType, value);
}

static int64_t GetDrvVersion(uint32_t deviceId)
{
    static int64_t errorVersion = -1;
    int64_t version = 0;
    tagDrvError ret = HalGetDeviceInfo(deviceId, DRV_MODULE_TYPE_SYSTEM, DRV_INFO_TYPE_VERSION, &version);
    return (ret == DRV_ERROR_NONE) ? version : errorVersion;
}

static PlatformType GetChipTypeImpl(uint32_t deviceId)
{
    int64_t versionInfo = GetDrvVersion(deviceId);
    if (versionInfo < 0) {
        LOG_ERROR("Call GetDrvVersion failed");
        return PlatformType::END_TYPE;
    }
    uint32_t chipId = ((static_cast<uint64_t>(versionInfo) >> 8) & 0xff);
    if (chipId >= static_cast<uint32_t>(PlatformType::END_TYPE)) {
        LOG_ERROR("Get Chip Type failed");
        return PlatformType::END_TYPE;
    }
    return static_cast<PlatformType>(chipId);
}

static uint64_t GetDevFreq(uint32_t device)
{
    static uint64_t freqDefault = 50;
    static const std::unordered_map<PlatformType, uint64_t> FREQ_MAP = {
        {PlatformType::CHIP_910B, 50},
        {PlatformType::CHIP_310B, 50},
    };
    int64_t freq = 0;
    tagDrvError ret = HalGetDeviceInfo(device, DRV_MODULE_TYPE_SYSTEM, DRV_INFO_TYPE_DEV_OSC_FREQUE, &freq);
    if (ret != DRV_ERROR_NONE) {
        auto platform = GetChipTypeImpl(device);
        auto iter = FREQ_MAP.find(platform);
        uint64_t defaultFreq = (iter == FREQ_MAP.end()) ? freqDefault : iter->second;
        return defaultFreq;
    }
    return freq;
}

static uint64_t GetClockRealTimeNs()
{
    struct timespec ts;
    if (memset_s(&ts, sizeof(timespec), 0, sizeof(timespec)) != EOK) {
        return 0;
    }
    clock_gettime(CLOCK_REALTIME, &ts);
    return static_cast<uint64_t>(ts.tv_sec) * SECTONSEC + static_cast<uint64_t>(ts.tv_nsec);
}

static uint64_t GetDevStartSysCnt(uint32_t device)
{
    constexpr uint64_t errorSystemCnt = 0;
    int64_t syscnt = 0;
    tagDrvError ret = HalGetDeviceInfo(device, DRV_MODULE_TYPE_SYSTEM, DRV_INFO_TYPE_SYS_COUNT, &syscnt);
    return (ret == DRV_ERROR_NONE) ? static_cast<uint64_t>(syscnt) : errorSystemCnt;
}

DevTimeInfo g_devTimeInfo = { };

static void InitDevTimeInfo(uint32_t deviceId)
{
    static constexpr uint32_t aveNum = 2;

    g_devTimeInfo.freq = GetDevFreq(deviceId);
    auto t1 = GetClockRealTimeNs();
    g_devTimeInfo.startSysCnt = GetDevStartSysCnt(deviceId);
    auto t2 = GetClockRealTimeNs();
    g_devTimeInfo.startRealTime = (t2 + t1) / aveNum;
    return;
}

uint64_t GetRealTimeFromSysCnt(uint32_t deviceId, uint64_t sysCnt)
{
    if (g_devTimeInfo.freq == 0) {
        LOG_ERROR("g_devTimeInfo.freq is 0, please check!");
        return 0;
    }
    uint64_t realTime = MSTONS * (sysCnt - g_devTimeInfo.startSysCnt) / g_devTimeInfo.freq +
        g_devTimeInfo.startRealTime;
    return realTime;
}

void StartDriverKernelInfoTrace(int32_t devId)
{
    InitDevTimeInfo(devId);
    SetProfCommand(devId);
    StarsSocLogConfigT configP;
    if (memset_s(&configP, sizeof(StarsSocLogConfigT), 0, sizeof(StarsSocLogConfigT)) != EOK) {
        LOG_ERROR("memset StarsSocLogConfigT failed");
        return;
    }
    configP.acsq_task = TS_PROFILE_COMMAND_TYPE_PROFILING_ENABLE;
    configP.ffts_thread_task = TS_PROFILE_COMMAND_TYPE_PROFILING_ENABLE;
    ProfStartParaT profStartPara;
    profStartPara.channelType = PROF_CHANNEL_TYPE_TS;
    static const uint32_t SAMPLE_PERIOD = 20;
    profStartPara.samplePeriod = SAMPLE_PERIOD;
    profStartPara.realTime = 1;
    profStartPara.userData = &configP;
    profStartPara.userDataSize = static_cast<unsigned int>(sizeof(StarsSocLogConfigT));

    using DriverProfStartFunc = int(*)(unsigned int, unsigned int, struct ProfStartPara*);
    static auto vallina = reinterpret_cast<DriverProfStartFunc>(GetSymbol("prof_drv_start"));
    if (vallina == nullptr) {
        LOG_ERROR("DriverProfStartFunc is nullptr");
        return;
    }
    int ret = vallina(static_cast<uint32_t>(devId), PROF_CHANNEL_STARS_SOC_LOG, &profStartPara);
    if (ret != 0) {
        LOG_ERROR("driver prof start failed.");
    }
    RuntimeKernelLinker::GetInstance();
    atexit(EndDriverKernelInfoTrace);
    return;
}

void EndDriverKernelInfoTrace()
{
    int32_t devId = MemScope::GD_INVALID_NUM;
    if (!GetDeviceInfo::Instance().GetDeviceId(devId) || devId == GD_INVALID_NUM) {
        LOG_ERROR("get device id failed");
    }
    using DriverProfEndFunc = int(*)(unsigned int, unsigned int);
    static auto vallina = reinterpret_cast<DriverProfEndFunc>(GetSymbol("prof_stop"));
    if (vallina == nullptr) {
        LOG_ERROR("EndDriverKernelInfoTrace is nullptr");
        return;
    }

    int ret = vallina(static_cast<uint32_t>(devId), PROF_CHANNEL_STARS_SOC_LOG);
    if (ret != 0) {
        LOG_ERROR("driver prof end failed.");
    }
    return;
}
}