#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "grecs.h"
#include "grecs/jsonparse.h"

struct json_value *
grecs_json_parse_string(char const *input, size_t length,
			char const **errmsg, struct grecs_locus *errloc)
{
	struct json_value *v;
	int rc;
	char *endp;

	rc = json_parse(input, length, &v, &endp);
	if (rc != JSON_E_NOERR) {
		if (errloc) {
			struct grecs_locus_point pt;
			char const *p;

			pt.file = errloc->beg.file;
			pt.line = 1;
			pt.col = 0;
			for (p = input; p != endp; p++) {
				if (*p == '\n')
					grecs_locus_point_advance_line(pt);
				else
					pt.col++;
			}
			errloc->beg = errloc->end = pt;
		}
		if (errmsg)
			*errmsg = json_strerror(rc);
		return NULL;
	}
	return v;
}

int
json_unescape(char c, char *o)
{
	static char transtab[] = "\\\\\"\"b\bf\fn\nr\rt\t";
	char *p;

	for (p = transtab; *p; p += 2) {
		if (p[0] == c) {
			*o = p[1];
			return 0;
		}
	}
	return -1;
}

struct json_value *
json_value_lookup(struct json_value *obj, const char *ident)
{
	char *qbuf = NULL;
	char const *qptr;
	size_t qlen = 0;

	while (obj && *ident) {
		char const *p;
		char *q;
		size_t l;

		for (p = ident; *p; p++) {
			if (*p == '\\')
				++p;
			else if (*p == '.')
				break;
		}
		if (*p == 0) {
			qptr = ident;
			ident = p;
		} else {
			l = p - ident + 1;
			if (l > qlen) {
				void *t = realloc(qbuf, l);
				if (t) {
					qbuf = t;
					qlen = l;
				} else {
					free(qbuf);
					return NULL;
				}
			}
			qptr = q = qbuf;
			while (*ident) {
				if (*ident == '\\') {
					char c;
					++ident;
					if (json_unescape(*ident, &c))
						*q++ = *ident++;
					else
						*q++ = c;
				} else if (*ident == '.') {
					++ident;
					break;
				} else
					*q++ = *ident++;
			}
			*q = 0;
		}

		switch (obj->type) {
		case json_null:
		case json_bool:
		case json_double:
		case json_long:
		case json_string:
			obj = NULL;
			break;
		case json_array:
			l = strtoul(qptr, &q, 10);
			if (*q != 0 || json_array_get(obj, l, &obj))
				obj = NULL;
			break;
		case json_object:
			if (json_object_get(obj, qptr, &obj))
				obj = NULL;
		}
	}
	if (*ident)
		obj = NULL;
	free(qbuf);
	if (!obj)
		errno = 0;
	return obj;
}
