#include "FFIUtils.h"
#include <Utils.h>

namespace sharedjsi
{

Function refCStringToString(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
                                          {
    char *str = jsPointerObjectToCPointer<char>(runtime, arguments[0]);
    if (str) {
      return String::createFromUtf8(runtime, str);
    } else {
      return Value::null();
    }
    return String::createFromUtf8(runtime, jsPointerObjectToCPointer<char>(runtime, arguments[0]));
  });
}

Function boxCStringIntoString(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
                                          {
    char *str = jsPointerObjectToCPointer<char>(runtime, arguments[0]);
    if (str) {
      String res = String::createFromUtf8(runtime, str);
      ::ditto_c_string_free(str);
      return res;
    } else {
      return Value::null();
    }
  });
}

Function sliceRefToUInt8Array(Runtime &runtime) {
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime, STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &,
                                             const Value *arguments,
                                             size_t) -> Value {
    auto fatPtrJs = arguments[0].asObject(runtime);
    auto ptr = jsPointerObjectToCPointer<uint8_t>(runtime, fatPtrJs.getProperty(runtime, "ptr"));
    auto len = static_cast<size_t>(fatPtrJs.getProperty(runtime, "len").asNumber());

    slice_boxed_uint8 slice = {.ptr = ptr, .len = len};
    if (!slice.ptr) {
      return Value::null();
    }

    TypedArray<TypedArrayKind::Uint8Array> typedArray(runtime, slice.len);
    ArrayBuffer arrayBuffer = typedArray.getBuffer(runtime);
    std::memcpy(arrayBuffer.data(runtime), slice.ptr, slice.len);

    return typedArray;
  });
}

Function sliceBoxedToUInt8Array(Runtime &runtime) {
  return Function::createFromHostFunction(runtime,
                                          PropNameID::forAscii(runtime, STRINGIFIED_FUNC_NAME()),
                                          0,
                                          [](Runtime &runtime,
                                             const Value &,
                                             const Value *arguments,
                                             size_t) -> Value {
    auto fatPtrJs = arguments[0].asObject(runtime);
    auto ptr = jsPointerObjectToCPointer<uint8_t>(runtime, fatPtrJs.getProperty(runtime, "ptr"));
    auto len = static_cast<size_t>(fatPtrJs.getProperty(runtime, "len").asNumber());

    slice_boxed_uint8 slice = {.ptr = ptr, .len = len};
    if (!slice.ptr) {
      return Value::null();
    }

    TypedArray<TypedArrayKind::Uint8Array> typedArray(runtime, slice.len);
    ArrayBuffer arrayBuffer = typedArray.getBuffer(runtime);
    std::memcpy(arrayBuffer.data(runtime), slice.ptr, slice.len);

    ::ditto_c_bytes_free(slice);

    return typedArray;
  });
}

Function withOutBoxCBytes(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 callback = arguments[0].asObject(runtime).asFunction(runtime);
    slice_boxed_uint8 box_c_bytes = { nullptr };
    auto out_box_cbytes = &box_c_bytes;
    auto out_box_cbytes_js = cPointerToJSPointerObject<slice_boxed_uint8>(runtime, out_box_cbytes);

    callback.call(runtime, out_box_cbytes_js);

    return cSlicedBoxToJSSlicedBox(runtime, box_c_bytes);
  });
}

}
