/**
* 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 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.
*/

/*!
 * \file sk_scope_info.h
 * \brief Super Kernel Scope Info - data structures for scope splitting
 */

#ifndef __SK_SCOPE_INFO_H__
#define __SK_SCOPE_INFO_H__

#include <bitset>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "sk_types.h"
#include "sk_node.h"

/*!
 * \struct OriginalScopeInfo
 * \brief Simple structure to store original scope info (for dumping)
 *        Used to preserve scope information before any splitting
 */
struct OriginalScopeInfo {
    uint16_t scopeId;
    std::bitset<MAX_SCOPE_NUM> scopeBitFlags;
    std::vector<uint64_t> nodeIds;

    const std::bitset<MAX_SCOPE_NUM>& GetScopeBitFlags() const { return scopeBitFlags; }
};

/*!
 * \struct ScopeStreamInfo
 * \brief Information about a single stream within a scope
 */
struct ScopeStreamInfo {
    uint32_t streamIdx = 0;              ///< Stream index in the graph
    uint64_t headNodeIdx = INVALID_TASK_ID;  ///< First node of this stream in scope
    uint64_t tailNodeIdx = INVALID_TASK_ID;  ///< Last node of this stream in scope
    uint64_t nodeSize = 0;               ///< Number of nodes from this stream in scope
};

/*!
 * \enum ScopeFusionStatus
 * \brief Fusion status for scope after PostProcess
 */
enum class ScopeFusionStatus : uint8_t {
    FAILED = 0,     // Scope fusion failed (initial state)
    SUCCESS,        // Scope fusion succeeded after PostProcess
};

/*!
 * \brief Convert ScopeFusionStatus to string
 */
inline const char* ScopeFusionStatusToStr(ScopeFusionStatus status) {
    switch (status) {
        case ScopeFusionStatus::FAILED:
            return "FAILED";
        case ScopeFusionStatus::SUCCESS:
            return "SUCCESS";
        default:
            return "UNKNOWN";
    }
}

/*!
 * \enum ScopeFailReason
 * \brief Failure reasons for scope processing (when fusion failed)
 */
enum class ScopeFailReason : uint8_t {
    NONE = 0,
    STREAM_SYNC_FAIL, // Insufficient resources are needed for scope fusion
};

/*!
 * \enum ScopeBreakReason
 * \brief Reasons why a scope boundary was created (scope was split)
 */
enum class ScopeBreakReason : uint8_t {
    NONE,
    UNFUSIBLE_NODE,
    DEADLOCK_DETECTED,
    SCHEMODE_CORE_DROP,
    SCHEMODE_CORE_RISE,
    DEBUG_PER_OP_MAX_CORE,
};

/*!
 * \brief Convert ScopeBreakReason to string
 */
inline const char* ScopeBreakReasonToStr(ScopeBreakReason reason) {
    switch (reason) {
        case ScopeBreakReason::UNFUSIBLE_NODE:
            return "There exists unfusible node in scope";
        case ScopeBreakReason::DEADLOCK_DETECTED:
            return "There exists deadlock in scope";
        case ScopeBreakReason::SCHEMODE_CORE_DROP:
            return "There exists an operator for full kernel synchronization, and the number of kernels of this operator is less than the maximum number of kernels of the fused superkernel";
        case ScopeBreakReason::SCHEMODE_CORE_RISE:
            return "There exists an operator for full kernel synchronization, and the number of kernels of this operator is greater than the maximum number of kernels of the previously fused superkernel";
        case ScopeBreakReason::DEBUG_PER_OP_MAX_CORE:
            return "Per-Op debug mode: each operator is an independent scope";
        default:
            return "UNKNOWN REASON";
    }
}

/*!
 * \class ScopeBreakInfo
 * \brief Information about why a scope boundary was created
 */
class ScopeBreakInfo {
public:
    ScopeBreakInfo() = default;

    // Builder-style setters for convenient construction
    ScopeBreakInfo& SetReason(ScopeBreakReason r) {
        reason = r;
        return *this;
    }

    ScopeBreakInfo& SetTriggerNode(uint64_t nodeId, uint32_t streamIdx = 0) {
        triggerNodeId = nodeId;
        triggerStreamIdx = streamIdx;
        return *this;
    }

    ScopeBreakInfo& SetTriggerStreamIdx(uint32_t streamIdx) {
        triggerStreamIdx = streamIdx;
        return *this;
    }

    ScopeBreakInfo& SetFusionFailReason(const FusionFailReasonInfo& failInfo) {
        fusionFailReason = failInfo;
        return *this;
    }

    ScopeBreakInfo& SetFusionFailReason(FusionFailReason failReason) {
        fusionFailReason = FusionFailReasonInfo(failReason);
        return *this;
    }

    ScopeBreakInfo& SetParentScopeId(uint16_t id) {
        parentScopeId = id;
        return *this;
    }

    ScopeBreakInfo& SetDetail(const std::string& d) {
        detail = d;
        return *this;
    }

    ScopeBreakInfo& SetDetail(std::string&& d) {
        detail = std::move(d);
        return *this;
    }

    // Getters
    ScopeBreakReason GetReason() const { return reason; }
    uint64_t GetTriggerNodeId() const { return triggerNodeId; }
    uint32_t GetTriggerStreamIdx() const { return triggerStreamIdx; }
    const FusionFailReasonInfo& GetFusionFailReason() const { return fusionFailReason; }
    uint16_t GetParentScopeId() const { return parentScopeId; }
    const std::string& GetDetail() const { return detail; }

    std::string Format() const {
        std::string result; 
        if (reason != ScopeBreakReason::NONE) {
            result = "breakReason=" + std::string(ScopeBreakReasonToStr(reason));
        }
        if (triggerNodeId != INVALID_TASK_ID) {
            result += ", triggerNode=" + std::to_string(triggerNodeId);
        }
        if (triggerStreamIdx != 0 || triggerNodeId != INVALID_TASK_ID) {
            result += ", triggerStream=" + std::to_string(triggerStreamIdx);
        }
        if (fusionFailReason != FusionFailReason::CAN_FUSE) {
            result += ", fusionFailReason=" + FusionFailReasonToStr(fusionFailReason);
        }
        if (parentScopeId != INVALID_SCOPE_ID) {
            result += ", parentScope=" + std::to_string(parentScopeId);
        }
        if (!detail.empty()) {
            result += ", detail=\"" + detail + "\"";
        }
        return result;
    }

private:
    ScopeBreakReason reason = ScopeBreakReason::NONE;
    uint64_t triggerNodeId = INVALID_TASK_ID;      // Node that triggered the break
    uint32_t triggerStreamIdx = 0;                 // Stream of trigger node
    FusionFailReasonInfo fusionFailReason;         // Detailed fusion failure reason (if applicable)
    uint16_t parentScopeId = INVALID_SCOPE_ID;     // Parent scope ID (split from, for tracing split chain)
    std::string detail;                            // Human-readable description
};

/*!
 * \class ScopeIdGenerator
 * \brief Singleton class for generating unique scope IDs
 *
 * This generator ensures each scope gets a unique ID at creation time.
 * The ID is not recycled when scope is destroyed, allowing full lifecycle tracking.
 * Uses uint16_t for compact storage; logs INFO when wraparound occurs (every 65536 scopes).
 */
class ScopeIdGenerator {
public:
    static ScopeIdGenerator& Instance() {
        static ScopeIdGenerator instance;
        return instance;
    }

    uint16_t NextId() {
        uint16_t id = nextId_++;
        if (nextId_ == 0) {
            SK_LOGI("ScopeId wraparound detected: scope ID counter reset to 0 after reaching maximum");
        }
        return id;
    }

private:
    ScopeIdGenerator() : nextId_(0) {}
    ScopeIdGenerator(const ScopeIdGenerator&) = delete;
    ScopeIdGenerator& operator=(const ScopeIdGenerator&) = delete;

    uint16_t nextId_;
};

/*!
 * \struct ScopeExtInfo
 * \brief Extended information for scope post-processing and scheduling
 */
struct ScopeExtInfo {
    std::vector<std::vector<aclmdlRITaskParams>> customParamsList; ///< Custom parameters for each stream
    std::vector<SuperKernelBaseNode*> filteredNodes;               ///< Post-processed nodes used for scheduling
    std::vector<std::unique_ptr<SuperKernelBaseNode>> eventNodes;  ///< Synthesized event nodes for stream sync
    uint64_t skMainNodeId = INVALID_TASK_ID;                       ///< Main launch node ID for this scope
    std::string scopeName;
    ScopeFusionStatus fusionStatus = ScopeFusionStatus::FAILED;    ///< Fusion status (initial: FAILED, PostProcess success: SUCCESS)
    ScopeFailReason failReason = ScopeFailReason::NONE;            ///< Failure reason when fusion failed

    ScopeExtInfo() = default;
    ScopeExtInfo(const ScopeExtInfo&) = delete;
    ScopeExtInfo& operator=(const ScopeExtInfo&) = delete;
    ScopeExtInfo(ScopeExtInfo&&) = default;
    ScopeExtInfo& operator=(ScopeExtInfo&&) = default;
};

/*!
 * \brief Convert ScopeFailReason to string
 */
inline const char* ScopeFailReasonToStr(ScopeFailReason reason)
{
    switch (reason) {
        case ScopeFailReason::STREAM_SYNC_FAIL:
            return "Insufficient resources are needed for scope fusion";
        default:
            return "UNKNOWN REASON";
    }
}

/*!
 * \class SuperKernelScopeInfo
 * \brief Information about a single scope in Super Kernel fusion
 *
 * A scope represents a collection of nodes from potentially multiple streams
 * that can be fused into a single Super Kernel.
 */
class SuperKernelScopeInfo {
public:
    SuperKernelScopeInfo() : scopeId_(ScopeIdGenerator::Instance().NextId()) {}
    ~SuperKernelScopeInfo() = default;
    SuperKernelScopeInfo(const SuperKernelScopeInfo&) = delete;
    SuperKernelScopeInfo& operator=(const SuperKernelScopeInfo&) = delete;
    SuperKernelScopeInfo(SuperKernelScopeInfo&& other) noexcept
        : scopeId_(other.scopeId_),
          scopeStreamInfos_(std::move(other.scopeStreamInfos_)),
          nodes_(std::move(other.nodes_)),
          scopeBitFlags_(other.scopeBitFlags_),
          breakInfo_(std::move(other.breakInfo_)),
          extInfo_(std::move(other.extInfo_)) {}
    SuperKernelScopeInfo& operator=(SuperKernelScopeInfo&& other) noexcept {
        if (this != &other) {
            scopeId_ = other.scopeId_;
            scopeStreamInfos_ = std::move(other.scopeStreamInfos_);
            nodes_ = std::move(other.nodes_);
            scopeBitFlags_ = other.scopeBitFlags_;
            breakInfo_ = std::move(other.breakInfo_);
            extInfo_ = std::move(other.extInfo_);
        }
        return *this;
    }

    uint16_t GetScopeId() const { return scopeId_; }

    // ============ ScopeStreamInfos ============
    const std::vector<ScopeStreamInfo>& GetScopeStreamInfos() const { return scopeStreamInfos_; }
    void SetScopeStreamInfos(std::vector<ScopeStreamInfo> infos) { scopeStreamInfos_ = std::move(infos); }
    void AddScopeStreamInfo(const ScopeStreamInfo& info) { scopeStreamInfos_.push_back(info); }

    // ============ Nodes ============
    const std::vector<SuperKernelBaseNode*>& GetNodes() const { return nodes_; }
    void SetNodes(std::vector<SuperKernelBaseNode*> nodeList) { nodes_ = std::move(nodeList); }
    void AddNode(SuperKernelBaseNode* node) { nodes_.push_back(node); }

    // ============ ScopeBitFlags ============
    const std::bitset<MAX_SCOPE_NUM>& GetScopeBitFlags() const { return scopeBitFlags_; }
    void SetScopeBitFlags(const std::bitset<MAX_SCOPE_NUM>& flags) { scopeBitFlags_ = flags; }

    // ============ BreakInfo ============
    const ScopeBreakInfo& GetBreakInfo() const { return breakInfo_; }
    ScopeBreakInfo& MutableBreakInfo() { return breakInfo_; }
    void SetBreakInfo(const ScopeBreakInfo& info) { breakInfo_ = info; }
    void SetBreakInfo(ScopeBreakInfo&& info) { breakInfo_ = std::move(info); }

    // Convenience method to set break info with builder pattern
    void SetBreakReason(ScopeBreakReason reason, uint64_t triggerNodeId = INVALID_TASK_ID,
                         uint32_t triggerStreamIdx = 0) {
        breakInfo_.SetReason(reason)
                  .SetTriggerNode(triggerNodeId, triggerStreamIdx);
    }

    // ============ ExtInfo ============
    const ScopeExtInfo& GetExtInfo() const { return extInfo_; }
    void SetExtInfo(ScopeExtInfo&& extInfo) { extInfo_ = std::move(extInfo); }
    ScopeExtInfo& MutableExtInfo() { return extInfo_; }

private:
    uint16_t scopeId_;                              ///< Unique scope ID assigned at creation
    std::vector<ScopeStreamInfo> scopeStreamInfos_; ///< Per-stream information
    std::vector<SuperKernelBaseNode*> nodes_;       ///< All nodes in this scope
    std::bitset<MAX_SCOPE_NUM> scopeBitFlags_;      ///< Scope bit flags
    ScopeBreakInfo breakInfo_;                      ///< Break reason for this scope boundary
    ScopeExtInfo extInfo_;                          ///< Extended info for post-processing and scheduling
};

#endif // __SK_SCOPE_INFO_H__