#include "Rlibjson.h"

int setType(int cur, int newType);
SEXP makeVector(SEXP l, int len, int type, SEXP nullValue);

/* Simple routine to return the parsed JSONNODE pointer.
   This would be the start of allowing the caller to manipulate the tree
   directly. */
SEXP
R_json_parse(SEXP str)
{
    JSONNODE *node;
    node = json_parse(CHAR(STRING_ELT(str, 0)));
    return(R_MakeExternalPtr(node, Rf_install("JSONNODE"), R_NilValue));
}

void
jsonErrorHandler(const json_char *foo)
{
    Rf_error( "json error handler: %s", foo);
}

SEXP
R_fromJSON(SEXP r_str, SEXP simplify, SEXP nullValue, SEXP simplifyWithNames, SEXP encoding,
            SEXP r_stringFun, SEXP r_str_type)
{
    const char * str = CHAR(STRING_ELT(r_str, 0));
    JSONNODE *node;
    SEXP ans;
    int nprotect = 0;
    StringFunctionType str_fun_type = GARBAGE;

    if(r_stringFun != R_NilValue) {
	if(TYPEOF(r_stringFun) == CLOSXP) {
	    SEXP e;
	    PROTECT(e = allocVector(LANGSXP, 2));
	    nprotect++;
	    SETCAR(e, r_stringFun);
	    r_stringFun = e;
	}
	str_fun_type = INTEGER(r_str_type)[0];
    } else 
	r_stringFun = NULL;

#if 0
    register_debug_callback(jsonErrorHandler);
#else
//#pragma message("activate the debug_callback")
#endif
    node = json_parse(str);
    ans = processJSONNode(node, json_type(node), INTEGER(simplify)[0], nullValue, LOGICAL(simplifyWithNames)[0],
 			  INTEGER(encoding)[0], r_stringFun, str_fun_type);
    json_delete(node);
    if(nprotect)
	UNPROTECT(nprotect);

    return(ans);
}

int 
getElType(int len, int numNumbers, int numStrings, int numLogicals, int numNulls)
{
    if(numStrings > 0) 
	return(STRSXP);
    if(numNumbers > 0) 
	return(REALSXP);
    return(LGLSXP);
}


SEXP 
processJSONNode(JSONNODE *n, int parentType, int simplify, SEXP nullValue, int simplifyWithNames, cetype_t charEncoding,
                 SEXP r_stringCall, StringFunctionType str_fun_type)
{
    
    if (n == NULL){
        Rf_error("invalid JSON input");
    }
 
    JSONNODE *i;
    int len = 0, ctr = 0;
    int nprotect = 0;
    int numNulls = 0;
    len = json_size(n);
    char startType = (char)parentType; // was 127
    
    int isNullHomogeneous = (TYPEOF(nullValue) == LGLSXP || TYPEOF(nullValue) == REALSXP ||
                                TYPEOF(nullValue) == STRSXP || TYPEOF(nullValue) == INTSXP);
    int numStrings = 0;
    int numLogicals = 0;
    int numNumbers = 0;

    SEXP ans, names = NULL;
    PROTECT(ans = NEW_LIST(len)); nprotect++;

    int homogeneous = 0;
    int elType = NILSXP;
    while (ctr < len){  // i != json_end(n)

	i = json_at(n, ctr);

        if (i == NULL){
            Rf_error("Invalid JSON Node");
        }

	json_char *node_name = json_name(i);
	
	char type = json_type(i);
	if(startType == 127)
	    startType = type;

	SEXP el;
	switch(type) {
   	   case JSON_NULL:
	       el = nullValue; /* R_NilValue; */
	       numNulls++;
	       if(isNullHomogeneous) {
		   homogeneous++;
		   elType = setType(elType, TYPEOF(nullValue));
	       } else
		   elType = TYPEOF(nullValue);
	       break;
   	   case JSON_ARRAY:
  	   case JSON_NODE:
	       el = processJSONNode(i, type, simplify, nullValue, simplifyWithNames, charEncoding, r_stringCall, str_fun_type);
	       if(Rf_length(el) > 1)
		   elType = VECSXP;
	       else
		   elType = setType(elType, TYPEOF(el));
	       break;
 	   case JSON_NUMBER:
	       el = ScalarReal(json_as_float(i));
	       homogeneous++;
	       elType = setType(elType, REALSXP);
	       numNumbers++;
	       break;
 	   case JSON_BOOL:
	       el = ScalarLogical(json_as_bool(i));
	       elType = setType(elType, LGLSXP);
	       numLogicals++;
	       break;
 	   case JSON_STRING:
	   {
//XXX Garbage collection
#if 0 //def JSON_UNICODE
	       wchar_t *wtmp = json_as_string(i);
	       char *tmp;
	       int len = wcslen(wtmp);
	       int size = sizeof(char) * (len * MB_LEN_MAX + 1);
	       tmp = (char *)malloc(size);
	       if (tmp == NULL) {
                   Rf_error("Cannot allocate memory");
               }
	       wcstombs(tmp, wtmp, size);
#else
    char *tmp = json_as_string(i);
//    tmp = reEnc(tmp, CE_BYTES, CE_UTF8, 1);
#endif


    if(r_stringCall != NULL && TYPEOF(r_stringCall) == EXTPTRSXP) {
        if(str_fun_type == SEXP_STR_ROUTINE) {
	    SEXPStringRoutine fun;
	    fun = (SEXPStringRoutine) R_ExternalPtrAddr(r_stringCall);	    
	    el = fun(tmp, charEncoding);
	} else {
	    char *tmp1;
	    StringRoutine fun;
	    fun = (StringRoutine) R_ExternalPtrAddr(r_stringCall);
	    tmp1 = fun(tmp);
	    if(tmp1 != tmp)
		json_free(tmp);
	    tmp = tmp1;
	    el = ScalarString(mkCharCE(tmp, charEncoding));
	}
    } else {
	el = ScalarString(mkCharCE(tmp, charEncoding));
    	     /* Call the R function if there is one. */
	if(r_stringCall != NULL) {
	    SETCAR(CDR(r_stringCall), el);
	    el = Rf_eval(r_stringCall, R_GlobalEnv);
	}
	/* XXX compute with elType. */
    }

	       json_free(tmp);
	       
	       elType = setType(elType, 
   				     /* If we have a class, not a primitive type */
                                  Rf_length(getAttrib(el, Rf_install("class"))) ? LISTSXP : TYPEOF(el));
               if(r_stringCall != NULL && str_fun_type != NATIVE_STR_ROUTINE) {
		   switch(TYPEOF(el)) {
			  case REALSXP:
   			     numNumbers++;
			  break;
			  case LGLSXP:
   			     numLogicals++;
			  break;
			  case STRSXP:
   			     numStrings++;
			  break;
		   }
	       } else if(TYPEOF(el) == STRSXP) 
		   numStrings++;
      }
	       break;
	default:
	    Rf_warning("shouldn't be here");
	    el = R_NilValue;
	    break;
	}
	SET_VECTOR_ELT(ans, ctr, el);

	if(parentType == JSON_NODE || (node_name && node_name[0])) {
	    if(names == NULL) {
	        PROTECT(names = NEW_CHARACTER(len)); nprotect++;
	    }
	    if(node_name && node_name[0])
		SET_STRING_ELT(names, ctr, mkChar(node_name));
	}
	json_free(node_name);
	ctr++;
    }

    /* If we have an empty object, we try to make it into a form equivalent to emptyNamedList
       if it is a {},  or as an AsIs object in R if an empty array. */
    if(len == 0 && (parentType == -1 || parentType == JSON_ARRAY || parentType == JSON_NODE)) {
        if(parentType == -1) 
            parentType = startType;

        if(parentType == JSON_NODE)
	   SET_NAMES(ans, NEW_CHARACTER(0));
        else  {
	   SET_CLASS(ans, ScalarString(mkChar("AsIs")));
	}

    } else if(simplifyWithNames || names == NULL || Rf_length(names) == 0) {
	int allSame = (numNumbers == len || numStrings == len || numLogicals == len) || 
	    ((TYPEOF(nullValue) == LGLSXP && LOGICAL(nullValue)[0] == NA_INTEGER) && 
	     ((numNumbers + numNulls) == len || (numStrings + numNulls) == len || (numLogicals + numNulls) == len));
        homogeneous = allSame ||  ( (numNumbers + numStrings + numLogicals + numNulls) == len);

        if(simplify == NONE) {
	} else if(allSame && 
		  ((numNumbers == len && (simplify & STRICT_NUMERIC)) ||
  		      ((numLogicals == len) && (simplify & STRICT_LOGICAL)) ||
		   ( (numStrings == len) && (simplify & STRICT_CHARACTER)))) {
	       elType = getElType(len, numNumbers, numStrings, numLogicals, numNulls);
   	       ans = makeVector(ans, len, elType, nullValue);
	} else if((simplify == ALL && homogeneous) || (simplify == STRICT && allSame)) {
	    elType = getElType(len, numNumbers, numStrings, numLogicals, numNulls);
	    ans = makeVector(ans, len, elType, nullValue);
	}
    }
      

    if(names)
	SET_NAMES(ans, names);
	
    UNPROTECT(nprotect);
    return(ans);
}

SEXP
makeVector(SEXP ans, int len, int type, SEXP nullValue)
{
    SEXP tmp;
    int ctr;

    if(type == REALSXP) {
	PROTECT(tmp = NEW_NUMERIC(len)); 
	for(ctr = 0; ctr < len; ctr++) {
	    SEXP el = VECTOR_ELT(ans, ctr);
	    REAL(tmp)[ctr] = TYPEOF(el) == LGLSXP && LOGICAL(el)[0] == NA_INTEGER ? NA_REAL : (TYPEOF(el) == REALSXP ? REAL(el)[0] : Rf_asReal(el));
	}
    } else if(type == LGLSXP) {
	PROTECT(tmp = NEW_LOGICAL(len)); 
	for(ctr = 0; ctr < len; ctr++) {
	    SEXP el = VECTOR_ELT(ans, ctr);
	    LOGICAL(tmp)[ctr] = TYPEOF(el) == LGLSXP ? LOGICAL(el)[0] : Rf_asInteger(el);
	}
    } else if(type == STRSXP) {
	PROTECT(tmp = NEW_CHARACTER(len)); 
	for(ctr = 0; ctr < len; ctr++) {
	    SEXP el = VECTOR_ELT(ans, ctr);
	    if(el == R_NilValue)
		SET_STRING_ELT(tmp, ctr, NA_STRING);
	    else if(TYPEOF(el) == STRSXP)
		SET_STRING_ELT(tmp, ctr, STRING_ELT(el, 0));
	    else if(TYPEOF(el) == LGLSXP) {
		SET_STRING_ELT(tmp, ctr, LOGICAL(el)[0] == NA_INTEGER ? NA_STRING : mkChar(LOGICAL(el)[0] ? "TRUE" : "FALSE"));
	    } else if(TYPEOF(el) == REALSXP) {
		char buf[70];
		snprintf(buf, sizeof(buf), "%lf", REAL(el)[0]);
		SET_STRING_ELT(tmp, ctr, mkChar(buf));
	    }
	}
    } else
	return(ans);

    UNPROTECT(1);
    return(tmp);
}

int setType(int cur, int newType)
{
    if(cur == newType)
	return(cur);

    if(newType == VECSXP || cur == VECSXP)
	return(VECSXP);

    switch(cur) {
    case INTSXP:
	switch(newType) {
	case LGLSXP:
	    return(INTSXP);
	    break;
	case REALSXP:
	    return(REALSXP);
	    break;
	case STRSXP:
	    return(STRSXP);
	    break;
	}
	break;
    case LGLSXP:
	switch(newType) {
	case INTSXP:
	    return(INTSXP);
	    break;
	case REALSXP:
	    return(REALSXP);
	    break;
	case STRSXP:
	    return(STRSXP);
	    break;
	}
	break;
    case REALSXP:
	switch(newType) {
	case INTSXP:
	    return(REALSXP);
	    break;
	case LGLSXP:
	    return(REALSXP);
	    break;
	case STRSXP:
	    return(STRSXP);
	    break;
	}
	break;
    case STRSXP:
	return(STRSXP);
	break;
    }

    return(newType);
}



SEXP
R_libjson_version(void)
{
    char buf[20];
    snprintf(buf, sizeof(buf),
	     "%d.%d-%d", (int) __LIBJSON_MAJOR__,
	     (int) __LIBJSON_MINOR__,
	     (int) __LIBJSON_PATCH__);

    return(ScalarString(mkChar(buf)));
}


SEXP
R_isValidJSON(SEXP input)
{
    const char *txt;
    int status = 0;
    txt = CHAR(STRING_ELT(input, 0));
    status = json_is_valid(txt);
    return(ScalarLogical(status));
}






/******************/

#ifdef R_JSON_STREAM

SEXP
R_json_new_stream(SEXP fun, SEXP pullFun)
{
    JSONSTREAM *stream;
    stream = json_new_stream();
    return(R_MakeExternalPtr(stream, Rf_install("JSON_STREAM"), R_NilValue));
}

SEXP
R_json_stream_push(SEXP r_stream, SEXP r_txt)
{
    JSONSTREAM *stream;
    stream = R_getStreamRef(r_stream);

    json_stream_push(stream, CHAR(STRING_ELT(r_txt, 0)));
    return(ScalarLogical(1));
}
#endif


#ifndef NEW_JSON_NEW_STREAM
SEXP expr;
#endif

SEXP
R_json_node_type(SEXP r_ref)
{
    JSONNODE *node = (JSONNODE *) R_ExternalPtrAddr(r_ref);
    return(ScalarInteger( (int) json_type(node)));
}

void
#ifdef NEW_JSON_NEW_STREAM
R_stream_callback(JSONNODE *node, void *data)
#else
R_stream_callback(JSONNODE *node) 
#endif
{
#ifdef NEW_JSON_NEW_STREAM
    SEXP expr = (SEXP) data;
#endif
    SEXP ref;
    ref = CAR(CDR(expr));
    R_SetExternalPtrAddr(ref, node);
    Rf_eval(expr, R_GlobalEnv);
}


SEXP 
makeNodeRef(JSONNODE *node)
{
    SEXP ans;
    PROTECT(ans = R_MakeExternalPtr(node, Rf_install("JSONNODE"), R_NilValue));
    SET_CLASS(ans, ScalarString(mkChar("JSONNODE")));
    UNPROTECT(1);
    return(ans);

}



SEXP
R_json_stream_parse(SEXP str, SEXP fun)
{
    JSONSTREAM *stream;
    SEXP nodeRef;
#ifdef NEW_JSON_NEW_STREAM 
    SEXP expr;
#endif    

    PROTECT(expr = allocVector(LANGSXP, 2));
    SETCAR(expr, fun);
    nodeRef = makeNodeRef(NULL);
    SETCAR(CDR(expr), nodeRef);

#ifdef NEW_JSON_NEW_STREAM 
    stream = json_new_stream(R_stream_callback, NULL, expr);
#else
    stream = json_new_stream(R_stream_callback);
#endif

    json_stream_push(stream, CHAR(STRING_ELT(str, 0)));
    UNPROTECT(1);
    return(R_NilValue);
}


SEXP
R_jsonPrettyPrint(SEXP r_content, SEXP r_encoding)
{
    const char *str = CHAR(STRING_ELT(r_content, 0));
    JSONNODE *node;
    json_char *ans;
    
    node = json_parse(str);
    if(!node) {
	Rf_error("couldn't parse the JSON content");
    }

    ans = json_write_formatted(node);
    return(ScalarString(mkCharCE(ans, INTEGER(r_encoding)[0])));
}


const char *
dummyStringOperation(const char *value)
{
#ifdef TEST_DUMMY_STRING_OP
    fprintf(stderr, "[dummyStringOperation] %s\n", value);
#endif
    return(value);
}

SEXP
R_json_dateStringOp(const char *value, cetype_t encoding)
{
    int withNew = 0, noNew = 0;

    if( (noNew = (strncmp(value, "/Date(", 6)  == 0)) ||
          (withNew = strncmp(value, "/new Date(", 10)) == 0) {
        double num;
	if(noNew) 
   	   sscanf(value + 6, "%lf)/", &num);
	else
   	   sscanf(value + 10, "%lf)/", &num);

	SEXP ans, classNames;
        PROTECT(ans = ScalarReal(num));
	PROTECT(classNames = NEW_CHARACTER(2));
	SET_STRING_ELT(classNames, 0, mkChar("POSIXct"));
	SET_STRING_ELT(classNames, 1, mkChar("POSIXt"));
	SET_CLASS(ans, classNames);
        UNPROTECT(2);
	return(ans);
    } else
       return(ScalarString(mkCharCE(value, encoding)));
}
