/*
 * Read and write JSON.
 *
 * Copyright (c) 2014  Marko Kreen
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <usual/json.h>
#include <usual/cxextra.h>
#include <usual/cbtree.h>
#include <usual/misc.h>
#include <usual/utf8.h>
#include <usual/ctype.h>
#include <usual/bytemap.h>
#include <usual/string.h>
#include <math.h>

#define TYPE_BITS	3
#define TYPE_MASK	((1 << TYPE_BITS) - 1)
#define UNATTACHED	((struct JsonValue *)(1 << TYPE_BITS))

#define JSON_MAX_KEY	(1024*1024)

#define NUMBER_BUF	100

#define JSON_MAXINT	((1LL << 53) - 1)
#define JSON_MININT	(-(1LL << 53) + 1)

/*
 * Common struct for all JSON values
 */
struct JsonValue {
	/* actual value for simple types */
	union {
		double v_float;			/* float */
		int64_t v_int;			/* int */
		bool v_bool;			/* bool */
		size_t v_size;			/* str/list/dict */
	} u;

	/* pointer to next elem and type in low bits */
	uintptr_t v_next_and_type;
};

/*
 * List container.
 */
struct ValueList {
	struct JsonValue *first;
	struct JsonValue *last;
	struct JsonValue **array;
};

/*
 * Extra data for list/dict.
 */
struct JsonContainer {
	/* parent container */
	struct JsonValue *c_parent;

	/* main context for child alloc */
	struct JsonContext *c_ctx;

	/* child elements */
	union {
		struct CBTree *c_dict;
		struct ValueList c_list;
	} u;
};

#define DICT_EXTRA (offsetof(struct JsonContainer, u.c_dict) + sizeof(struct CBTree *))
#define LIST_EXTRA (sizeof(struct JsonContainer))

/*
 * Allocation context.
 */
struct JsonContext {
	CxMem *pool;
	unsigned int options;

	/* parse state */
	struct JsonValue *parent;
	struct JsonValue *cur_key;
	struct JsonValue *top;
	const char *lasterr;
	char errbuf[128];
	int64_t linenr;
};

struct RenderState {
	struct MBuf *dst;
	unsigned int options;
};

/*
 * Parser states
 */
enum ParseState {
	S_INITIAL_VALUE = 1,
	S_LIST_VALUE,
	S_LIST_VALUE_OR_CLOSE,
	S_LIST_COMMA_OR_CLOSE,
	S_DICT_KEY,
	S_DICT_KEY_OR_CLOSE,
	S_DICT_COLON,
	S_DICT_VALUE,
	S_DICT_COMMA_OR_CLOSE,
	S_PARENT,
	S_DONE,
	MAX_STATES,
};

/*
 * Tokens that change state.
 */
enum TokenTypes {
	T_STRING,
	T_OTHER,
	T_COMMA,
	T_COLON,
	T_OPEN_DICT,
	T_OPEN_LIST,
	T_CLOSE_DICT,
	T_CLOSE_LIST,
	MAX_TOKENS
};

/*
 * 4-byte ints for small string tokens.
 */

#define C_NULL FOURCC('n','u','l','l')
#define C_TRUE FOURCC('t','r','u','e')
#define C_ALSE FOURCC('a','l','s','e')

/*
 * Signature for render functions.
 */
typedef bool (*render_func_t)(struct RenderState *rs, struct JsonValue *jv);

static bool render_any(struct RenderState *rs, struct JsonValue *jv);

/*
 * Header manipulation
 */

static inline enum JsonValueType get_type(struct JsonValue *jv)
{
	return jv->v_next_and_type & TYPE_MASK;
}

static inline bool has_type(struct JsonValue *jv, enum JsonValueType type)
{
	if (!jv)
		return false;
	return get_type(jv) == type;
}

static inline struct JsonValue *get_next(struct JsonValue *jv)
{
	return (struct JsonValue *)(jv->v_next_and_type & ~(uintptr_t)TYPE_MASK);
}

static inline void set_next(struct JsonValue *jv, struct JsonValue *next)
{
	jv->v_next_and_type = (uintptr_t)next | get_type(jv);
}

static inline bool is_unattached(struct JsonValue *jv)
{
	return get_next(jv) == UNATTACHED;
}

static inline void *get_extra(struct JsonValue *jv)
{
	return (void *)(jv + 1);
}

static inline char *get_cstring(struct JsonValue *jv)
{
	enum JsonValueType type = get_type(jv);
	if (type != JSON_STRING)
		return NULL;
	return get_extra(jv);
}

/*
 * Collection header manipulation.
 */

static inline struct JsonContainer *get_container(struct JsonValue *jv)
{
	enum JsonValueType type = get_type(jv);
	if (type != JSON_DICT && type != JSON_LIST)
		return NULL;
	return get_extra(jv);
}

static inline void set_parent(struct JsonValue *jv, struct JsonValue *parent)
{
	struct JsonContainer *c = get_container(jv);
	if (c)
		c->c_parent = parent;
}

static inline struct JsonContext *get_context(struct JsonValue *jv)
{
	struct JsonContainer *c = get_container(jv);
	return c ? c->c_ctx : NULL;
}

static inline struct CBTree *get_dict_tree(struct JsonValue *jv)
{
	struct JsonContainer *c;
	if (has_type(jv, JSON_DICT)) {
		c = get_container(jv);
		return c->u.c_dict;
	}
	return NULL;
}

static inline struct ValueList *get_list_vlist(struct JsonValue *jv)
{
	struct JsonContainer *c;
	if (has_type(jv, JSON_LIST)) {
		c = get_container(jv);
		return &c->u.c_list;
	}
	return NULL;
}

/*
 * Random helpers
 */

/* copy and return final pointer */
static inline char *plain_copy(char *dst, const char *src, const char *endptr)
{
	if (src < endptr) {
		memcpy(dst, src, endptr - src);
		return dst + (endptr - src);
	}
	return dst;
}

/* error message on context */
_PRINTF(2,0)
static void format_err(struct JsonContext *ctx, const char *errmsg, va_list ap)
{
	char buf[119];
	if (ctx->lasterr)
		return;
	vsnprintf(buf, sizeof(buf), errmsg, ap);
	snprintf(ctx->errbuf, sizeof(ctx->errbuf), "Line #%" PRIi64 ": %s", ctx->linenr, buf);
	ctx->lasterr = ctx->errbuf;
}

/* set message and return false */
_PRINTF(2,3)
static bool err_false(struct JsonContext *ctx, const char *errmsg, ...)
{
	va_list ap;
	va_start(ap, errmsg);
	format_err(ctx, errmsg, ap);
	va_end(ap);
	return false;
}

/* set message and return NULL */
_PRINTF(2,3)
static void *err_null(struct JsonContext *ctx, const char *errmsg, ...)
{
	va_list ap;
	va_start(ap, errmsg);
	format_err(ctx, errmsg, ap);
	va_end(ap);
	return NULL;
}

/* callback for cbtree, returns key bytes */
static size_t get_key_data_cb(void *dictptr, void *keyptr, const void **dst_p)
{
	struct JsonValue *key = keyptr;
	*dst_p = get_cstring(key);
	return key->u.v_size;
}

/* add elemnt to list */
static void real_list_append(struct JsonValue *list, struct JsonValue *elem)
{
	struct ValueList *vlist;

	vlist = get_list_vlist(list);
	if (vlist->last) {
		set_next(vlist->last, elem);
	} else {
		vlist->first = elem;
	}
	vlist->last = elem;
	vlist->array = NULL;

	list->u.v_size++;
}

/* add key to tree */
static bool real_dict_add_key(struct JsonContext *ctx, struct JsonValue *dict, struct JsonValue *key)
{
	struct CBTree *tree;

	tree = get_dict_tree(dict);
	if (!tree)
		return err_false(ctx, "Expect dict");

	if (json_value_size(key) > JSON_MAX_KEY)
		return err_false(ctx, "Too large key");

	dict->u.v_size++;
	if (!cbtree_insert(tree, key))
		return err_false(ctx, "Key insertion failed");

	return true;
}

/* create basic value struct, link to stuctures */
static struct JsonValue *mk_value(struct JsonContext *ctx, enum JsonValueType type, size_t extra, bool attach)
{
	struct JsonValue *val;
	struct JsonContainer *col = NULL;

	if (!ctx)
		return NULL;

	val = cx_alloc(ctx->pool, sizeof(struct JsonValue) + extra);
	if (!val)
		return err_null(ctx, "No memory");
	if ((uintptr_t)val & TYPE_MASK)
		return err_null(ctx, "Unaligned pointer");

	/* initial value */
	val->v_next_and_type = type;
	val->u.v_int = 0;

	if (type == JSON_DICT || type == JSON_LIST) {
		col = get_container(val);
		col->c_ctx = ctx;
		col