/*
 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi_hitrace_util.h"

#include "hilog/log.h"

namespace OHOS {
namespace HiviewDFX {
namespace {
#undef LOG_DOMAIN
#define LOG_DOMAIN 0xD002D33
#undef LOG_TAG
#define LOG_TAG "HITRACE_UTIL_NAPI"

constexpr uint32_t UINT32_T_PRO_DEFAULT_VALUE = 0;
constexpr uint64_t UINT64_T_PRO_DEFAULT_VALUE = 0;
constexpr uint64_t INVALID_CHAIN_ID = 0;
constexpr char CHAIN_ID_ATTR[] = "chainId";
constexpr char SPAN_ID_ATTR[] = "spanId";
constexpr char PARENT_SPAN_ID_ATTR[] = "parentSpanId";
constexpr char FLAGS_ATTR[] = "flags";

napi_value CreateInt32Value(const napi_env env, int32_t value)
{
    napi_value result = nullptr;
    NAPI_CALL(env, napi_create_int32(env, value, &result));
    return result;
}

napi_value CreateInt64Value(const napi_env env, int64_t value)
{
    napi_value result = nullptr;
    NAPI_CALL(env, napi_create_int64(env, value, &result));
    return result;
}

napi_value CreateBigInt64Value(const napi_env env, uint64_t value)
{
    napi_value result = nullptr;
    NAPI_CALL(env, napi_create_bigint_uint64(env, value, &result));
    return result;
}

napi_status SetNamedProperty(const napi_env env, napi_value& object,
    const std::string& propertyName, napi_value& propertyValue)
{
    napi_status status = napi_set_named_property(env, object, propertyName.c_str(), propertyValue);
    if (status != napi_ok) {
        HILOG_ERROR(LOG_CORE, "set property %{public}s failed.", propertyName.c_str());
    }
    return status;
}

napi_value GetPropertyByName(const napi_env env, const napi_value& object,
    const std::string& propertyName)
{
    napi_value result = nullptr;
    NAPI_CALL(env, napi_get_named_property(env, object, propertyName.c_str(), &result));
    return result;
}
}

bool NapiHitraceUtil::CheckValueTypeValidity(const napi_env env, const napi_value& jsObj,
    const napi_valuetype typeName)
{
    napi_valuetype valueType = napi_undefined;
    napi_status ret = napi_typeof(env, jsObj, &valueType);
    if (ret != napi_ok) {
        HILOG_ERROR(LOG_CORE, "failed to parse the type of napi value.");
        return false;
    }
    if (valueType != typeName) {
        HILOG_ERROR(LOG_CORE, "you have called a function with parameters of wrong type.");
        return false;
    }
    return true;
}

void NapiHitraceUtil::CreateHiTraceIdJsObject(const napi_env env, HiTraceId& traceId,
    napi_value& valueObject)
{
    napi_create_object(env, &valueObject);
    NapiHitraceUtil::SetPropertyBigInt64(env, valueObject, CHAIN_ID_ATTR,
        traceId.GetChainId());
    HILOG_DEBUG(LOG_CORE, "Native2Js: chainId is %{public}llx.",
        static_cast<unsigned long long>(traceId.GetChainId()));
    NapiHitraceUtil::SetPropertyInt64(env, valueObject, SPAN_ID_ATTR, traceId.GetSpanId());
    HILOG_DEBUG(LOG_CORE, "Native2Js: spanId is %{public}llx.",
        static_cast<unsigned long long>(traceId.GetSpanId()));
    NapiHitraceUtil::SetPropertyInt64(env, valueObject, PARENT_SPAN_ID_ATTR,
        traceId.GetParentSpanId());
    HILOG_DEBUG(LOG_CORE, "Native2Js: parentSpanId is %{public}llx.",
        static_cast<unsigned long long>(traceId.GetParentSpanId()));
    NapiHitraceUtil::SetPropertyInt32(env, valueObject, FLAGS_ATTR,
        traceId.GetFlags());
    HILOG_DEBUG(LOG_CORE, "Native2Js: flags is %{public}d.", traceId.GetFlags());
}

void NapiHitraceUtil::TransHiTraceIdJsObjectToNative(const napi_env env, HiTraceId& traceId,
    const napi_value& valueObject)
{
    uint64_t chainId = NapiHitraceUtil::GetPropertyBigInt64(env, valueObject, CHAIN_ID_ATTR);
    HILOG_DEBUG(LOG_CORE, "Js2Native: chainId is %{public}llx.",
        static_cast<unsigned long long>(chainId));
    if (chainId == INVALID_CHAIN_ID) {
        return;
    }
    traceId.SetChainId(chainId);
    uint64_t spanId = NapiHitraceUtil::GetPropertyInt64(env, valueObject, SPAN_ID_ATTR);
    HILOG_DEBUG(LOG_CORE, "Js2Native: spanId is %{public}llx.",
        static_cast<unsigned long long>(spanId));
    traceId.SetSpanId(spanId);
    uint64_t parentSpanId = NapiHitraceUtil::GetPropertyInt64(env, valueObject,
        PARENT_SPAN_ID_ATTR);
    HILOG_DEBUG(LOG_CORE, "Js2Native: parentSpanId is %{public}llx.",
        static_cast<unsigned long long>(parentSpanId));
    traceId.SetParentSpanId(parentSpanId);
    uint32_t flags = NapiHitraceUtil::GetPropertyInt32(env, valueObject, FLAGS_ATTR);
    HILOG_DEBUG(LOG_CORE, "Js2Native: flags is %{public}d.", flags);
    traceId.SetFlags(flags);
}

void NapiHitraceUtil::EnableTraceIdObjectFlag(const napi_env env, HiTraceId& traceId,
    napi_value& traceIdObject)
{
    NapiHitraceUtil::SetPropertyInt32(env, traceIdObject, FLAGS_ATTR, traceId.GetFlags());
}

void NapiHitraceUtil::SetPropertyInt32(const napi_env env, napi_value& object,
    const std::string& propertyName, uint32_t value)
{
    napi_value peropertyValue = CreateInt32Value(env, value);
    SetNamedProperty(env, object, propertyName, peropertyValue);
}

void NapiHitraceUtil::SetPropertyInt64(const napi_env env, napi_value& object,
    const std::string& propertyName, uint64_t value)
{
    napi_value peropertyValue = CreateInt64Value(env, value);
    SetNamedProperty(env, object, propertyName, peropertyValue);
}

void NapiHitraceUtil::SetPropertyBigInt64(const napi_env env, napi_value& object,
    const std::string& propertyName, uint64_t value)
{
    napi_value peropertyValue = CreateBigInt64Value(env, value);
    SetNamedProperty(env, object, propertyName, peropertyValue);
}

uint32_t NapiHitraceUtil::GetPropertyInt32(const napi_env env, const napi_value& object,
    const std::string& propertyName)
{
    napi_value propertyValue = GetPropertyByName(env, object, propertyName);
    napi_valuetype type;
    napi_status status = napi_typeof(env, propertyValue, &type);
    if (status != napi_ok) {
        HILOG_ERROR(LOG_CORE, "failed to get %{public}s from HiTraceId Js Object.",
            propertyName.c_str());
        return UINT32_T_PRO_DEFAULT_VALUE;
    }
    if (type != napi_valuetype::napi_number) {
        HILOG_ERROR(LOG_CORE, "type is not napi_number property.");
        return UINT32_T_PRO_DEFAULT_VALUE;
    }
    int32_t numberValue = 0;
    status = napi_get_value_int32(env, propertyValue, &numberValue);
    if (status == napi_ok) {
        return numberValue;
    }
    HILOG_ERROR(LOG_CORE, "failed to get napi_number property from HiTraceId Js Object.");
    return UINT32_T_PRO_DEFAULT_VALUE;
}

uint64_t NapiHitraceUtil::GetPropertyInt64(const napi_env env, const napi_value& object,
    const std::string& propertyName)
{
    napi_value propertyValue = GetPropertyByName(env, object, propertyName);
    napi_valuetype type;
    napi_status status = napi_typeof(env, propertyValue, &type);
    if (status != napi_ok) {
        HILOG_ERROR(LOG_CORE, "failed to get %{public}s from HiTraceId Js Object.",
            propertyName.c_str());
        return UINT64_T_PRO_DEFAULT_VALUE;
    }
    if (type != napi_valuetype::napi_number) {
        HILOG_ERROR(LOG_CORE, "type is not napi_number property.");
        return UINT64_T_PRO_DEFAULT_VALUE;
    }
    int64_t numberValue = 0;
    status = napi_get_value_int64(env, propertyValue, &numberValue);
    if (status == napi_ok) {
        return numberValue;
    }
    HILOG_ERROR(LOG_CORE, "failed to get napi_number property from HiTraceId Js Object.");
    return UINT64_T_PRO_DEFAULT_VALUE;
}

uint64_t NapiHitraceUtil::GetPropertyBigInt64(const napi_env env, const napi_value& object,
    const std::string& propertyName)
{
    napi_value propertyValue = GetPropertyByName(env, object, propertyName);
    napi_valuetype type;
    napi_status status = napi_typeof(env, propertyValue, &type);
    if (status != napi_ok) {
        HILOG_ERROR(LOG_CORE, "failed to get %{public}s from HiTraceId Js Object.",
            propertyName.c_str());
        return UINT64_T_PRO_DEFAULT_VALUE;
    }
    uint64_t bigInt64Value = 0;
    bool lossless = true;
    napi_get_value_bigint_uint64(env, propertyValue, &bigInt64Value, &lossless);
    return bigInt64Value;
}
} // namespace HiviewDFX
} // namespace OHOS