#include <mongoc/mongoc-util-private.h>

#include <mongoc/mongoc.h>

#include <mlib/config.h>

#include <TestSuite.h>
#include <test-conveniences.h>


static void
test_command_name(void)
{
   bson_t *commands[] = {
      tmp_bson("{'foo': 1}"),
      tmp_bson("{'query': {'foo': 1}}"),
      tmp_bson("{'query': {'foo': 1}, '$readPreference': 1}"),
      tmp_bson("{'$query': {'foo': 1}}"),
      tmp_bson("{'$query': {'foo': 1}, '$readPreference': 1}"),
      tmp_bson("{'$readPreference': 1, '$query': {'foo': 1}}"),
   };

   size_t i;

   for (i = 0; i < sizeof(commands) / sizeof(bson_t *); i++) {
      ASSERT_CMPSTR("foo", _mongoc_get_command_name(commands[i]));
   }
}


static void
test_rand_simple(void)
{
   int i;
   unsigned int seed = 0;
   int value, first_value;

   first_value = _mongoc_rand_simple(&seed);

   for (i = 0; i < 1000; i++) {
      value = _mongoc_rand_simple(&seed);
      if (value != first_value) {
         /* success */
         break;
      }
   }
}

static void
test_lowercase_utf8(void)
{
   char *snowman = "\xE2\x9b\x84";
   char *letters = "aBcDe";
   char *buf = bson_malloc0(strlen(snowman) + 1);

   mongoc_lowercase(snowman, buf);
   ASSERT_CMPSTR(snowman, buf);
   bson_free(buf);

   buf = bson_malloc0(strlen(letters) + 1);
   mongoc_lowercase(letters, buf);
   ASSERT_CMPSTR("abcde", buf);
   bson_free(buf);
}


static void
test_wire_server_versions(void)
{
   mlib_diagnostic_push();
   mlib_disable_constant_conditional_expression_warnings();
   /* Ensure valid inclusive range. */
   ASSERT_WITH_MSG(WIRE_VERSION_MIN <= WIRE_VERSION_MAX,
                   "WIRE_VERSION_MAX (%d) must be greater than or equal to "
                   "WIRE_VERSION_MIN (%d)",
                   WIRE_VERSION_MAX,
                   WIRE_VERSION_MIN);
   mlib_diagnostic_pop();

   /* Bumping WIRE_VERSION_MAX must be accompanied by an update to
    * `_mongoc_wire_version_to_server_version`. */
   ASSERT_WITH_MSG(strcmp(_mongoc_wire_version_to_server_version(WIRE_VERSION_MAX), "Unknown"),
                   "WIRE_VERSION_MAX must have a corresponding server version defined in "
                   "_mongoc_wire_version_to_server_version");

   /* Unlikely given the minimum version should always be a value older than the
    * maximum version, but nevertheless important to assert so warning/error
    * messages remain valid. */
   ASSERT_WITH_MSG(strcmp(_mongoc_wire_version_to_server_version(WIRE_VERSION_MIN), "Unknown"),
                   "WIRE_VERSION_MIN must have a corresponding server version defined in "
                   "_mongoc_wire_version_to_server_version");
}

static void
test_bin_to_hex(void)
{
   const char *bin = "foobar";
   const char *expect = "666F6F626172";
   char *got = bin_to_hex((const uint8_t *)bin, strlen(bin));
   ASSERT_CMPSTR(got, expect);
   bson_free(got);
}

static void
test_hex_to_bin(void)
{
   const char *expect = "foobar";
   size_t len;

   // Test lowercase:
   {
      const char *hexstr = "666f6f62617200";
      uint8_t *got = hex_to_bin(hexstr, &len);
      ASSERT_CMPSTR((const char *)got, expect);
      ASSERT_CMPSIZE_T(len, ==, 7);
      bson_free(got);
   }

   // Test uppercase:
   {
      const char *hexstr = "666F6F62617200";
      uint8_t *got = hex_to_bin(hexstr, &len);
      ASSERT_CMPSTR((const char *)got, expect);
      ASSERT_CMPSIZE_T(len, ==, 7);
      bson_free(got);
   }

   ASSERT_WITH_MSG(!hex_to_bin("  66", &len), "whitespace is an error");
   ASSERT_WITH_MSG(!hex_to_bin("666", &len), "non-even number of digits is an error");
   ASSERT_WITH_MSG(!hex_to_bin("ZZ", &len), "non-hex digits is an error");
}

static void
test_percent_encode(void)
{
   // Simple case:
   {
      char *got = mongoc_percent_encode("hello world!");
      ASSERT(got);
      ASSERT_CMPSTR(got, "hello%20world%21");
      bson_free(got);
   }

   // Empty string:
   {
      char *got = mongoc_percent_encode("");
      ASSERT(got);
      ASSERT_CMPSTR(got, "");
      bson_free(got);
   }

   // All reserved characters:
   {
      char *got = mongoc_percent_encode("!#$");
      ASSERT(got);
      ASSERT_CMPSTR(got, "%21%23%24");
      bson_free(got);
   }
}

void
test_util_install(TestSuite *suite)
{
   TestSuite_Add(suite, "/Util/command_name", test_command_name);
   TestSuite_Add(suite, "/Util/rand_simple", test_rand_simple);
   TestSuite_Add(suite, "/Util/lowercase_utf8", test_lowercase_utf8);
   TestSuite_Add(suite, "/Util/wire_server_versions", test_wire_server_versions);
   TestSuite_Add(suite, "/Util/bin_to_hex", test_bin_to_hex);
   TestSuite_Add(suite, "/Util/hex_to_bin", test_hex_to_bin);
   TestSuite_Add(suite, "/Util/percent_encode", test_percent_encode);
}
