/*
 * Copyright (C) 2021 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 "delivery_callback.h"

#include <uv.h>

#include "telephony_log_wrapper.h"
#include "napi_sms_util.h"

namespace OHOS {
namespace Telephony {
static constexpr const char *PDU = "pdu";
static const int32_t CALLBACK_VALUE_LEN = 2;

DeliveryCallback::DeliveryCallback(bool hasCallback, napi_env env, napi_ref thisVarRef, napi_ref callbackRef)
    : hasCallback_(hasCallback), env_(env), thisVarRef_(thisVarRef), callbackRef_(callbackRef)
{
    if (hasCallback_) {
        napi_reference_ref(env_, thisVarRef_, nullptr);
        napi_reference_ref(env_, callbackRef_, nullptr);
    }
}

DeliveryCallback::~DeliveryCallback()
{
    if (hasCallback_) {
        uint32_t refCount = 0;
        if (napi_reference_unref(env_, thisVarRef_, &refCount) == napi_ok && refCount == 0) {
            napi_delete_reference(env_, thisVarRef_);
        }
        if (napi_reference_unref(env_, callbackRef_, &refCount) == napi_ok && refCount == 0) {
            napi_delete_reference(env_, callbackRef_);
        }
    }
    env_ = nullptr;
    thisVarRef_ = nullptr;
    callbackRef_ = nullptr;
}

void GetCallbackValues(napi_env &env, const std::string &pdu, napi_value *values, uint32_t len)
{
    if (len < CALLBACK_VALUE_LEN) {
        TELEPHONY_LOGE("callback values len invalid");
        return;
    }
    if (!pdu.empty() && pdu.size() <= 10000) {  // 10000 is max pdu size
        values[0] = NapiUtil::CreateUndefined(env);
        napi_create_object(env, &values[1]);
        napi_value arrayValue = nullptr;
        napi_create_array(env, &arrayValue);
        for (uint32_t i = 0; i < static_cast<uint32_t>(pdu.size()); ++i) {
            napi_value element = nullptr;
            int32_t intValue = pdu[i];
            napi_create_int32(env, intValue, &element);
            napi_set_element(env, arrayValue, i, element);
        }
        napi_set_named_property(env, values[1], PDU, arrayValue);
    } else {
        values[0] = NapiUtil::CreateErrorMessage(env, "invalid delivery report");
        values[1] = NapiUtil::CreateUndefined(env);
    }
}

void CompleteSmsDeliveryWork(uv_work_t *work, int status)
{
    TELEPHONY_LOGI("CompleteSmsDeliveryWork start");
    if (work == nullptr) {
        TELEPHONY_LOGE("CompleteSmsDeliveryWork work is nullptr");
        return;
    }
    if (work->data == nullptr) {
        delete work;
        return;
    }
    std::unique_ptr<DeliveryCallbackContext> pContext(static_cast<DeliveryCallbackContext *>(work->data));

    napi_env env = pContext->env;
    napi_ref thisVarRef = pContext->thisVarRef;
    napi_ref callbackRef = pContext->callbackRef;
    std::string pduStr = pContext->pduStr;
    napi_handle_scope scope = nullptr;
    if (napi_open_handle_scope(env, &scope) != napi_ok || scope == nullptr) {
        delete work;
        return;
    }
    napi_value callbackFunc = nullptr;
    if (napi_get_reference_value(env, callbackRef, &callbackFunc) != napi_ok || callbackFunc == nullptr) {
        napi_close_handle_scope(env, scope);
        delete work;
        return;
    }

    napi_value thisVar = nullptr;
    if (napi_get_reference_value(env, thisVarRef, &thisVar) != napi_ok || thisVar == nullptr) {
        napi_close_handle_scope(env, scope);
        delete work;
        return;
    }
    napi_value callbackValues[CALLBACK_VALUE_LEN] = { 0 };
    GetCallbackValues(env, pduStr, callbackValues, CALLBACK_VALUE_LEN);
    napi_value callbackResult = nullptr;
    size_t argc = sizeof(callbackValues) / sizeof(callbackValues[0]);
    napi_call_function(env, thisVar, callbackFunc, argc, callbackValues, &callbackResult);
    napi_close_handle_scope(env, scope);
    delete work;
    TELEPHONY_LOGI("CompleteSmsDeliveryWork end");
}

void DeliveryCallback::OnSmsDeliveryResult(const std::u16string &pdu)
{
    TELEPHONY_LOGI("OnSmsDeliveryResult start");
    if (!hasCallback_) {
        TELEPHONY_LOGE("OnSmsDeliveryResult hasCallback_ is false");
        return;
    }
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(env_, &loop);
    DeliveryCallbackContext *pContext = new DeliveryCallbackContext;
    pContext->env = env_;
    pContext->thisVarRef = thisVarRef_;
    pContext->callbackRef = callbackRef_;
    pContext->pduStr = NapiUtil::ToUtf8(pdu);
    uv_work_t *work = new uv_work_t;
    work->data = static_cast<void *>(pContext);

    int32_t errCode = uv_queue_work_with_qos(loop, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int status) { CompleteSmsDeliveryWork(work, status); }, uv_qos_default);
    if (errCode != 0) {
        TELEPHONY_LOGE("OnSmsDeliveryResult uv_queue_work_with_qos failed, errCode: %{public}d", errCode);
        delete pContext;
        delete work;
    }
    return;
}
} // namespace Telephony
} // namespace OHOS