// Jasic JSON (JavaScript Object Notation) formatted string manipulation from C
// This is a 'nice' thing to do in javascript but not really in C, this does allow some access from C anyway 
//  (intended primarily for addressbook import / export)

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#define DMID 14
#include "debug.h"
#include "lprintf.h"

#include "json.h"

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

extern int emsg(const char * arg_list, ...);
//#define dmsg imsg_surgeweb

char *json_token_var(char **upto, int endit);
char *json_token_val(char **upto, int endit);
char *json_token_obj(char **upto);


// Very basic routine to get first matching value from JSON stored data
//   bit of a hack and to be replace by following routines that actually do a pretty good job of parsing...
char *json_basicvalue(char *val, char *field, char *bf)
{
	char tmp[BFSZ],*s,*pin,*pout=bf,last=0;
	bf[0]='\0';
	if(val==NULL) goto done;
	if(!(val[0]=='{'||val[0]=='[')){
		ncpy(bf,val,BFSZ-1);
		goto done;
	}

	lprintf(tmp,BFSZ,"'%s':'",field);
	s=strstr(val,tmp);
	if (s==NULL) goto done;
	for(pin=s+lib_strlen(tmp);*pin;pin++){
		if((*pin=='\'' && last!='\\') || (pout-bf>=BFSZ-6)){
			*pout='\0';
			break;
		}
		*pout++=*pin;
		last=*pin;
	}
done:
	return bf;
}
// Get one named field from a JSON object string eg "{'tag':'value','tag2':'value2'}"
char *json_object_getfield(char *obj, char *field, char *out)
{
	char *var,*val,*upto,bf[BFSZ];
	out[0]='\0';
	ncpy(bf,obj,BFSZ-1);
	upto=bf;
	if(*upto++!='{') goto error;
	for(;*upto;){
		if(*upto=='}') break;
		var=json_token_var(&upto,TRUE);
		if(!var) goto error;
		val=json_token_val(&upto,TRUE);
		if(!val) goto error;
		if(strcmpnc(var,field)==0){
			ncpy(out,val,BFSZ-1);
			return out;
		}
	}	
	return NULL;
error:
	return NULL;
}
// Update one named field from a JSON object string eg "{'tag':'value','tag2':'value2'}"
int json_object_setfield(char *obj, char *field, char *value, char *real_out)
{
	char *var,*val,*upto,bf[BFSZ],bf2[BFSZ],*out=bf2;
	int first=TRUE,done=FALSE;
	bf2[0]='\0';
	ncpy(bf,obj,BFSZ-1);
	upto=bf;
	if(*upto!='{') goto error;
	*out++=*upto++;
	for(;*upto;){
		if(*upto=='}') break;
		var=json_token_var(&upto,TRUE);
		if(!var) goto error;
		val=json_token_val(&upto,TRUE);
		if(!val) goto error;
		if(strcmpnc(var,field)==0){
			lprintf(out,BFSZ-(int)(out-bf2),"%s'%s':'%s'",first?"":",",var,value);
			done=TRUE;
		}else{
			lprintf(out,BFSZ-(int)(out-bf2),"%s'%s':'%s'",first?"":",",var,val);
		}
		out=bf2+lib_strlen(bf2);
		first=FALSE;
	}
	if(!done){
		lprintf(out,BFSZ-(int)(out-bf2),"%s'%s':'%s'",first?"":",",field,value);
	}
	ncat(bf2,"}",BFSZ-1);
	ncpy(real_out,bf2,BFSZ-1);
	return TRUE;
error:
	return FALSE;
}
// Update one named field from a JSON object string eg "{'tag':{...},'tag2':{...}}"
int json_object_setfield_object(char *obj, char *field, char *value, char *real_out)
{
	char *var,*val,*upto,bf[BFSZ],bf2[BFSZ],*out=bf2,tmp[BFSZ];
	int first=TRUE,done=FALSE;
	bf2[0]='\0';
	ncpy(bf,obj,BFSZ-1);
	upto=bf;
	if(*upto!='{') goto error;
	*out++=*upto++;
	for(;*upto;){
		if(*upto=='}') break;
		var=json_token_var(&upto,TRUE);
		if(!var) goto error;
		val=json_token_obj(&upto);
		if(!val) goto error;
		if(strcmpnc(var,field)==0){
			lprintf(out,BFSZ-(int)(out-bf2),"%s'%s':%s",first?"":",",var,value);
			done=TRUE;
		}else{
			ncpy(tmp,val,BFSZ-1);
			tmp[upto-val]='\0';
			lprintf(out,BFSZ-(int)(out-bf2),"%s'%s':%s",first?"":",",var,tmp);
		}
		out=bf2+lib_strlen(bf2);
		first=FALSE;
	}
	if(!done){
		lprintf(out,BFSZ-(int)(out-bf2),"%s'%s':%s",first?"":",",field,value);
	}
	ncat(bf2,"}",BFSZ-1);
	ncpy(real_out,bf2,BFSZ-1);
	return TRUE;
error:
	return FALSE;
}

int json_array_length(char *arr)
{
	char *s=arr;
	int count=0;
	if(!s) goto error; 
	while(strchr(" \t,",*s)) s++;
	if(*s!='[') goto error;
	s++;
	for(;*s;){
		while(strchr(" \t,",*s)) s++;
		if(*s==']') break;
		json_token_obj(&s);
		count++;
		// Need to make json_array_length handle unterminated arrays, but for now just apply some limit sanity checks
		if(count>1000000){
			// Still need proper fix of original problem:
			// Surgeweb import resulted in:
			// 	[{'type':'home','value':'207 Spring St.&#13;&#10;&#13;&#10;\nAlmonte Ontario K0A 1A0'}]" 
			// which then got processed to:
			// 	[{'type':'home','value':'207 Spring St.
			// so end of array never found
			count=0;
			break;
		}
	}
	return count;
error:
	return 0;
}
char *json_array_item(char *arr, int item, char *obj)
{
	char *begin,*s=arr;
	int count=0;
	while(strchr(" \t,",*s)) s++;
	if(*s!='[') goto error;
	s++;
	for(;*s;){
		while(strchr(" \t,",*s)) s++;
		if(*s==']') break;
		begin=json_token_obj(&s);
		count++;
		if(count==item){
			if (!begin)
				return NULL;
			ncpy(obj,begin,(int)(s-begin));
			return obj;
		}
	}
error:
	return NULL;
}
char *json_array_append(char *arr, char *obj, char *real_out)
{
	char *s=arr,bf2[BFSZ],*out=bf2,tmp[BFSZ],*begin;
	int first=TRUE;
	while(strchr(" \t,",*s)) s++;
	if(*s!='[') goto error;
	*out++=*s++;
	for(;*s;){
		int blen;
		while(strchr(" \t,",*s)) s++;
		if(*s==']') break;
		begin=json_token_obj(&s);
		if (!begin) return NULL;
		blen = (int)(s - begin);
		if (blen>BFSZ-1) return NULL;
		ncpy(tmp,begin,blen);
		lprintf(out,BFSZ-(int)(out-bf2),"%s%s",first?"":",",tmp);
		out=bf2+lib_strlen(bf2);
		first=FALSE;
	}
	lprintf(out,BFSZ-(int)(out-bf2),"%s%s]",first?"":",",obj);
	ncpy(real_out,bf2,BFSZ-1);
	return real_out;
error:
	return NULL;
}

// Return next tag token eg "tag" in "{'tag':'value','tag2':'value2'}"
char *json_token_var(char **upto,int endit)
{
	char *s=*upto,quote=0,*begin;
	int found=FALSE,have_colon=FALSE;
	while(strchr(" \t,",*s)) s++;
	if (*s=='\'' || *s=='"'){
		quote=*s++;
	}
	begin=s;
	for(;*s;s++){
		if(quote){
			if(*s=='\\'){
				s++;
			}else if(*s==quote){
				found=TRUE;
				break;
			}
			continue;
		}
		if(strchr(" :\t\r\n",*s)){
			if(*s==':')
				have_colon=TRUE;
			found=TRUE;
			break;
		}
	}
	if(found){
		if(endit)*s='\0';
		s++;
		if(!have_colon){
			while(strchr(" \t,",*s)) s++;	
			if(*s!=':') return NULL;
			s++;
		}
		*upto=s;
		return begin;
	}
	return NULL;
}
// Return next value token eg "value" in "{'tag':'value','tag2':'value2'}"
char *json_token_val(char **upto,int endit)
{
	char *s=*upto,quote=0,*begin;
	while(strchr(" \t,",*s)) s++;
	if (*s=='\'' || *s=='"'){
		quote=*s++;
	}else{
		// Routine only knows how to deal with quoted text values
		return NULL;
	}
	begin=s;
	for(;*s;s++){
		if(*s=='\\'){
			s++;
		}else if(*s==quote){
			if(endit)*s='\0';
			*upto=s+1;
			return begin;
		}
	}
	return NULL;
}
// Return next object token eg {'tag':'value'} in "[{'tag':'value'},{'tag2':'value2'}]"
// WARNING: Token is intentionally not terminated!! so check using *upto
char *json_token_obj(char **upto)
{
	char *s=*upto,*begin,*var,*val;

	while(strchr(" \t,",*s)) s++;
	if(*s!='{') goto error;
	begin=s++;
	for(;*s;){
		while(strchr(" \t,",*s)) s++;
		if(*s=='}') {
			s++;
			break;
		}
		var=json_token_var(&s,FALSE);
		if(!var) goto error;
		val=json_token_val(&s,FALSE);
		if(!val) goto error;
	}
	*upto=s;
	return begin;
error:
	return NULL;
}

int json_validate_string(char **s, int depth)
{
	if(**s=='\''){
		for((*s)++;**s!='\''&&**s;(*s)++){
			if(**s=='\\') (*s)++;
		}
		if(**s!='\'')return FALSE;
		(*s)++;
		return TRUE;
	}else{
		for(;sisalnum(**s)&&**s;(*s)++){
			if(**s=='\\') (*s)++;
		}
		return TRUE;
	}
}
int json_validate_plainstring(char *s)
{
	int last=0;
	for(;*s;s++){
		if(*s=='\'' && last!='\\') return FALSE;		
		last=*s;
	}
	return TRUE;
}
int json_validate_array(char **s, int depth)
{
	if(**s!='[') return FALSE;
	(*s)++;
	if(**s==']') return TRUE;

	for(;**s;){
		if(!json_validate(s,depth+1)) return FALSE;
		if(**s!=',') break;
		(*s)++;
	}
	if(**s!=']') return FALSE;
	(*s)++;
	return TRUE;
}
int json_validate_object(char **s,int depth)
{
	if(**s!='{') return FALSE;
	(*s)++;
	if(**s=='}') return TRUE;
	for(;**s;){
		if(!json_validate_string(s,depth+1)) return FALSE;
		if(**s!=':') return FALSE;
		(*s)++;
		if(!json_validate(s,depth+1)) return FALSE;
		if(**s!=',') break;
		(*s)++;
	}
	if(**s!='}') return FALSE;
	(*s)++;
	return TRUE;
}

int json_validate(char **s,int depth)
{
	switch(**s){
		case '[': 
			if(!json_validate_array(s,depth)) return FALSE;
			break;
		case '{': 
			if(!json_validate_object(s,depth)) return FALSE;
			break;
		case '\0': 
			break;
		default:
		case '\'':
			if(depth==1 && **s!='\'')return TRUE;	// Plain string not part
			if(!json_validate_string(s,depth)) return FALSE;
			break;
	}
	if (depth==1 && **s)  return FALSE;
	return TRUE;
}

int isjson(char *s)
{
	if(s[0]=='{' || s[0]=='[') return TRUE;
	return FALSE;
}

int json_email_array_wash(char*src, char *dest, int bfsz)
{
	char bf[BFSZ], tmp[BFSZ], email[BFSZ], name[BFSZ];
	char *var1,*val1,*var2,*val2,*upto;
	int count=1;
	ncpy(dest,"[",BFSZ-1);
	ncpy(bf,src,BFSZ-1);
	upto=bf;
//  needs to parse say: "[{'type':'home','value':'a@b.c'},{'type':'work','value':'b@c.d'},{'type':'other','value':'scienceteachers2@dallask12.org	'}]"
	if(*upto++!='[') goto error;
	for(;*upto;){
		if(*upto==']') break;
		if(*upto++!='{') goto error;
		//  ... 'type' ...
		var1=json_token_var(&upto,TRUE);
		if(!var1) goto error;
		//  ... 'home' ...
		val1=json_token_val(&upto,TRUE);
		if(!val1) goto error;
		//  ... 'value' ...
		var2=json_token_var(&upto,TRUE);
		if(!var2) goto error;
		//  ... 'a@b.c' ...
		val2=json_token_val(&upto,TRUE);
		if(!val2) goto error;

		lib_split_email_name(val2,email,name,BFSZ-1);
		lib_trim_spaces(email,tmp);
		ncpy(email,tmp,BFSZ-1);
		if(lib_strlen(email)>0){
			if(count++>1) ncat(dest,",",BFSZ-1);
			lprintf(tmp,BFSZ,"{'%s':'%s','%s':'%s'}",var1,val1,var2,email);
			ncat(dest,tmp,BFSZ-1);
		}
		if(*upto++!='}') goto error;
		if(*upto==',') upto++;
	}	
	ncat(dest,"]",BFSZ-1);
	return TRUE;
error:
	ncpy(dest,"[]",BFSZ-1);
	return FALSE;
}

// *** 

char* json_validate2(char*p1, char*result, int bfsz)
{
	if (p1[0]==0) {
		ncpy(result,"''",bfsz-1);
	}else if(p1[0]=='[' || p1[0]=='{'){
		ncpy(result,p1,bfsz-1);
	}else {
		char tmp[BFSZ];
		lprintf(result,bfsz,"'%s'",lib_trim_squote(tmp,p1,BFSZ));
	}
	return result;
}
char* json_validate_int(char*p1, char*result, int bfsz)
{
	if (lib_strlen(p1)==0) 
		ncpy(result,"0",bfsz-1);
	else 
		ncpy(result,p1,bfsz-1);
	return result;
}
char* json_validate2_bool(char*p1, char*result, int bfsz)
{
	if (istrue(p1)) 
		ncpy(result,"true",bfsz-1);
	else 
		ncpy(result,"false",bfsz-1);
	return result;
}
char* json_validate_bool(char*p1)
{
	char *s;
	if (istrue(p1)) 
		s="true";
	else 
		s="false";
	return s;
}

int json_blank(char*p1)
{
	if (lib_strlen(p1)==0 || strcmp(p1,"[]")==0) return TRUE;
	return FALSE;
}


void json_object_getbool(char *obj, char *field, int *result)
{
	char bf[BFSZ];
	json_object_getfield(obj, field, bf);
	*result = istrue(bf);
}
