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

#ifndef PTA_NPU_OP_API_COMMON_INC_OP_LOG_H_
#define PTA_NPU_OP_API_COMMON_INC_OP_LOG_H_

#include <stdint.h>
#include <string>
#include <type_traits>
#include <unordered_map>
#include "op_errno.h"
#ifdef __GNUC__
#include <sys/syscall.h>
#include <unistd.h>
#else
#include "mmpa/mmpa_api.h"
#endif

#define OPAPI_SUBMOD_NAME "NNOP"
#define OP_ID 63
#define LOG_FMT_IDX 3
#define LOG_AGR_IDX 4

namespace op {
class OpLog {
public:
    static uint64_t GetTid()
    {
#ifdef __GNUC__
        const uint64_t tid = static_cast<uint64_t>(syscall(__NR_gettid));
#else
        const uint64_t tid = static_cast<uint64_t>(GetCurrentThreadId());
#endif
        return tid;
    }
};
} // namespace op

void ReportErrorMessageInner(const std::string& code, const char* fmt, ...);
void DlogRecordInner(int32_t moduleId, int32_t level, const char* fmt, ...);
int32_t CheckLogLevelInner(int32_t moduleId, int32_t level);
const std::unordered_map<char, std::string> ERRNO_PREFIX_TO_ERROR_CODE = {
    {'1', "EZ1001"}, {'3', "EZ9903"}, {'5', "EZ9999"}};

template <typename... Arguments>
void ReportErrorMessage(const char* code, const char* fmt, Arguments&&... args)
{
    std::string errorCode = "EZ9999";
    const auto iter = ERRNO_PREFIX_TO_ERROR_CODE.find(code[0]);
    if (iter != ERRNO_PREFIX_TO_ERROR_CODE.end()) {
        errorCode = iter->second;
    }
    ReportErrorMessageInner(errorCode, fmt, std::forward<Arguments>(args)...);
}

namespace op::internal {
std::string GetLogApiInfo();
}

#define OP_LOG_DEBUG 0
#define OP_LOG_INFO 1
#define OP_LOG_WARN 2
#define OP_LOG_ERROR 3
#define OP_LOG_EVENT 0x10

constexpr const char* GetFileName(const char* path)
{
    const char* file = path;
    while (*path != '\0') {
        if (*path++ == '/') {
            file = path;
        }
    }
    return file;
}

inline std::string GetOpName()
{
    auto opInfo = op::internal::GetLogApiInfo();
    if (!opInfo.empty()) {
        opInfo = "OpName:[" + opInfo + "] ";
    }
    return opInfo;
}
/**
 * @brief DOplogSub: print log, need caller to specify level and submodule
 * call CheckLogLevelInner in advance to optimize performance, call interface with fmt input take time
 *
 * @param [in]moduleId: module id, eg: CCE
 * @param [in]submodule: eg: engine
 * @param [in]level(0: debug, 1: info, 2: warning, 3: error, 5: trace, 6: oplog, 16: event)
 * @param [in]fmt: log content
 */
#define DOplogSub(moduleId, submodule, level, fmt, ...)                                                     \
    do {                                                                                                    \
        if (unlikely(CheckLogLevelInner(moduleId, level) == 1)) {                                           \
            DlogRecordInner(moduleId, level, "[%s:%d][%s]" fmt, GetFileName(__FILE__), __LINE__, submodule, \
                            ##__VA_ARGS__);                                                                 \
        }                                                                                                   \
    } while (false)

#define DDfxlogSub(moduleId, submodule, level, file, line, fmt, ...)                                   \
    do {                                                                                               \
        if (unlikely(CheckLogLevelInner(moduleId, level) == 1)) {                                      \
            DlogRecordInner(moduleId, level, "[%s:%d][%s]" fmt, file, line, submodule, ##__VA_ARGS__); \
        }                                                                                              \
    } while (false)

#if defined(NNOPBASE_UT) || defined(NNOPBASE_ST)
#define OP_TEST_LOG(fmt, ...)                                                                                \
    do {                                                                                                     \
        fprintf(stdout, "[OP_TEST] [tid: %lu][%s:%d] %s:" fmt "\n", op::OpLog::GetTid(), __FILE__, __LINE__, \
                __FUNCTION__, ##__VA_ARGS__);                                                                \
        fflush(stdout);                                                                                      \
    } while (0)
#define OP_LOGI(...) OP_TEST_LOG(__VA_ARGS__)
#define OP_LOGW(...) OP_TEST_LOG(__VA_ARGS__)
#define OP_LOGE(errno, ...) OP_TEST_LOG(__VA_ARGS__)
#define OP_LOGE_WITHOUT_REPORT(errno, ...) OP_TEST_LOG(__VA_ARGS__)
#define OP_LOGD(...) OP_TEST_LOG(__VA_ARGS__)
#define OP_EVENT(...) OP_TEST_LOG(__VA_ARGS__)
#define OP_DFX_LOGD(file, line, func, ...)
#define OP_DFX_LOGI(file, line, func, ...)
#define OP_DFX_LOGW(file, line, func, ...)
#define OP_DFX_LOGE(file, line, func, ...)
#elif !defined(__ANDROID__) && !defined(ANDROID)
#define OP_LOGI(...) D_OP_LOGI(GetOpName().c_str(), __VA_ARGS__)
#define OP_LOGW(...) D_OP_LOGW(GetOpName().c_str(), __VA_ARGS__)

#define OP_DFX_LOGD(file, line, func, ...) D_OP_DFX_LOGD(GetOpName().c_str(), file, line, func, __VA_ARGS__)
#define OP_DFX_LOGI(file, line, func, ...) D_OP_DFX_LOGI(GetOpName().c_str(), file, line, func, __VA_ARGS__)
#define OP_DFX_LOGW(file, line, func, ...) D_OP_DFX_LOGW(GetOpName().c_str(), file, line, func, __VA_ARGS__)
#define OP_DFX_LOGE(file, line, func, ...) D_OP_DFX_LOGW(GetOpName().c_str(), file, line, func, __VA_ARGS__)
#define REPORT_ERROR_MESSAGE(code, ...)                                \
    do {                                                               \
        ReportErrorMessage(std::to_string(code).c_str(), __VA_ARGS__); \
    } while (0)
#define OP_LOGE_WITHOUT_REPORT(errno, ...) D_OP_LOGE(GetOpName().c_str(), errno, __VA_ARGS__)
#define OP_LOGE(errno, ...)                         \
    do {                                            \
        OP_LOGE_WITHOUT_REPORT(errno, __VA_ARGS__); \
        REPORT_ERROR_MESSAGE(errno, __VA_ARGS__);   \
    } while (false)

#define OP_LOGD(...) D_OP_LOGD(GetOpName().c_str(), __VA_ARGS__)
#define OP_EVENT(...) D_OP_EVENT(GetOpName().c_str(), __VA_ARGS__)
#else
#define OP_LOGI(...)
#define OP_LOGW(...)
#define OP_LOGE_WITHOUT_REPORT(...)
#define OP_LOGE(...)
#define OP_LOGD(...)
#define OP_EVENT(...)
#endif

#define OpLogSub(moduleId, level, op_info, fmt, ...)                                                      \
    DOplogSub(static_cast<int32_t>(moduleId), OPAPI_SUBMOD_NAME, level, "[%s][%lu] %s" fmt, __FUNCTION__, \
              op::OpLog::GetTid(), op_info, ##__VA_ARGS__)

#define OpLogErrSub(moduleId, level, op_info, errno, fmt, ...)                                                      \
    DOplogSub(static_cast<int32_t>(moduleId), OPAPI_SUBMOD_NAME, level, "[%s][%lu] errno[%d] %s" fmt, __FUNCTION__, \
              op::OpLog::GetTid(), errno, op_info, ##__VA_ARGS__)

#define OpDfxLogSub(moduleId, level, file, line, func, op_info, fmt, ...)                                             \
    DDfxlogSub(static_cast<int32_t>(moduleId), OPAPI_SUBMOD_NAME, level, GetFileName(file), line, "[%s][%lu] %s" fmt, \
               func, op::OpLog::GetTid(), op_info, ##__VA_ARGS__)

#if !defined(__ANDROID__) && !defined(ANDROID)
#define D_OP_LOGI(opname, fmt, ...) OpLogSub(OP_ID, OP_LOG_INFO, opname, fmt, ##__VA_ARGS__)
#define D_OP_LOGW(opname, fmt, ...) OpLogSub(OP_ID, OP_LOG_WARN, opname, fmt, ##__VA_ARGS__)
#define D_OP_LOGE(opname, errno, fmt, ...) OpLogErrSub(OP_ID, OP_LOG_ERROR, opname, errno, fmt, ##__VA_ARGS__)
#define D_OP_LOGD(opname, fmt, ...) OpLogSub(OP_ID, OP_LOG_DEBUG, opname, fmt, ##__VA_ARGS__)
#define D_OP_EVENT(opname, fmt, ...) OpLogSub(OP_ID, OP_LOG_EVENT, opname, fmt, ##__VA_ARGS__)

#define D_OP_DFX_LOGI(opname, file, line, func, fmt, ...) \
    OpDfxLogSub(OP_ID, OP_LOG_INFO, file, line, func, opname, fmt, ##__VA_ARGS__)
#define D_OP_DFX_LOGW(opname, file, line, func, fmt, ...) \
    OpDfxLogSub(OP_ID, OP_LOG_WARN, file, line, func, opname, fmt, ##__VA_ARGS__)
#define D_OP_DFX_LOGE(opname, file, line, func, fmt, ...) \
    OpDfxLogSub(OP_ID, OP_LOG_ERROR, file, line, func, opname, fmt, ##__VA_ARGS__)
#define D_OP_DFX_LOGD(opname, file, line, func, fmt, ...) \
    OpDfxLogSub(OP_ID, OP_LOG_DEBUG, file, line, func, opname, fmt, ##__VA_ARGS__)

#else
#define D_OP_LOGI(opname, fmt, ...)
#define D_OP_LOGW(opname, fmt, ...)
#define D_OP_LOGE(opname, fmt, ...)
#define D_OP_LOGD(opname, fmt, ...)
#define D_OP_EVENT(opname, fmt, ...)
#endif

#define unlikely(x) __builtin_expect((x), 0)
#define likely(x) __builtin_expect((x), 1)

#define OP_CHECK_NOTNULL(val)                                                          \
    do {                                                                               \
        if (unlikely((val) == nullptr)) {                                              \
            OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "Parameter[%s] must not be null.", #val); \
            return ACLNN_ERR_INNER_NULLPTR;                                            \
        }                                                                              \
    } while (false)

#define RET_IF_NOT_SUCCESS(x)
#define CHECK_RET(condition, ret_value)                                              \
    do {                                                                             \
        if (unlikely(!(condition))) {                                                \
            OP_LOGE_WITHOUT_REPORT(ACLNN_ERR_INNER, "check %s failed.", #condition); \
            return ret_value;                                                        \
        }                                                                            \
    } while (false)
#define CHECK_RET_CODE(func, fmt, ...)         \
    do {                                       \
        aclnnStatus __rc = func;               \
        if (unlikely(__rc != ACLNN_SUCCESS)) { \
            OP_LOGE(__rc, fmt, ##__VA_ARGS__); \
            return __rc;                       \
        }                                      \
    } while (false)

#define OP_CHECK(cond, log_func, return_expr) \
    do {                                      \
        if (unlikely(!(cond))) {              \
            log_func;                         \
            return_expr;                      \
        }                                     \
    } while (false)

#define OP_CHECK_NO_RETURN(cond, log_func) \
    do {                                   \
        if (unlikely(!(cond))) {           \
            log_func;                      \
        }                                  \
    } while (false)

#define CHECK_COND(cond, ret, fmt, ...)       \
    do {                                      \
        if (unlikely(!(cond))) {              \
            OP_LOGE(ret, fmt, ##__VA_ARGS__); \
            return ret;                       \
        }                                     \
    } while (false)

#endif // PTA_NPU_OP_API_COMMON_INC_OP_LOG_H_