#include "Utils.h"

#include <Document.h>
#include <unordered_map>

namespace sharedjsi
{

Function ditto_document_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
                                          {
    CDocument_t *document = jsPointerObjectToCPointer<CDocument_t>(runtime, arguments[0]);
    ::ditto_document_free(document);
    return Value();
  });
}

Function ditto_document_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
                                          {
    CDocument_t const *document = jsPointerObjectToCPointer<CDocument>(runtime, arguments[0]);
    slice_boxed_uint8_t res = ::ditto_document_id(document);
    return cSlicedBoxToJSSlicedBox(runtime, res);
  });
}

Function ditto_document_id_query_compatible(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
                                          {

    std::vector<uint8_t> id_vec = jsTypedArrayToCVector(runtime, arguments[0]);
    slice_ref_uint8_t id = borrowVecAsRefSlice(id_vec);

    std::string stringPrimitiveStr = arguments[1].getString(runtime).utf8(runtime);
    StringPrimitiveFormat_t stringPrimitiveFormat;
    if (stringPrimitiveStr == "WithQuotes") {
      stringPrimitiveFormat = STRING_PRIMITIVE_FORMAT_WITH_QUOTES;
    } if (stringPrimitiveStr == "WithoutQuotes") {
      stringPrimitiveFormat = STRING_PRIMITIVE_FORMAT_WITHOUT_QUOTES;
    } else {
      throw std::invalid_argument("Invalid string primitive format.");
    }

    char *res = ::ditto_document_id_query_compatible(id, stringPrimitiveFormat);
    return cPointerToJSPointerObject<char>(runtime, res);
  });
}

Function ditto_document_get_cbor_with_path_type(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
                                          {
    CDocument_t const *document = jsPointerObjectToCPointer<CDocument>(runtime, arguments[0]);
    std::string path_str = jsTypedArrayToCString(runtime, arguments[1]);
    const char *path = path_str.c_str();
    std::string pathTypeStr = arguments[2].asString(runtime).utf8(runtime);

    static const std::unordered_map<std::string, PathAccessorType_t> lookup = {
      {"String", PATH_ACCESSOR_TYPE_STRING},
      {"Number", PATH_ACCESSOR_TYPE_NUMBER},
      {"Int", PATH_ACCESSOR_TYPE_INT},
      {"UInt", PATH_ACCESSOR_TYPE_U_INT},
      {"Float", PATH_ACCESSOR_TYPE_FLOAT},
      {"Double", PATH_ACCESSOR_TYPE_DOUBLE},
      {"Bool", PATH_ACCESSOR_TYPE_BOOL},
      {"Null", PATH_ACCESSOR_TYPE_NULL},
      {"Object", PATH_ACCESSOR_TYPE_OBJECT},
      {"Array", PATH_ACCESSOR_TYPE_ARRAY},
      {"Any", PATH_ACCESSOR_TYPE_ANY},
      {"Counter", PATH_ACCESSOR_TYPE_COUNTER},
      {"Register", PATH_ACCESSOR_TYPE_REGISTER},
      {"Attachment", PATH_ACCESSOR_TYPE_ATTACHMENT},
      {"Rga", PATH_ACCESSOR_TYPE_R_W_MAP},
      {"RWMap", PATH_ACCESSOR_TYPE_R_W_MAP},
    };

    PathAccessorType_t path_type;
    auto it = lookup.find(pathTypeStr);
    if (it != lookup.end()) {
      path_type = it->second;
    } else {
      throw std::invalid_argument("Invalid path type string");
    }

    CBORPathResult_t res = ::ditto_document_get_cbor_with_path_type(document, path, path_type);

    Object obj(runtime);
    obj.setProperty(runtime, "status_code", static_cast<double>(res.status_code));
    obj.setProperty(runtime, "cbor", cSlicedBoxToJSSlicedBox(runtime, res.cbor));
    return obj;
  });
}


Function ditto_validate_document_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
                                          {

    std::vector<uint8_t> doc_id_vec = jsTypedArrayToCVector(runtime, arguments[0]);
    slice_ref_uint8_t doc_id = borrowVecAsRefSlice(doc_id_vec);

    slice_boxed_uint8 *out_cbor = jsPointerObjectToCPointer<slice_boxed_uint8>(runtime, arguments[1]);
    uint32_t res = ::ditto_validate_document_id(doc_id, out_cbor);
    return static_cast<double>(res);
  });
}
}
