/**
 *
 * Agora Real Time Engagement
 * Created by Wei Hu in 2022-04.
 * Copyright (c) 2022 Agora IO. All rights reserved.
 *
 */
#pragma once

#include "rte_config.h"

#include <node_api.h>

#include "lib/mutex.h"
#include "lib/shared_ptr.h"
#include "lib/signature.h"
#include "lib/string.h"
#include "rte_runtime/sanitizer/thread_check.h"

#define RTE_NODEJS_THREADSAFE_FUNCTION_SIGNATURE 0x1D11D6EF2722D8FBU

#define CREATE_JS_CALLBACK(rte_tsfn, env, log_name, js_cb, invoke_js_cb)       \
  do {                                                                         \
    rte_tsfn = rte_nodejs_threadsafe_function_create((env), log_name, (js_cb), \
                                                     (invoke_js_cb));          \
    assert((rte_tsfn));                                                        \
    rte_nodejs_threadsafe_function_inc_rc((rte_tsfn));                         \
  } while (0)

#define PREPARE_TO_CALL_TSFN(call_info)    \
  do {                                     \
    rte_nodejs_sem_init(&(call_info).sem); \
  } while (0)

#define END_TO_CALL_TSFN(call_info, fmt, ...) \
  do {                                        \
    if (rc) {                                 \
      rte_nodejs_sem_wait(&(call_info).sem);  \
    } else {                                  \
      ZF_LOGW(fmt, ##__VA_ARGS__);            \
    }                                         \
    rte_nodejs_sem_cleanup(&(call_info).sem); \
    if (!(call_info).js_result) {             \
      ZF_LOGW(fmt, ##__VA_ARGS__);            \
    }                                         \
  } while (0)

typedef struct rte_nodejs_threadsafe_function_t {
  rte_signature_t signature;

  rte_mutex_t* lock;
  rte_string_t name;

  // The JS function which this tsfn calling.
  napi_ref js_func_ref;

  // The threadsafe function itself.
  napi_threadsafe_function tsfn;

  // Used to determine the timing of destroying this extension bridge.
  rte_sharedptr_t* shared_ptr;

  rte_sanitizer_thread_check_t thread_check;
} rte_nodejs_threadsafe_function_t;

AGORA_RTE_PRIVATE_API bool rte_nodejs_threadsafe_function_check_integrity(
    rte_nodejs_threadsafe_function_t* self, bool check_thread);

AGORA_RTE_API rte_nodejs_threadsafe_function_t*
rte_nodejs_threadsafe_function_create(
    napi_env env, const char* name, napi_value js_func,
    napi_threadsafe_function_call_js invoke_js);

AGORA_RTE_PRIVATE_API void rte_nodejs_threadsafe_function_inc_rc(
    rte_nodejs_threadsafe_function_t* self);

AGORA_RTE_PRIVATE_API void rte_nodejs_threadsafe_function_dec_rc(
    rte_nodejs_threadsafe_function_t* self);

AGORA_RTE_API bool rte_nodejs_threadsafe_function_invoke(
    rte_nodejs_threadsafe_function_t* rte_tsfn, void* data);

AGORA_RTE_API void rte_nodejs_threadsafe_function_release(
    napi_env env, rte_nodejs_threadsafe_function_t* self);
