/*
    0bsolete JSON library
    Copyright (C) 2014-2022 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include <libziaint.h>

#include <zjson0.h>
#include <zstr.h>

#include <zdebug.h>
#include <eprintf.h>

#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <string.h>

static char *zjson0_get1(const char *str, int ofs, int len){
	char *ret, *c, *d;
	int backslash = 0, u, j;
	char s[10];

	if (len < 0) return NULL;
	if (len == 0) return g_strdup("");

	ret = g_strndup(str + ofs, len + 1);
	z_trim_end(ret);

	len = strlen(ret);
	if (ret[len - 1] == '"') ret[len - 1] = '\0';

	// apply backslashes
	for (c = ret, d = ret; *c != '\0'; c++){
		if (backslash){
			backslash = 0;
			switch (*c){
				case '\\':
					*d++ = '\\';
					break;
				case 'n':
					*d++ = '\n';
					break;
				case 'r':
					*d++ = '\r';
					break;
				case 'b':
					*d++ = '\b';
					break;
				case 'f':
					*d++ = '\f';
					break;
				case 't':
					*d++ = '\t';
					break;
				case '"':
					*d++ = '"';
					break;
				case '/':
					*d++ = '/';
					break;
				case 'u':
					for (j = 0; j < 4; j++){
						c++;
						if (*c == '\0') goto error;
						s[j] = *c;
					}
					s[j] = '\0';
					u = strtol(s, NULL, 16);
					if (u<0x80) *d++ = u;
					else if (u<0x800) *d++ = 192 + u / 64, *d++ = 128 + u % 64;
					else if (u - 0xd800u<0x800) goto error;
					else if (u<0x10000) *d++ = 224 + u / 4096, *d++ = 128 + u / 64 % 64, *d++ = 128 + u % 64;
					else if (u<0x110000) *d++ = 240 + u / 262144, *d++ = 128 + u / 4096 % 64, *d++ = 128 + u / 64 % 64, *d++ = 128 + u % 64;
					else goto error;
					break;
				error:
					error("zjson0: error parsing unicode string.\n");
					break;
			}
		}else{
			if (*c == '\\'){
				backslash = 1;
				continue;
			}
			*d++ = *c;
		}
	}
	*d = '\0';


	return ret;
}

char *zjson0_get_str(const char *str, int len, const char *path){
	char *ret = NULL, *key, *val;
	int i, level = 0, ko, kl, vo, vl, po;
	char *search = g_strdup(path);
	char *remains = NULL;
	//	char *xx  = zjson0_get1(str, 0, len);
	char *c;

	//char *cjcjc = g_strndup(str, len);
	//dbg("%s\n", cjcjc);
	//g_free(cjcjc);

	if (path == NULL) return NULL;

	if (len < 0) len = strlen(str);

	c = strchr(search, '.');
	if (c != NULL) {
		*c = '\0';
		remains = c + 1;
	}

	if (str[0] == '['){
		int pathint = atoi(path);
		int ib = 1;
		int idx = 0;
		int parcnt = 0;
		for (i = 1; i < len && ret == NULL; i++){
			switch (str[i]){
				case '{':
					parcnt++;
					break;
				case '}':
					parcnt--;
					break;
				case ',':
					if (parcnt > 0) break;
					if (idx == pathint){
						ret = zjson0_get_str(str + ib, i - ib, remains);
					}
					ib = i + 1;
					idx++;
					break;
			}
			if (parcnt == 0 && i + 1 >= len){
				if (idx == pathint){
					ret = zjson0_get_str(str + ib, i - ib, remains);
				}
			}
		}
		g_free(search);
		return ret;
	}

	ko = 0;
	for (i = 1; i < len && ret == NULL; i++){
		key = val = NULL;

		while (i < len && isspace((unsigned char)str[i])) i++;
		ko = i + 1;
		while (i < len && str[i] != ':') i++;
		kl = i - ko - 1;
		while (i < len && isspace((unsigned char)str[i])) i++;
		vo = i + 2;
		if (str[i + 1] == '{'){
			while (i < len && str[i] != ',' && str[i] != '{' && str[i] != '}') i++;
		}
		else if (str[i + 1] == '"'){
			int backslash = 0;
			for (i = i + 2; i < len; i++){
				if (backslash){
					backslash = 0;
				}
				else
				{
					switch (str[i]){
					case '\\':
						backslash = 1;
						continue;
					case '"':
						i++;
						goto found;
					}
				}
			}
		found:;
		} else if (str[i + 1] == '['){ // only for single dimensional arrays
			for (i = i + 2; i < len; i++){
				if (str[i] == ']') break;
			}
		}else{
			int backslash = 0;
			vo = i + 1;
			for (; i < len; i++){
				if (backslash){
					backslash = 0;
				}
				else
				{
					switch (str[i]){
					case '\\':
						backslash = 1;
						continue;
					case ',':
						goto found2;
					}
				}
			}
		found2:;
		}
		vl = i - vo - 1;
		key = zjson0_get1(str, ko, kl);
		val = zjson0_get1(str, vo, vl);
		if (val != NULL && strcmp(search, key) == 0 && remains && *remains){
			//dbg("pole\n");
			ret = zjson0_get_str(str + vo - 1, i - vo + 2, remains);	 // including []
		}else if (val != NULL && strcmp(search, key) == 0) {
			g_free(key);
			ret = val;
			break;
		}
		if (str[i] == '{') {
			po = i;
			level++;
			for (i++; i < len && level > 0; i++){
				if (str[i] == '{') level++;
				if (str[i] == '}') level--;
			}

			if (strcmp(search, key) == 0) ret = zjson0_get_str(str + po, i - po - 1, remains);
		}
		g_free(key);
		g_free(val);
	}
	g_free(search);
	//dbg("\n");
	return ret;
}

int zjson0_get_int(const char *str, int len, const char *path){
    char *c = zjson0_get_str(str, len, path);
    if (c == NULL) return 0;
    int ret = atoi(c);
    g_free(c);
    return ret;
}

void zjson0_test(void){
	char *key, *r, *u, *b, *s;
	char *val, *ok;

	//r = "{\"return\":\"Success\",\"rowids\":[7192,7193]}";
	r = "{\"rowids\":[7192,7193]}";
	char *rowids = zjson0_get_str(r, -1, "rowids");
	dbg("rowids = %p", rowids);


	r = "{\"song\":\"EJ, PADA, PADA, ROSENKA\"}";
	key = "song";
	val = zjson0_get_str(r, strlen(r), key);
	dbg("key='%s'  val='%s'\n", key, val);

	u = "{\"song\":\"VODOP\\u00c1D\"}";
	key = "song";
	val = zjson0_get_str(u, strlen(u), key);
	dbg("key='%s'  val='%s'\n", key, val);

	b = "{\"img\":\"https:\\/\\/is5-ssl.mzstatic.com\\/image\\/thumb\\/626x0w.jpg\"}";
	key = "img";
	ok = "https://is5-ssl.mzstatic.com/image/thumb/626x0w.jpg";
	val = zjson0_get_str(b, strlen(b), key);
	dbg("key='%s'  val='%s'\n", key, val); 
	assert(strcmp(val, ok) == 0);	 


	s = "{\"login\":{\"result\":\"NeedToken\",\"token\":\"b03a54f2c660eae532eaaab9a272973b\",\"cookieprefix\":\"wiki_krq\",\"sessionid\":\"99611b7e82e04d8a7e2542030d5f18a1\"},\"second\":\"secval\"}";

	key = "login.result";
	val = zjson0_get_str(s, strlen(s), key);
	dbg("key='%s'  val='%s'\n", key, val);

	key = "login.token";
	val = zjson0_get_str(s, strlen(s), key);
	dbg("key='%s'  val='%s'\n", key, val);

	key = "login";
	val = zjson0_get_str(s, strlen(s), key);
	dbg("key='%s'  val='%s'\n", key, val);

	key = "neni";
	val = zjson0_get_str(s, strlen(s), key);
	dbg("key='%s'  val='%s'\n", key, val);	 

	key = "login.sessionid";
	val = zjson0_get_str(s, strlen(s), key);
	dbg("key='%s'  val='%s'\n", key, val);

	key = "second";
	val = zjson0_get_str(s, strlen(s), key);
	dbg("key='%s'  val='%s'\n", key, val);
	  

	s = "{\"query\":{\"pages\":{\"-1\":{\"ns\":0,\"title\":\"Main Page\",\"missing\":\"\",\"starttimestamp\":\"2014-07-15T06:21:10Z\",\"edittoken\":\"43cf06841bc074e7922cece1617f1504+\\\\\"}}}}";
	key = "query.pages.-1.edittoken";
	val = zjson0_get_str(s, strlen(s), key);
	dbg("key='%s'  val='%s'\n", key, val);


	s = "{\"cmd\":\"cfg_set\",\"ch\":\"A\",\"dev\":\"\",\"key\":\"name\",\"val\":\"acko\"}";
	key = "dev";
	val = zjson0_get_str(s, strlen(s), key);
	dbg("key='%s'  val='%s'\n", key, val);

}



void zjson0_object_start(GString *gs, char *name){
	if (name != NULL) zg_string_eprintfa("j", gs, "\"%s\": ", name);
	g_string_append_c(gs, '{');

}

void zjson0_object_end(GString *gs){
    zjson0_strip(gs);
	g_string_append(gs, "},");
}

void zjson0_array_start(GString *gs, char *name){
	if (name != NULL) zg_string_eprintfa("j", gs, "\"%s\": ", name);
	g_string_append_c(gs, '[');

}

void zjson0_array_end(GString *gs){
    zjson0_strip(gs);
	g_string_append(gs, "],");
}


void zjson0_item_int(GString *gs, char *name, int value){
	if (name != NULL) zg_string_eprintfa("j", gs, "\"%s\": ", name);
	g_string_append_printf(gs, "%d, ", value);
}

void zjson0_item_int64(GString *gs, char *name, int64_t value){
	if (name != NULL) zg_string_eprintfa("j", gs, "\"%s\": ", name);
	g_string_append_printf(gs, "%lld, ", (long long)value);
}

void zjson0_item_double(GString *gs, char *name, double value, int places){
	if (name != NULL) zg_string_eprintfa("j", gs, "\"%s\": ", name);
    if (isnan(value)){
    	g_string_append_printf(gs, "null, ");
    }else{
    	g_string_append_printf(gs, "%0.*f, ", places, value);
    }
}

void zjson0_item_string(GString *gs, char *name, const char *value){
	if (name != NULL) zg_string_eprintfa("j", gs, "\"%s\": ", name);
	zg_string_eprintfa("j", gs, "\"%s\", ", value);
}

void zjson0_item_bool(GString *gs, char *name, int value){
	if (name != NULL) zg_string_eprintfa("j", gs, "\"%s\": ", name);
	g_string_append_printf(gs, "%s, ", value ? "true" : "false");
}

void zjson0_item_null(GString *gs, char *name){
	if (name != NULL) zg_string_eprintfa("j", gs, "\"%s\": ", name);
	g_string_append(gs, "null, ");
}

#ifndef Z_MSC
void zjson0_item_sql(GString *gs, char *name, time_t value){
    char s[30];
    struct tm tm;
	if (name != NULL) zg_string_eprintfa("j", gs, "\"%s\": ", name);

    gmtime_r(&value, &tm);
    strftime(s, sizeof(s), "%Y-%m-%dT%H:%M:%S", &tm);

	zg_string_eprintfa("j", gs, "\"%s\", ", s);
}
#endif

void zjson0_strip(GString *gs){
    int i;
	for (i = gs->len - 1; i >= 0; i--){
		if (gs->str[i] == ' ' || gs->str[i] == '\r' || gs->str[i] == '\n' || gs->str[i] == '\t') continue;
		if (gs->str[i] == ',') g_string_erase(gs, i, -1);
		break;
	}

}




