#define _XOPEN_SOURCE
#define __USE_MISC

#include <libziaint.h>

#include <zdebug.h>
#include <zjson.h>
#include <zstr.h>

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

//#include <errno.h>
//#include <ZFile.h>
//#include <ZTime.h>


void zjson_begin_object(struct zjson *json){
    json->type = "{";
    g_string_append(json->gs, "{");
	json->aa = json->gs->str;
}

void zjson_begin_array(struct zjson *json){
    json->type = "[";
	g_string_append(json->gs, "[");
	json->aa = json->gs->str;
}

void zjson_end(struct zjson *json){
    if (!json->type) return;

	if (json->type[0] == '{'){
		g_string_append(json->gs, "}");
    }else if (json->type[0] == '['){
		g_string_append(json->gs, "]");
    }
    json->type = NULL;
	json->aa = json->gs->str;
}

void zjson_concatEscaped(struct zjson *json, const char *str){
    for (const char *c = str; *c != '\0'; c++){
        switch (*c) {
            case '\\':
            case '"':
				g_string_append(json->gs, "\\");
				g_string_append_c(json->gs, *c);
                break;
            case '\b':
				g_string_append(json->gs, "\\b");
                break;
            case '\t':
				g_string_append(json->gs, "\\t");
                break;
            case '\n':
				g_string_append(json->gs, "\\n");
                break;
            case '\f':
				g_string_append(json->gs, "\\f");
                break;
            case '\r':
				g_string_append(json->gs, "\\r");
                break;
            default:
                if (((unsigned char)*c) < ' ') {
                    char s2[30];
                    sprintf(s2, "\\u%04x", (unsigned char)*c);
					g_string_append(json->gs, s2);
                } else {
					g_string_append_c(json->gs, *c);
                }
        }
    }
	json->aa = json->gs->str;
}

void zjson_add_private(struct zjson *json, const char *key, const char *value, bool asStr, bool raw){
	if (json->gs->len > 0 && json->gs->str[json->gs->len - 1] != json->type[0]) g_string_append(json->gs, ", ");
    if (key != NULL){
		g_string_append(json->gs, "\"");
        zjson_concatEscaped(json, key);
        g_string_append(json->gs, "\":");
    }
	if (asStr) g_string_append(json->gs, "\"");
    if (raw){
		g_string_append(json->gs, value);
    }else{
		zjson_concatEscaped(json, value);
    }
	if (asStr) g_string_append(json->gs, "\"");
	json->aa = json->gs->str;
}


void zjson_add_str(struct zjson *json, const char *key, const char *value){
	zjson_add_private(json, key, value, true, false);
}

void zjson_add_c(struct zjson *json, const char *key, const char value){
    char s[2];
    s[0] = value;
    s[1] = '\0';
	zjson_add_private(json, key, s, true, true);
}

void zjson_add_bool(struct zjson *json, const char *key, const bool value){
	zjson_add_private(json, key, value ? "true" : "false", false, false);
}

void zjson_add_int(struct zjson *json, const char *key, const int value){
    char s[30];
    g_snprintf(s, sizeof(s), "%d", value);
	zjson_add_private(json, key, s, false, false);
}

void zjson_add_long(struct zjson *json, const char *key, const long value){
    char s[30];
	g_snprintf(s, sizeof(s), "%ld", value);
	zjson_add_private(json, key, s, false, false);
}

void zjson_add_epoch(struct zjson *json, const char *key, time_t value, unsigned int places){
    char s[30];
	g_snprintf(s, sizeof(s), "%0*lld", places, (long long)value);
	zjson_add_private(json, key, s, false, false);
}

#ifdef Z_UNIX_ANDROID
void zjson_add_sql(struct zjson *json, const char *key, time_t value){
    char s[30];
    struct tm tm;
    gmtime_r(&value, &tm);
    strftime(s, sizeof(s), "%Y-%m-%dT%H:%M:%S", &tm);
	zjson_add_private(json, key, s, false, false);
}
#endif

void zjson_add_float(struct zjson *json, const char *key, const float value, unsigned int places){
    bool n = isnan(value);
    if (n){
        return;
    }
	char s[30];
	g_snprintf(s, sizeof(s), "%.*f", places, value);
	zjson_add_private(json, key, s, false, false);
}

void zjson_add_double(struct zjson *json, const char *key, const double value, unsigned int places){
    bool n = isnan(value);
    if (n){
        return;
    }
	char s[30];
	g_snprintf(s, sizeof(s), "%.*f", places, value);
	zjson_add_private(json, key, s, false, false);
}

void zjson_add_json(struct zjson *json, const char *key, const struct zjson *src){
	zjson_add_private(json, key, json->gs->str, false, true);
}

void zjson_add_mac(struct zjson *json, const char *key, const uint8_t *mac){
    char s[20];
	g_snprintf(s, sizeof(s), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
	zjson_add_private(json, key, s, true, false);
}




void zjson_addln(struct zjson *json){
    g_string_append(json->gs, "\r\n");
	json->aa = json->gs->str;
}





struct zjson *zjson_init(const char *src){
	struct zjson *json = g_new0(struct zjson, 1);
	json->gs = g_string_new("");
    zjson_begin(json, src);
	return json;
}


void zjson_begin(struct zjson *json, const char *src){
	if (src == NULL) src = "";
	g_string_assign(json->gs, src);
    zjson_init_parse(json);
	json->aa = json->gs->str;
}

void zjson_free(struct zjson *json){
	g_string_free(json->gs, TRUE);
	g_free(json);
}



void zjson_init_parse(struct zjson *json){
	json->c = json->gs->str;
	json->level = 0;
}

char *zjson_get1(struct zjson *json, bool escape){
    if (*json->c == '\0') return false;
	GString *ret = g_string_new("");

	while (*json->c == '\t' || *json->c == '\r' || *json->c == '\n' || *json->c == ' ') json->c++;
   
    bool isString = false, wasString = false;

	bool backslash = false;
    int j, u;
    char s[10];
	for (; *json->c != '\0'; json->c++){
		if (backslash){
			backslash = 0;
			switch (*json->c){
				case '\\':
					g_string_append(ret, "\\");
					break;
				case 'n':
					g_string_append(ret, "\n");
					break;
				case 'r':
					g_string_append(ret, "\r");
					break;
				case 'b':
					g_string_append(ret, "\b");
					break;
				case 'f':
					g_string_append(ret, "\f");
					break;
				case 't':
					g_string_append(ret, "\t");
					break;
				case '"':
					g_string_append(ret, "\"");
					break;
				case '/':
					g_string_append(ret, "\\");
					break;
				case 'u':
					for (j = 0; j < 4; j++){
						json->c++;
						if (*json->c == '\0') {
							g_string_free(ret, TRUE);
							return NULL;
						}
						s[j] = *json->c;
					}
					s[j] = '\0';
					u = strtol(s, NULL, 16);
					if (u < 0x80) {
						g_string_append_c(ret, u);
					}
					else if (u < 0x800) {
						g_string_append_c(ret, 192 + u / 64);
						g_string_append_c(ret, 128 + u % 64);
					}
					else if (u - 0xd800u < 0x800) {
						g_string_free(ret, TRUE);
						return NULL;
					}
					else if (u < 0x10000) { 
						g_string_append_c(ret, u / 4096);
						g_string_append_c(ret, 128 + u / 64 % 64);
						g_string_append_c(ret, 128 + u % 64);
					}
					else if (u < 0x110000) {
						g_string_append_c(ret, 240 + u / 262144);
						g_string_append_c(ret, 128 + u / 4096 % 64);
						g_string_append_c(ret, 128 + u / 64 % 64);
						g_string_append_c(ret, 128 + u % 64);
					}
					else {
						g_string_free(ret, TRUE);
						return NULL;
					}
			}
		}else{
			if (*json->c == '\\' && escape){
				backslash = 1;
				continue;
			}
			if (*json->c == '"'){
                if (isString){
                    isString = false;
                    if (escape){
						if (json->level == 1) continue;
                    }
                }else{
					wasString = isString = escape || json->level == 1;
					if (json->level == 1) {
                        if (escape){
                            g_string_assign(ret, "");
                            continue;
                        }
                    }
                }
            }
          
            if (!isString){
				if (*json->c == ',') {
					if (json->level == 1){
                        json->c++;
                        //printf("get1a1 '%s'  wasString='%d'\n", ret.c_str(), wasString);
                        if (!wasString) {
							zg_string_trim(ret);
                            if (strcmp(ret->str, "null") == 0) g_string_assign(ret, "");
                        }
                        wasString = false;
                        //printf("get1a2 '%s'\n", ret.c_str());
						char *ret2 = ret->str;
						g_string_free(ret, FALSE);
						return ret2;
                    }
                }
				if (*json->c == ':') {
					if (json->level == 1){
						json->c++;
                        if (!wasString) {
							zg_string_trim(ret);
                        }
                        wasString = false;

                        //printf("get1b '%s'\n", ret.c_str());
						char *ret2 = ret->str;
						g_string_free(ret, FALSE);
                        return ret2;
                    }
                }
				if (*json->c == '{' || *json->c == '['){
					json->level++;
					if (json->level == 1){
                        continue;
                    }
                }
				if (*json->c == '}' || *json->c == ']') {
					json->level--;
					if (json->level == 1){
						if (!wasString) zg_string_trim(ret);
                        wasString = false;
                    }
					if (json->level == 0){
						if (!wasString) {
							zg_string_trim(ret);
							if (strcmp(ret->str, "null") == 0) g_string_assign(ret, "");
						}
                        //printf("get1c '%s'\n", ret.c_str());
						char *ret2 = ret->str;
						g_string_free(ret, FALSE);
						return ret2;
                    }
                }
				if (wasString && json->level == 1 && (*json->c == '\t' || *json->c == '\r' || *json->c == '\n' || *json->c == ' ')) continue;
            }
			g_string_append_c(ret, *json->c);
		}
	}
	g_string_free(ret, TRUE);
	return false;
}

char *zjson_get_private(struct zjson *json, const char *key, bool escape){
    char *k = NULL, *v = NULL;
    if (key != NULL) zjson_init_parse(json);
    while(true){
        if (key != NULL){
			k = zjson_get1(json, true);
            if (!k) return NULL;
        }
		v = zjson_get1(json, escape);
		if (!v){
			if (k) g_free(k);
			return NULL;
		}
        //printf("key=%s   value=%s\n", k.ss, v.ss);
        if ((k != NULL && strcmp(k, key) == 0) || key == NULL){
			if (k) g_free(k);
			return v;
        }
		g_free(k); k = NULL;
		g_free(v); v = NULL;
    }
    return NULL;
}

char *zjson_get_str(struct zjson *json, const char *key, const char *def){
	char *ret = zjson_get_private(json, key, true);
	if (!ret){
		return g_strdup(def);
	}
	return ret;
}

char zjson_get_c(struct zjson *json, const char *key, char def){
	char *s = zjson_get_private(json, key, true);
	char ret = def;
	if (s != NULL){
		ret = *s;
		g_free(s);
	}
    return ret;
}


bool zjson_get_bool(struct zjson *json, const char *key, bool def){
	char *s = zjson_get_private(json, key, true);
	bool ret = def;
	if (s != NULL){
		ret = strcmp(s, "true") == 0;
		g_free(s);
	}
	return ret;
}

int zjson_get_int(struct zjson *json, const char *key, int def){
	char *s = zjson_get_private(json, key, true);
	int ret = def;
	if (s != NULL){
		ret = atoi(s);
		g_free(s);
	}
	return ret;
}

long zjson_get_long(struct zjson *json, const char *key, long def){
	char *s = zjson_get_private(json, key, true);
	long ret = def;
	if (s != NULL){
		ret = atol(s);
		g_free(s);
	}
	return ret;
}


uint8_t *zjson_get_mac(struct zjson *json, const char *key, const uint8_t *def){
	char *s = zjson_get_private(json, key, true);
	if (s == NULL){
		return zg_memdup(def, 6);
	}else{
		char *buf = g_strdup(s);
		uint8_t *mac = g_new0(uint8_t, 6);
		for (int i = 0; i < 6; i++){
			char *c = strtok(i == 0 ? buf : NULL, ":-");
			if (c == NULL) {
				g_free(s);
				g_free(buf);
				g_free(mac);
				return zg_memdup(def, 6);
			}
			long l = strtol(c, NULL, 16);
			mac[i] = (uint8_t)l;
		}
		g_free(s);
		g_free(buf);
		return mac;
	}
}

time_t zjson_get_epoch(struct zjson *json, const char *key, time_t def){
	time_t ret = (time_t)zjson_get_long(json, key, (long)def);
	return ret;
}

/*#ifdef Z_UNIX_ANDROID
time_t zjson_get_sql(struct zjson *json, const char *key, time_t def){
	char *s = zjson_get_private(json, key, true);
	time_t ret = def;
	if (s != NULL){
		struct tm tm;
		char *p = strptime(s, "%Y-%m-%dT%H:%M:%S", &tm);
		ret = timegm(&tm);
		g_free(s);
	}
	return ret;
}
#endif*/

float zjson_get_float(struct zjson *json, const char *key, float def){
	char *s = zjson_get_private(json, key, true);
	float ret = def;
	if (s != NULL){
		ret = (float)atof(s);
		g_free(s);
	}
	return ret;
}

double zjson_get_double(struct zjson *json, const char *key, double def){
	char *s = zjson_get_private(json, key, true);
	double ret = def;
	if (s != NULL){
		ret = atof(s);
		g_free(s);
	}
	return ret;
}



struct zjson *zjson_get_object(struct zjson *json, const char *key){
	char *s = zjson_get_private(json, key, false);
	if (s == NULL){
		return NULL;
	}
	
	struct zjson *ret = zjson_init(s);
	g_free(s);
    return ret;
}

struct zjson *zjson_get_array(struct zjson *json, const char *key){
	struct zjson *ret = zjson_get_object(json, key);
    return ret;
}

int zjson_array_length(struct zjson *json){
    int len = 0;
    while(true){
		char *s = zjson_get_private(json, NULL, false);
		if (s == NULL) break;
		g_free(s);
        len++;
    }    
    return len;
}


///////////////////////////////////////////////////////
void zjson_test(void){
	char *key;
	char *val;// , *ok;

	struct zjson *r = zjson_init("{\"rowids\":[7192,7193]}");
	struct zjson *rowids = zjson_get_array(r, "rowids");
	dbg("rowids = %p", rowids);
	g_free(rowids);
	zjson_free(r);


	r = zjson_init("{\"song\":\"EJ, PADA, PADA, ROSENKA\"}");
	key = "song";
	val = zjson_get_str(r, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	g_free(val);
	zjson_free(r);

	struct zjson *u = zjson_init("{\"song\":\"VODOP\\u00c1D\"}");
	key = "song";
	val = zjson_get_str(u, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	g_free(val);
	zjson_free(u);

	/*struct zjson *b = zjson_init("{\"img\":\"https:\\/\\/is5-ssl.mzstatic.com\\/image\\/thumb\\/626x0w.jpg\"}");
	key = "img";
	ok = "https://is5-ssl.mzstatic.com/image/thumb/626x0w.jpg";
	val = zjson_get_str(b, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	assert(strcmp(val, ok) == 0);
	g_free(val);
	zjson_free(b);*/


	struct zjson *s = zjson_init("{\"login\":{\"result\":\"NeedToken\",\"token\":\"b03a54f2c660eae532eaaab9a272973b\",\"cookieprefix\":\"wiki_krq\",\"sessionid\":\"99611b7e82e04d8a7e2542030d5f18a1\"},\"second\":\"secval\"}");

	key = "login.result";
	val = zjson_get_str(s, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	g_free(val);

	key = "login.token";
	val = zjson_get_str(s, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	g_free(val);

	key = "login";
	val = zjson_get_str(s, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	g_free(val);

	key = "neni";
	val = zjson_get_str(s, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	g_free(val);

	key = "login.sessionid";
	val = zjson_get_str(s, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	g_free(val);

	key = "second";
	val = zjson_get_str(s, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	g_free(val);
	zjson_free(s);


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


	s = zjson_init("{\"cmd\":\"cfg_set\",\"ch\":\"A\",\"dev\":\"\",\"key\":\"name\",\"val\":\"acko\"}");
	key = "dev";
	val = zjson_get_str(s, key, NULL);
	dbg("key='%s'  val='%s'\n", key, val);
	g_free(val);
	zjson_free(s);

}
