/* sdb - MIT - Copyright 2012-2018 - pancake */

#include <limits.h>

static void doIndent(int idt, char **o, const char *tab) {
	int i;
	char *x;
	for (i = 0; i < idt; i++) {
		for (x = (char *) tab; *x; x++) {
			*(*o)++ = *x;
		}
	}
}

SDB_API char *sdb_json_indent(const char *s, const char *tab) {
	int idx, indent = 0;
	int instr = 0;
	int sz_instr = 0;
	size_t o_size = 0;
	char *o, *O;
	if (!s) {
		return NULL;
	}

	size_t tab_len = strlen (tab);
	for (idx = 0; s[idx]; idx++) {
		if (sz_instr) {
			if (s[idx] == '"') {
				sz_instr = 0;
			} else if (s[idx] == '\\' && s[idx + 1] == '"') {
				if (o_size == SIZE_MAX) {
					return NULL;
				}
				o_size++;
			}
			if (o_size == SIZE_MAX) {
				return NULL;
			}
			o_size++;
			continue;
		} else if (s[idx] == '"') {
			sz_instr = 1;
		}

		if (o_size > INT_MAX - (indent * tab_len + 2)) {
			return NULL;
		}

		if (s[idx] == '{' || s[idx] == '[') {
			indent++;
			// 2 corresponds to the \n and the parenthesis
			o_size += indent * tab_len + 2;
		} else if (s[idx] == '}' || s[idx] == ']') {
			if (indent > 0) {
				indent--;
			}
			// 2 corresponds to the \n and the parenthesis
			o_size += indent * tab_len + 2;
		} else if (s[idx] == ',') {
			// 2 corresponds to the \n and the ,
			o_size += indent * tab_len + 2;
		} else if (s[idx] == ':') {
			o_size += 2;
		} else {
			o_size++;
		}
	}
	// 2 corresponds to the last \n and \0
	o_size += 2;
	indent = 0;

	O = (char *)sdb_gh_malloc (o_size + 1);
	if (!O) {
		return NULL;
	}

	for (o = O; *s; s++) {
		if (instr) {
			if (s[0] == '"') {
				instr = 0;
			} else if (s[0] == '\\' && s[1] == '"') {
				*o++ = *s;
			}
			*o++ = *s;
			continue;
		} else {
			if (s[0] == '"') {
				instr = 1;
			}
		}
		if (*s == '\n' || *s == '\r' || *s == '\t' || *s == ' ') {
			continue;
		}
		switch (*s) {
		case ':':
			*o++ = *s;
			*o++ = ' ';
			break;
		case ',':
			*o++ = *s;
			*o++ = '\n';
			doIndent (indent, &o, tab);
			break;
		case '{':
		case '[':
			*o++ = *s;
			*o++ = (indent != -1)? '\n': ' ';
			indent++;
			doIndent (indent, &o, tab);
			break;
		case '}':
		case ']':
			*o++ = '\n';
			indent--;
			doIndent (indent, &o, tab);
			*o++ = *s;
			break;
		default:
			*o++ = *s;
		}
	}
	*o++ = '\n';
	*o = 0;

	return O;
}

// TODO: move to utils?
SDB_API char *sdb_json_unindent(const char *s) {
	int instr = 0;
	int len = strlen (s);
	char *o, *O = (char *)sdb_gh_malloc (len + 1);
	if (!O) {
		return NULL;
	}
	memset (O, 0, len);
	for (o = O; *s; s++) {
		if (instr) {
			if (s[0] != '"') {
				if (s[0] == '\\' && s[1] == '"') {
					*o++ = *s;
				}
			} else {
				instr = 0;
			}
			*o++ = *s;
			continue;
		} else if (s[0] == '"') {
			instr = 1;
		}
		if (*s == '\n' || *s == '\r' || *s == '\t' || *s == ' ') {
			continue;
		}
		*o++ = *s;
	}
	*o = 0;
	return O;
}
