/*
 * Copyright 2009-present MongoDB, Inc.
 *
 * 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 <bson/bson-bcon.h>
#include <bson/bson.h>

#include <TestSuite.h>
#include <test-libmongoc.h> // skip_if_no_large_allocations


static void
test_value_basic(void)
{
   static const uint8_t raw[16] = {0};
   const bson_value_t *value;
   bson_value_t copy;
   bson_iter_t iter;
   bson_oid_t oid;
   bson_t other = BSON_INITIALIZER;
   bson_t *doc;
   bson_t sub = BSON_INITIALIZER;
   bool r;
   int i;

   bson_oid_init(&oid, NULL);

   doc = BCON_NEW("double",
                  BCON_DOUBLE(123.4),
                  "utf8",
                  "this is my string",
                  "document",
                  BCON_DOCUMENT(&sub),
                  "array",
                  BCON_DOCUMENT(&sub),
                  "binary",
                  BCON_BIN(BSON_SUBTYPE_BINARY, raw, sizeof raw),
                  "undefined",
                  BCON_UNDEFINED,
                  "oid",
                  BCON_OID(&oid),
                  "bool",
                  BCON_BOOL(true),
                  "datetime",
                  BCON_DATE_TIME(12345678),
                  "null",
                  BCON_NULL,
                  "regex",
                  BCON_REGEX("^hello", "i"),
                  "dbpointer",
                  BCON_DBPOINTER("test.test", &oid),
                  "code",
                  BCON_CODE("var a = function() {}"),
                  "symbol",
                  BCON_SYMBOL("my_symbol"),
                  "codewscope",
                  BCON_CODEWSCOPE("var a = 1;", &sub),
                  "int32",
                  BCON_INT32(1234),
                  "timestamp",
                  BCON_TIMESTAMP(1234, 4567),
                  "int64",
                  BCON_INT32(4321),
                  "maxkey",
                  BCON_MAXKEY,
                  "minkey",
                  BCON_MINKEY);

   r = bson_iter_init(&iter, doc);
   BSON_ASSERT(r);

   for (i = 0; i < 20; i++) {
      r = bson_iter_next(&iter);
      BSON_ASSERT(r);

      value = bson_iter_value(&iter);
      BSON_ASSERT(value);

      bson_value_copy(value, &copy);

      r = bson_append_value(&other, bson_iter_key(&iter), -1, &copy);
      BSON_ASSERT(r);

      bson_value_destroy(&copy);
   }

   r = bson_iter_next(&iter);
   BSON_ASSERT(!r);

   bson_destroy(doc);
   bson_destroy(&sub);
   bson_destroy(&other);
}


static void
test_value_decimal128(void)
{
   const bson_value_t *value;
   bson_value_t copy;
   bson_iter_t iter;
   bson_decimal128_t dec;
   bson_t other = BSON_INITIALIZER;
   bson_t *doc;

   BSON_ASSERT(bson_decimal128_from_string("123.5", &dec));
   doc = BCON_NEW("decimal128", BCON_DECIMAL128(&dec));
   BSON_ASSERT(bson_iter_init(&iter, doc) && bson_iter_next(&iter));
   value = bson_iter_value(&iter);
   BSON_ASSERT(value);
   bson_value_copy(value, &copy);
   BSON_ASSERT(bson_append_value(&other, bson_iter_key(&iter), -1, &copy));

   bson_value_destroy(&copy);
   bson_destroy(doc);
   bson_destroy(&other);
}

static void
test_value_big_copy(void *unused)
{
   BSON_UNUSED(unused);
   char *big_string = bson_malloc(UINT32_MAX);
   ASSERT(big_string);
   memset(big_string, UINT32_MAX, 'a'); // `big_string` is not NULL-terminated.

   bson_value_t dst;
   bson_value_t src = {.value_type = BSON_TYPE_UTF8, .value = {.v_utf8 = {.str = big_string, .len = UINT32_MAX}}};
   bson_value_copy(&src, &dst);
   bson_free(big_string);
   bson_value_destroy(&dst);
}

void
test_value_install(TestSuite *suite)
{
   TestSuite_Add(suite, "/bson/value/basic", test_value_basic);
   TestSuite_Add(suite, "/bson/value/decimal128", test_value_decimal128);
   TestSuite_AddFull(suite, "/bson/value/big_copy", test_value_big_copy, NULL, NULL, skip_if_no_large_allocations);
}
