/* -------------------------------------------------------------------------
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This file is part of the MindStudio project.
 *
 * 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 "analysis/csrc/application/timeline/step_trace_assembler.h"

namespace Analysis {
namespace Application {
using namespace Analysis::Utils;
namespace {
const int SORT_INDEX_OFFSET = 70000;
const std::string ITER_CAT = "Iteration Time";
const std::string DEFAULT_ARG_NA = "N/A";
const std::string FP_BP_CAT = "FP_BP Time";
const std::string REFRESH_CAT = "Iteration Refresh";
const std::string DATA_AUG = "Data_aug Bound";
const std::string REDUCE_CAT = "Reduce";
const std::string GET_NEXT_CAT = "GetNext Time";
}

void StepTraceEvent::ProcessArgs(Analysis::Infra::JsonWriter& ostream)
{
    ostream["Iteration ID"] << indexId_;
    ostream["FP Start"] << fpStart_;
    ostream["BP End"] << bpEnd_;
    ostream["Iteration End"] << iterEnd_;
    ostream["Iteration Time(us)"] << iterTime_;
}

void FpBpTraceEvent::ProcessArgs(Analysis::Infra::JsonWriter& ostream)
{
    ostream["Iteration ID"] << indexId_;
    ostream["FP Start"] << fpStart_;
    ostream["BP End"] << bpEnd_;
    ostream["FP_BP Time(us)"] << fpBpTime_;
}

void RefreshTraceEvent::ProcessArgs(Analysis::Infra::JsonWriter& ostream)
{
    ostream["Iteration ID"] << indexId_;
    ostream["BP End"] << bpEnd_;
    ostream["Iteration End"] << iterEnd_;
    ostream["Iteration Refresh(us)"] << refresh_;
}

void DataAug1TraceEvent::ToJson(Analysis::Infra::JsonWriter& ostream)
{
    TraceEvent::ToJson(ostream);
    ostream["ph"] << ph_;
    ostream["cat"] << cat_;
    ostream["id"] << id_;
    ostream["ts"] << ts_;
    ostream["args"];
    ostream.StartObject();
    ostream["Data_aug Bound(us)"] << arg_;
    ostream.EndObject();
}

void DataAug0TraceEvent::ToJson(Analysis::Infra::JsonWriter& ostream)
{
    TraceEvent::ToJson(ostream);
    ostream["ph"] << ph_;
    ostream["cat"] << cat_;
    ostream["id"] << id_;
    ostream["ts"] << ts_;
    ostream["args"];
    ostream.StartObject();
    ostream["Iteration ID"] << arg_;
    ostream.EndObject();
}

void ReduceTraceEvent::ProcessArgs(JsonWriter& ostream)
{
    ostream["Iteration ID"] << indexId_;
    for (const auto &it : args_) {
        ostream[it.first.c_str()] << it.second;
    }
}

void NextTraceEvent::ProcessArgs(Analysis::Infra::JsonWriter& ostream)
{
    ostream["GetNext Start"] << start_;
    ostream["GetNext End"] << end_;
    ostream["GetNext Time(us)"] << time_;
}

StepTraceAssembler::StepTraceAssembler()
    : JsonAssembler(PROCESS_STEP_TRACE, {
    {MSPROF_JSON_FILE, FileCategory::MSPROF}, {STEP_TRACE_FILE, FileCategory::STEP}})
{}

void StepTraceAssembler::GenerateTrainTrace(const std::vector<TrainTraceData>& trainData, const std::string& profPath,
                                            std::unordered_map<uint16_t, uint32_t>& pidMap, const LayerInfo &layer)
{
    uint32_t formatPid;
    std::string traceName;
    std::string fpStart;
    std::string bpEnd;
    for (const auto &data : trainData) {
        fpStart = data.fpStart == 0 ? DEFAULT_ARG_NA : DivideByPowersOfTenWithPrecision(data.fpStart);
        bpEnd = data.bpEnd == 0 ? DEFAULT_ARG_NA : DivideByPowersOfTenWithPrecision(data.bpEnd);
        traceName = "Iteration " + std::to_string(data.indexId);
        formatPid = GetDevicePid(pidMap, data.deviceId, profPath, layer.sortIndex);
        int tid = static_cast<int>(data.modelId) + SORT_INDEX_OFFSET;
        pidTidSet_.insert({formatPid, tid});
        std::shared_ptr<StepTraceEvent> event;
        MAKE_SHARED_RETURN_VOID(event, StepTraceEvent, formatPid, tid, data.iterTime / NS_TO_US,
                                DivideByPowersOfTenWithPrecision(data.timestamp), traceName, ITER_CAT,
                                data.indexId, data.iterTime / NS_TO_US, fpStart, bpEnd,
                                DivideByPowersOfTenWithPrecision(data.iterEnd));
        res_.push_back(event);
        if (data.fpStart != 0 && data.bpEnd != 0) {
            GenerateFpBpTrace(data, formatPid, tid);
            GenerateRefreshTrace(data, formatPid, tid);
            GenerateDataAugTrace(data, formatPid, tid);
        }
    }
}

void StepTraceAssembler::GenerateFpBpTrace(const Analysis::Domain::TrainTraceData& data, uint32_t pid, int tid)
{
    std::shared_ptr<FpBpTraceEvent> fpEvent;
    std::string name = "FP_BP Time " + std::to_string(data.indexId);
    MAKE_SHARED_RETURN_VOID(fpEvent, FpBpTraceEvent, pid, tid, data.fpBpTime / NS_TO_US,
                            DivideByPowersOfTenWithPrecision(data.fpStart), name, FP_BP_CAT, data.indexId,
                            data.fpBpTime / NS_TO_US, DivideByPowersOfTenWithPrecision(data.fpStart),
                            DivideByPowersOfTenWithPrecision(data.bpEnd));
    res_.push_back(fpEvent);
}

void StepTraceAssembler::GenerateRefreshTrace(const Analysis::Domain::TrainTraceData& data, uint32_t pid, int tid)
{
    std::shared_ptr<RefreshTraceEvent> refreshEvent;
    std::string name = "Iteration Refresh " + std::to_string(data.indexId);
    MAKE_SHARED_RETURN_VOID(refreshEvent, RefreshTraceEvent, pid, tid, (data.gradRefreshBound) / NS_TO_US,
                            DivideByPowersOfTenWithPrecision(data.bpEnd), name, REFRESH_CAT, data.indexId,
                            data.gradRefreshBound / NS_TO_US, DivideByPowersOfTenWithPrecision(data.bpEnd),
                            DivideByPowersOfTenWithPrecision(data.iterEnd));
    res_.push_back(refreshEvent);
}

void StepTraceAssembler::GenerateDataAugTrace(const Analysis::Domain::TrainTraceData& data, uint32_t pid, int tid)
{
    std::string id = "Data_aug Bound ";
    id.append(std::to_string(pid)).append("_").append(std::to_string(tid));
    std::string name = "Data_aug Bound " + std::to_string(data.indexId - 1);
    std::shared_ptr<DataAug0TraceEvent> aug0Event;
    MAKE_SHARED_RETURN_VOID(aug0Event, DataAug0TraceEvent, pid, tid, DivideByPowersOfTenWithPrecision(data.fpStart),
                            name, DATA_AUG, id, data.indexId);
    res_.push_back(aug0Event);
    uint64_t ts = data.bpEnd + data.dataAugBound / 2;
    std::shared_ptr<DataAug1TraceEvent> aug1Event;
    name = "Data_aug Bound " + std::to_string(data.indexId);
    MAKE_SHARED_RETURN_VOID(aug1Event, DataAug1TraceEvent, pid, tid, DivideByPowersOfTenWithPrecision(ts),
                            name, DATA_AUG, id, data.dataAugBound / NS_TO_US);
    res_.push_back(aug1Event);
}

void StepTraceAssembler::GenerateMetaData(std::unordered_map<uint16_t, uint32_t>& pidMap, const LayerInfo &layer)
{
    for (const auto &it : pidMap) {
        std::shared_ptr<MetaDataNameEvent> processName;
        MAKE_SHARED_RETURN_VOID(processName, MetaDataNameEvent, it.second, DEFAULT_TID, META_DATA_PROCESS_NAME,
                                layer.component);
        res_.push_back(processName);
        std::shared_ptr<MetaDataLabelEvent> processLabel;
        MAKE_SHARED_RETURN_VOID(processLabel, MetaDataLabelEvent, it.second, DEFAULT_TID, META_DATA_PROCESS_LABEL,
                                GetLayerInfoLabelWithDeviceId(layer.label, it.second));
        res_.push_back(processLabel);
        std::shared_ptr<MetaDataIndexEvent> processIndex;
        MAKE_SHARED_RETURN_VOID(processIndex, MetaDataIndexEvent, it.second, DEFAULT_TID, META_DATA_PROCESS_INDEX,
                                layer.sortIndex);
        res_.push_back(processIndex);
    }
    for (const auto &it : pidTidSet_) {
        std::string argName = std::string("Step Trace(Model ID:")
                .append(std::to_string(static_cast<uint32_t>(it.second - SORT_INDEX_OFFSET))).append(")");
        std::shared_ptr<MetaDataNameEvent> threadName;
        MAKE_SHARED_RETURN_VOID(threadName, MetaDataNameEvent, it.first, it.second, META_DATA_THREAD_NAME, argName);
        res_.push_back(threadName);
        std::shared_ptr<MetaDataIndexEvent> threadIndex;
        MAKE_SHARED_RETURN_VOID(threadIndex, MetaDataIndexEvent, it.first, it.second, META_DATA_THREAD_INDEX,
                                it.second);
        res_.push_back(threadIndex);
    }
}

void StepTraceAssembler::GenerateReduceTrace(const std::vector<AllReduceData>& reduceData, const std::string& profPath,
                                             std::unordered_map<uint16_t, uint32_t>& pidMap,
                                             const Analysis::Domain::LayerInfo& layer)
{
    std::string traceName;
    uint32_t formatPid;
    std::size_t count = 0;
    std::string start;
    std::string end;
    for (const auto &data : reduceData) {
        traceName = REDUCE_CAT;
        traceName.append("_").append(std::to_string(data.indexId)).append("_").append(std::to_string(count));
        std::shared_ptr<ReduceTraceEvent> reduceEvent;
        std::unordered_map<std::string, std::string> args;
        start = "Reduce Start " + std::to_string(count);
        end = "Reduce End " + std::to_string(count);
        args.emplace(start, std::to_string(data.timestamp));
        args.emplace(end, std::to_string(data.end));
        formatPid = GetDevicePid(pidMap, data.deviceId, profPath, layer.sortIndex);
        int tid = static_cast<int>(data.modelId) + SORT_INDEX_OFFSET;
        MAKE_SHARED_RETURN_VOID(reduceEvent, ReduceTraceEvent, formatPid, tid, (data.end - data.timestamp) / NS_TO_US,
                                DivideByPowersOfTenWithPrecision(data.timestamp), traceName, REDUCE_CAT, data.indexId,
                                args);
        res_.push_back(reduceEvent);
        ++count;
    }
}

void StepTraceAssembler::GenerateNextTrace(const std::vector<GetNextData>& nextData, const std::string& profPath,
                                           std::unordered_map<uint16_t, uint32_t>& pidMap,
                                           const Analysis::Domain::LayerInfo& layer)
{
    std::string traceName = "GetNext";
    uint32_t formatPid;
    for (const auto &data : nextData) {
        if (data.timestamp == 0 || data.end == 0) {
            continue;
        }
        std::shared_ptr<NextTraceEvent> event;
        formatPid = GetDevicePid(pidMap, data.deviceId, profPath, layer.sortIndex);
        int tid = static_cast<int>(data.modelId) + SORT_INDEX_OFFSET;
        MAKE_SHARED_RETURN_VOID(event, NextTraceEvent, formatPid, tid, (data.end - data.timestamp) / NS_TO_US,
                                DivideByPowersOfTenWithPrecision(data.timestamp), traceName, GET_NEXT_CAT,
                                DivideByPowersOfTenWithPrecision(data.timestamp),
                                DivideByPowersOfTenWithPrecision(data.end), (data.end - data.timestamp) / NS_TO_US);
        res_.push_back(event);
    }
}

uint8_t StepTraceAssembler::AssembleData(DataInventory& dataInventory, JsonWriter& ostream,
                                         const std::string& profPath)
{
    auto trainData = dataInventory.GetPtr<std::vector<TrainTraceData>>();
    auto reduceData = dataInventory.GetPtr<std::vector<AllReduceData>>();
    auto nextData = dataInventory.GetPtr<std::vector<GetNextData>>();
    if (trainData == nullptr && reduceData == nullptr && nextData == nullptr) {
        WARN("Can't get step trace data from dataInventory");
        return DATA_NOT_EXIST;
    }
    std::unordered_map<uint16_t, uint32_t> devicePid;
    auto layer = GetLayerInfo(PROCESS_STEP_TRACE);
    if (trainData != nullptr) {
        GenerateTrainTrace(*trainData, profPath, devicePid, layer);
        GenerateMetaData(devicePid, layer);
    }
    if (reduceData != nullptr) {
        GenerateReduceTrace(*reduceData, profPath, devicePid, layer);
    }
    if (nextData != nullptr) {
        GenerateNextTrace(*nextData, profPath, devicePid, layer);
    }
    if (res_.empty()) {
        ERROR("Can't Generate any Ascend process data");
        return ASSEMBLE_FAILED;
    }
    for (const auto &node : res_) {
        node->DumpJson(ostream);
    }
    // 为了让下一个写入的内容形成正确的JSON格式,需要补一个","
    ostream << ",";
    return ASSEMBLE_SUCCESS;
}
}
}