/*-
 *   BSD LICENSE
 *
 *   Copyright (c) Intel Corporation.
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "spdk/json.h"

#include "spdk_internal/utf.h"
#include "spdk_internal/log.h"

#define SPDK_JSON_DEBUG(...) SPDK_DEBUGLOG(SPDK_LOG_JSON_UTIL, __VA_ARGS__)

size_t
spdk_json_val_len(const struct spdk_json_val *val)
{
	if (val == NULL) {
		return 0;
	}

	if (val->type == SPDK_JSON_VAL_ARRAY_BEGIN || val->type == SPDK_JSON_VAL_OBJECT_BEGIN) {
		return val->len + 2;
	}

	return 1;
}

bool
spdk_json_strequal(const struct spdk_json_val *val, const char *str)
{
	size_t len;

	if (val->type != SPDK_JSON_VAL_STRING && val->type != SPDK_JSON_VAL_NAME) {
		return false;
	}

	len = strlen(str);
	if (val->len != len) {
		return false;
	}

	return memcmp(val->start, str, len) == 0;
}

char *
spdk_json_strdup(const struct spdk_json_val *val)
{
	size_t len;
	char *s;

	if (val->type != SPDK_JSON_VAL_STRING && val->type != SPDK_JSON_VAL_NAME) {
		return NULL;
	}

	len = val->len;

	if (memchr(val->start, '\0', len)) {
		/* String contains embedded NUL, so it is not a valid C string. */
		return NULL;
	}

	s = malloc(len + 1);
	if (s == NULL) {
		return s;
	}

	memcpy(s, val->start, len);
	s[len] = '\0';

	return s;
}

struct spdk_json_num {
	bool negative;
	uint64_t significand;
	int64_t exponent;
};

static int
spdk_json_number_split(const struct spdk_json_val *val, struct spdk_json_num *num)
{
	const char *iter;
	size_t remaining;
	uint64_t *pval;
	uint64_t frac_digits = 0;
	uint64_t exponent_u64 = 0;
	bool exponent_negative = false;
	enum {
		NUM_STATE_INT,
		NUM_STATE_FRAC,
		NUM_STATE_EXP,
	} state;

	memset(num, 0, sizeof(*num));

	if (val->type != SPDK_JSON_VAL_NUMBER) {
		return -EINVAL;
	}

	remaining = val->len;
	if (remaining == 0) {
		return -EINVAL;
	}

	iter = val->start;
	if (*iter == '-') {
		num->negative = true;
		iter++;
		remaining--;
	}

	state = NUM_STATE_INT;
	pval = &num->significand;
	while (remaining--) {
		char c = *iter++;

		if (c == '.') {
			state = NUM_STATE_FRAC;
		} else if (c == 'e' || c == 'E') {
			state = NUM_STATE_EXP;
			pval = &exponent_u64;
		} else if (c == '-') {
			assert(state == NUM_STATE_EXP);
			exponent_negative = true;
		} else if (c == '+') {
			assert(state == NUM_STATE_EXP);
			/* exp_negative = false; */ /* already false by default */
		} else {
			uint64_t new_val;

			assert(c >= '0' && c <= '9');
			new_val = *pval * 10 + c - '0';
			if (new_val < *pval) {
				return -ERANGE;
			}

			if (state == NUM_STATE_FRAC) {
				frac_digits++;
			}

			*pval = new_val;
		}
	}

	if (exponent_negative) {
		if (exponent_u64 > 9223372036854775808ULL) { /* abs(INT64_MIN) */
			return -ERANGE;
		}
		num->exponent = (int64_t) - exponent_u64;
	} else {
		if (exponent_u64 > INT64_MAX) {
			return -ERANGE;
		}
		num->exponent = exponent_u64;
	}
	num->exponent -= frac_digits;

	/* Apply as much of the exponent as possible without overflow or truncation */
	if (num->exponent < 0) {
		while (num->exponent && num->significand >= 10 && num->significand % 10 == 0) {
			num->significand /= 10;
			num->exponent++;
		}
	} else { /* positive exponent */
		while (num->exponent) {
			uint64_t new_val = num->significand * 10;

			if (new_val < num->significand) {
				break;
			}

			num->significand = new_val;
			num->exponent--;
		}
	}

	return 0;
}

int
spdk_json_number_to_uint16(const struct spdk_json_val *val, uint16_t *num)
{
	struct spdk_json_num split_num;
	int rc;

	rc = spdk_json_number_split(val, &split_num);
	if (rc) {
		return rc;
	}

	if (split_num.exponent || split_num.negative) {
		return -ERANGE;
	}

	if (split_num.significand > UINT16_MAX) {
		return -ERANGE;
	}
	*num = (uint16_t)split_num.significand;
	return 0;
}

int
spdk_json_number_to_int32(const struct spdk_json_val *val, int32_t *num)
{
	struct spdk_json_num split_num;
	int rc;

	rc = spdk_json_number_split(val, &split_num);
	if (rc) {
		return rc;
	}

	if (split_num.exponent) {
		return -ERANGE;
	}

	if (split_num.negative) {
		if (split_num.significand > 2147483648) { /* abs(INT32_MIN) */
			return -ERANGE;
		}
		*num = (int32_t) - (int64_t)split_num.significand;
		return 0;
	}

	/* positive */
	if (split_num.significand > INT32_MAX) {
		return -ERANGE;
	}
	*num = (int32_t)split_num.significand;
	return 0;
}

int
spdk_json_number_to_uint32(const struct spdk_json_val *val, uint32_t *num)
{
	struct spdk_json_num split_num;
	int rc;

	rc = spdk_json_number_split(val, &split_num);
	if (rc) {
		return rc;
	}

	if (split_num.exponent || split_num.negative) {
		return -ERANGE;
	}

	if (split_num.significand > UINT32_MAX) {
		return -ERANGE;
	}
	*num = (uint32_t)split_num.significand;
	return 0;
}

int
spdk_json_number_to_uint64(const struct spdk_json_val *val, uint64_t *num)
{
	struct spdk_json_num split_num;
	int rc;

	rc = spdk_json_number_split(val, &split_num);
	if (rc) {
		return rc;
	}

	if (split_num.exponent || split_num.negative) {
		return -ERANGE;
	}

	*num = split_num.significand;
	return 0;
}

int
spdk_json_decode_object(const struct spdk_json_val *values,
			const struct spdk_json_object_decoder *decoders, size_t num_decoders, void *out)
{
	uint32_t i;
	bool invalid = false;
	size_t decidx;
	bool *seen;

	if (values == NULL || values->type != SPDK_JSON_VAL_OBJECT_BEGIN) {
		return -1;
	}

	seen = calloc(sizeof(bool), num_decoders);
	if (seen == NULL) {
		return -1;
	}

	for (i = 0; i < values->len;) {
		const struct spdk_json_val *name = &values[i + 1];
		const struct spdk_json_val *v = &values[i + 2];
		bool found = false;

		for (decidx = 0; decidx < num_decoders; decidx++) {
			const struct spdk_json_object_decoder *dec = &decoders[decidx];
			if (spdk_json_strequal(name, dec->name)) {
				void *field = (void *)((uintptr_t)out + dec->offset);

				found = true;

				if (seen[decidx]) {
					/* duplicate field name */
					invalid = true;
				} else {
					seen[decidx] = true;
					if (dec->decode_func(v, field)) {
						invalid = true;
						/* keep going to fill out any other valid keys */
					}
				}
				break;
			}
		}

		if (!found) {
			invalid = true;
		}

		i += 1 + spdk_json_val_len(v);
	}

	for (decidx = 0; decidx < num_decoders; decidx++) {
		if (!decoders[decidx].optional && !seen[decidx]) {
			/* required field is missing */
			invalid = true;
			break;
		}
	}

	free(seen);
	return invalid ? -1 : 0;
}

int
spdk_json_decode_array(const struct spdk_json_val *values, spdk_json_decode_fn decode_func,
		       void *out, size_t max_size, size_t *out_size, size_t stride)
{
	uint32_t i;
	char *field;
	char *out_end;

	if (values == NULL || values->type != SPDK_JSON_VAL_ARRAY_BEGIN) {
		return -1;
	}

	*out_size = 0;
	field = out;
	out_end = field + max_size * stride;
	for (i = 0; i < values->len;) {
		const struct spdk_json_val *v = &values[i + 1];

		if (field == out_end) {
			return -1;
		}

		if (decode_func(v, field)) {
			return -1;
		}

		i += spdk_json_val_len(v);
		field += stride;
		(*out_size)++;
	}

	return 0;
}

int
spdk_json_decode_bool(const struct spdk_json_val *val, void *out)
{
	bool *f = out;

	if (val->type != SPDK_JSON_VAL_TRUE && val->type != SPDK_JSON_VAL_FALSE) {
		return -1;
	}

	*f = val->type == SPDK_JSON_VAL_TRUE;
	return 0;
}

int
spdk_json_decode_uint16(const struct spdk_json_val *val, void *out)
{
	uint16_t *i = out;

	return spdk_json_number_to_uint16(val, i);
}

int
spdk_json_decode_int32(const struct spdk_json_val *val, void *out)
{
	int32_t *i = out;

	return spdk_json_number_to_int32(val, i);
}

int
spdk_json_decode_uint32(const struct spdk_json_val *val, void *out)
{
	uint32_t *i = out;

	return spdk_json_number_to_uint32(val, i);
}

int
spdk_json_decode_uint64(const struct spdk_json_val *val, void *out)
{
	uint64_t *i = out;

	return spdk_json_number_to_uint64(val, i);
}

int
spdk_json_decode_string(const struct spdk_json_val *val, void *out)
{
	char **s = out;

	free(*s);

	*s = spdk_json_strdup(val);

	if (*s) {
		return 0;
	} else {
		return -1;
	}
}

static struct spdk_json_val *
spdk_json_first(struct spdk_json_val *object, enum spdk_json_val_type type)
{
	/* 'object' must be JSON object or array. 'type' might be combination of these two. */
	assert((type & (SPDK_JSON_VAL_ARRAY_BEGIN | SPDK_JSON_VAL_OBJECT_BEGIN)) != 0);

	assert(object != NULL);

	if ((object->type & type) == 0) {
		return NULL;
	}

	object++;
	if (object->len == 0) {
		return NULL;
	}

	return object;
}

static struct spdk_json_val *
spdk_json_value(struct spdk_json_val *key)
{
	return key->type == SPDK_JSON_VAL_NAME ? key + 1 : NULL;
}

int
spdk_json_find(struct spdk_json_val *object, const char *key_name, struct spdk_json_val **key,
	       struct spdk_json_val **val, enum spdk_json_val_type type)
{
	struct spdk_json_val *_key = NULL;
	struct spdk_json_val *_val = NULL;
	struct spdk_json_val *it;

	assert(object != NULL);

	for (it = spdk_json_first(object, SPDK_JSON_VAL_ARRAY_BEGIN | SPDK_JSON_VAL_OBJECT_BEGIN);
	     it != NULL;
	     it = spdk_json_next(it)) {
		if (it->type != SPDK_JSON_VAL_NAME) {
			continue;
		}

		if (spdk_json_strequal(it, key_name) != true) {
			continue;
		}

		if (_key) {
			SPDK_JSON_DEBUG("Duplicate key '%s'", key_name);
			return -EINVAL;
		}

		_key = it;
		_val = spdk_json_value(_key);

		if (type != SPDK_JSON_VAL_INVALID && (_val->type & type) == 0) {
			SPDK_JSON_DEBUG("key '%s' type is %#x but expected one of %#x\n", key_name, _val->type, type);
			return -EDOM;
		}
	}

	if (key) {
		*key = _key;
	}

	if (val) {
		*val = _val;
	}

	return _val ? 0 : -ENOENT;
}

int
spdk_json_find_string(struct spdk_json_val *object, const char *key_name,
		      struct spdk_json_val **key, struct spdk_json_val **val)
{
	return spdk_json_find(object, key_name, key, val, SPDK_JSON_VAL_STRING);
}

int
spdk_json_find_array(struct spdk_json_val *object, const char *key_name,
		     struct spdk_json_val **key, struct spdk_json_val **val)
{
	return spdk_json_find(object, key_name, key, val, SPDK_JSON_VAL_ARRAY_BEGIN);
}

struct spdk_json_val *
spdk_json_object_first(struct spdk_json_val *object)
{
	struct spdk_json_val *first = spdk_json_first(object, SPDK_JSON_VAL_OBJECT_BEGIN);

	/* Empty object? */
	return first && first->type != SPDK_JSON_VAL_OBJECT_END ? first : NULL;
}

struct spdk_json_val *
spdk_json_array_first(struct spdk_json_val *array_begin)
{
	struct spdk_json_val *first = spdk_json_first(array_begin, SPDK_JSON_VAL_ARRAY_BEGIN);

	/* Empty array? */
	return first && first->type != SPDK_JSON_VAL_ARRAY_END ? first : NULL;
}

static struct spdk_json_val *
spdk_json_skip_object_or_array(struct spdk_json_val *val)
{
	unsigned lvl;
	enum spdk_json_val_type end_type;
	struct spdk_json_val *it;

	if (val->type == SPDK_JSON_VAL_OBJECT_BEGIN) {
		end_type = SPDK_JSON_VAL_OBJECT_END;
	} else if (val->type == SPDK_JSON_VAL_ARRAY_BEGIN) {
		end_type = SPDK_JSON_VAL_ARRAY_END;
	} else {
		SPDK_JSON_DEBUG("Expected JSON object (%#x) or array (%#x) but got %#x\n",
				SPDK_JSON_VAL_OBJECT_BEGIN, SPDK_JSON_VAL_ARRAY_END, val->type);
		return NULL;
	}

	lvl = 1;
	for (it = val + 1; it->type != SPDK_JSON_VAL_INVALID && lvl != 0; it++) {
		if (it->type == val->type) {
			lvl++;
		} else if (it->type == end_type) {
			lvl--;
		}
	}

	/* if lvl != 0 we have invalid JSON object */
	if (lvl != 0) {
		SPDK_JSON_DEBUG("Can't find end of object (type: %#x): lvl (%u) != 0)\n", val->type, lvl);
		it = NULL;
	}

	return it;
}

struct spdk_json_val *
spdk_json_next(struct spdk_json_val *it)
{
	struct spdk_json_val *val, *next;

	switch (it->type) {
	case SPDK_JSON_VAL_NAME:
		val = spdk_json_value(it);
		next = spdk_json_next(val);
		break;

	/* We are in the middle of an array - get to next entry */
	case SPDK_JSON_VAL_NULL:
	case SPDK_JSON_VAL_TRUE:
	case SPDK_JSON_VAL_FALSE:
	case SPDK_JSON_VAL_NUMBER:
	case SPDK_JSON_VAL_STRING:
		val = it + 1;
		return val;

	case SPDK_JSON_VAL_ARRAY_BEGIN:
	case SPDK_JSON_VAL_OBJECT_BEGIN:
		next = spdk_json_skip_object_or_array(it);
		break;

	/* Can't go to the next object if started from the end of array or object */
	case SPDK_JSON_VAL_ARRAY_END:
	case SPDK_JSON_VAL_OBJECT_END:
	case SPDK_JSON_VAL_INVALID:
		return NULL;
	default:
		assert(false);
		return NULL;

	}

	/* EOF ? */
	if (next == NULL) {
		return NULL;
	}

	switch (next->type) {
	case SPDK_JSON_VAL_ARRAY_END:
	case SPDK_JSON_VAL_OBJECT_END:
	case SPDK_JSON_VAL_INVALID:
		return NULL;
	default:
		/* Next value */
		return next;
	}
}

SPDK_LOG_REGISTER_COMPONENT("json_util", SPDK_LOG_JSON_UTIL)