#include <SyncSubscription.h>

#include "Utils.h"

namespace sharedjsi
{

Function dittoffi_sync_register_subscription_throws(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ditto = jsPointerObjectToCPointer<CDitto_t>(runtime, arguments[0]);
    std::string query_str = jsTypedArrayToCString(runtime, arguments[1]);
    const char *query = query_str.c_str();

    std::vector<uint8_t> query_args_cbor_vec = jsTypedArrayToCVector(runtime, arguments[2]);
    slice_ref_uint8_t query_args_cbor = borrowVecAsOptRefSlice(query_args_cbor_vec);

    dittoffi_result_dittoffi_sync_subscription_ptr_t res = ::dittoffi_sync_register_subscription_throws(ditto, query, query_args_cbor);

    Object obj(runtime);
    obj.setProperty(runtime, "success", cPointerToJSPointerObject(runtime, res.success));
    obj.setProperty(runtime, "error", cPointerToJSPointerObject(runtime, res.error));
    return obj;
  });
}

Function dittoffi_sync_subscriptions(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ditto = jsPointerObjectToCPointer<CDitto_t>(runtime, arguments[0]);
    Vec_dittoffi_sync_subscription_ptr_t res = ::dittoffi_sync_subscriptions(ditto);

    jsi::Array jsArray(runtime, res.len);

    for (size_t i = 0; i < res.len; ++i) {
      dittoffi_sync_subscription_t *ffi_subscription = res.ptr[i];
      auto jsObj = cPointerToJSPointerObject<dittoffi_sync_subscription_t>(runtime, ffi_subscription);
      jsArray.setValueAtIndex(runtime, i, jsObj);
    }

    ::dittoffi_sync_subscriptions_free_sparse(res);

    return jsArray;
  });
}

Function dittoffi_sync_subscription_id(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ffi_sync_subscription = jsPointerObjectToCPointer<dittoffi_sync_subscription_t>(runtime, arguments[0]);
    slice_boxed_uint8_t id_cbor = ::dittoffi_sync_subscription_id(ffi_sync_subscription);
    return cSlicedBoxToJSSlicedBox(runtime, id_cbor);
  });
}

Function dittoffi_sync_subscription_query_string(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ffi_sync_subscription = jsPointerObjectToCPointer<dittoffi_sync_subscription_t>(runtime, arguments[0]);
    char *res = ::dittoffi_sync_subscription_query_string(ffi_sync_subscription);
    return cPointerToJSPointerObject<char>(runtime, res);
  });
}

Function dittoffi_sync_subscription_query_arguments_cbor(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ffi_sync_subscription = jsPointerObjectToCPointer<dittoffi_sync_subscription_t>(runtime, arguments[0]);
    slice_boxed_uint8_t id_cbor = ::dittoffi_sync_subscription_query_arguments_cbor(ffi_sync_subscription);
    return cSlicedBoxToJSSlicedBox(runtime, id_cbor);
  });
}

Function dittoffi_sync_subscription_query_arguments_json(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ffi_sync_subscription = jsPointerObjectToCPointer<dittoffi_sync_subscription_t>(runtime, arguments[0]);
    slice_boxed_uint8_t args_json_bytes = ::dittoffi_sync_subscription_query_arguments_json(ffi_sync_subscription);
    return cSlicedBoxToJSSlicedBox(runtime, args_json_bytes);
  });
}

Function dittoffi_sync_subscription_cancel(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ffi_sync_subscription = jsPointerObjectToCPointer<dittoffi_sync_subscription_t>(runtime, arguments[0]);
    ::dittoffi_sync_subscription_cancel(ffi_sync_subscription);
    return {};
  });
}

Function dittoffi_sync_subscription_is_cancelled(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ffi_sync_subscription = jsPointerObjectToCPointer<dittoffi_sync_subscription_t>(runtime, arguments[0]);
    return ::dittoffi_sync_subscription_is_cancelled(ffi_sync_subscription);
  });
}

Function dittoffi_sync_subscription_free(Runtime &runtime)
{
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime,
                                                               STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &thisValue,
                                             const Value *arguments,
                                             size_t count) -> Value
                                          {
    auto *ffi_sync_subscription = jsPointerObjectToCPointer<dittoffi_sync_subscription_t>(runtime, arguments[0]);
    ::dittoffi_sync_subscription_free(ffi_sync_subscription);
    return {};
  });
}
}
