/*
 * Copyright 2021-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 "test-mongocrypt-util.h"
#include "test-mongocrypt-assert.h"

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

const char *mongocrypt_ctx_state_to_string(mongocrypt_ctx_state_t state) {
    switch (state) {
    case MONGOCRYPT_CTX_ERROR: return "MONGOCRYPT_CTX_ERROR";
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB: return "MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB";
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: return "MONGOCRYPT_CTX_NEED_MONGO_COLLINFO";
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: return "MONGOCRYPT_CTX_NEED_MONGO_MARKINGS";
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS: return "MONGOCRYPT_CTX_NEED_MONGO_KEYS";
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: return "MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS";
    case MONGOCRYPT_CTX_NEED_KMS: return "MONGOCRYPT_CTX_NEED_KMS";
    case MONGOCRYPT_CTX_READY: return "MONGOCRYPT_CTX_READY";
    case MONGOCRYPT_CTX_DONE: return "MONGOCRYPT_CTX_DONE";
    default: return "UNKNOWN";
    }
}

char *data_to_hex(const uint8_t *buf, size_t len) {
    char *hex_chars = malloc(len * 2 + 1);

    char *p = hex_chars;
    size_t i;

    for (i = 0; i < len; i++) {
        ASSERT(2 == bson_snprintf(p, 3, "%02x", buf[i]));
        p += 2;
    }

    *p = '\0';

    return hex_chars;
}

void bson_iter_bson(bson_iter_t *iter, bson_t *bson) {
    uint32_t len;
    const uint8_t *data = NULL;
    if (BSON_ITER_HOLDS_DOCUMENT(iter)) {
        bson_iter_document(iter, &len, &data);
    }
    if (BSON_ITER_HOLDS_ARRAY(iter)) {
        bson_iter_array(iter, &len, &data);
    }
    BSON_ASSERT(data);
    bson_init_static(bson, data, len);
}

bool kms_ctx_feed_all(mongocrypt_kms_ctx_t *kms_ctx, const uint8_t *data, uint32_t datalen) {
    mongocrypt_binary_t *bytes;
    uint32_t offset = 0;

    while (mongocrypt_kms_ctx_bytes_needed(kms_ctx) > 0) {
        uint32_t len = mongocrypt_kms_ctx_bytes_needed(kms_ctx);

        // Cap `len` to the total input length.
        // `mongocrypt_kms_ctx_bytes_needed` may request more bytes than is in the response.
        // The HTTP response parser defaults to requesting 1024 bytes initially.
        if (len > datalen - offset) {
            len = datalen - offset;
        }
        ASSERT_CMPINT(offset + len, <=, datalen);
        bytes = mongocrypt_binary_new_from_data((uint8_t *)data + offset, len);
        if (!mongocrypt_kms_ctx_feed(kms_ctx, bytes)) {
            mongocrypt_binary_destroy(bytes);
            return false;
        }

        offset += len;
        mongocrypt_binary_destroy(bytes);
    }
    ASSERT_CMPINT(0, ==, mongocrypt_kms_ctx_bytes_needed(kms_ctx));

    return true;
}
