/*
 * $Id$
 *
 * Kazoo module interface
 *
 * Copyright (C) 2010-2014 2600Hz
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * Kamailio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * Kamailio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * History:
 * --------
 * 2014-08  first version (2600hz)
 */

#include <stdio.h>
#include <string.h>

#include "../../core/mod_fix.h"
#include "../../core/lvalue.h"

#include "kz_json.h"
#include "const.h"
#include "../../core/pvar.h"
#include "../../core/usr_avp.h"



static str kz_pv_str_empty = {"", 0};

enum json_type kz_json_get_type(struct json_object *jso)
{
  return json_object_get_type(jso);
}

typedef str* json_key;
typedef json_key* json_keys;

json_keys kz_str_split(char* a_str, const char a_delim, int* c)
{
	json_keys result = 0;
    int count     = 0;
    char* tmp        = a_str;
    char* last_comma = 0;
    char delim[2];
    delim[0] = a_delim;
    delim[1] = 0;
    int len = 0;

    /* Count how many elements will be extracted. */
    while (*tmp)
    {
        if (a_delim == *tmp)
        {
            count++;
            last_comma = tmp;
        }
        tmp++;
    }

    /* Add space for trailing token. */
    count += last_comma < (a_str + strlen(a_str) - 1);

    /* Add space for terminating null string so caller
       knows where the list of returned strings ends. */
//    count++;
    *c = count;
    LM_DBG("COUNT %d\n", count);

    result = pkg_malloc(sizeof(json_key) * count);
    memset(result, 0, sizeof(json_key) * count);

    if (result)
    {
        int idx  = 0;
        char* token = strtok(a_str, delim);

        while (token)
        {
            LM_DBG("TOKEN %d : %s\n", idx, token);

            assert(idx < count);

            result[idx] = pkg_malloc(sizeof(str));
            len = strlen(token);

            result[idx]->len = len;
			result[idx]->s = pkg_malloc((len + 1) * sizeof(char));
			strncpy(result[idx]->s, token, len);
			result[idx]->s[len] = '\0';

        	int i = 0;
        	while(i < len) {
        		if(result[idx]->s[i] == kz_json_escape_char)
        			result[idx]->s[i] = '.';
        		i++;
        	}
        	LM_DBG("TOKEN2 %d : %s\n", idx, result[idx]->s);
            token = strtok(0, delim);
            idx++;
        }
        assert(idx == count);
    }

    return result;
}

struct json_object * kz_json_get_field_object(str* json, str* field)
{
  json_keys keys;
  json_key key;
  char* dup;
  char f1[250], f2[250];//, f3[25];
  int i, parts;

  dup = pkg_malloc(json->len+1);
  memcpy(dup, json->s, json->len);
  dup[json->len] = '\0';
  struct json_object *j = json_tokener_parse(dup);
  pkg_free(dup);

  if (j==NULL) {
	  LM_ERR("empty or invalid JSON\n");
	  return NULL;
  }

  struct json_object *jtree = NULL;
  struct json_object *ret = NULL;

  LM_DBG("getting json %.*s\n", field->len, field->s);

  dup = pkg_malloc(field->len+1);
  memcpy(dup, field->s, field->len);
  dup[field->len] = '\0';
  keys = kz_str_split(dup, '.', &parts);
  pkg_free(dup);

    if (keys)
    {
    	jtree = j;
        for (i = 0; i < parts; i++)
        {
        	key = keys[i];
        	LM_DBG("TOKEN %d , %p, %p : %s\n", i, keys[i], key->s, key->s);

        	if(jtree != NULL) {
				//str field1 = str_init(token);
				// check for idx []
				int sresult = sscanf(key->s, "%[^[][%[^]]]", f1, f2); //, f3);
				LM_DBG("CHECK IDX %d - %s , %s, %s\n", sresult, key->s, f1, (sresult > 1? f2 : "(null)"));

				jtree = kz_json_get_object(jtree, f1);
				if(jtree != NULL) {
					char *value = (char*)json_object_get_string(jtree);
					LM_DBG("JTREE OK %s\n", value);
				}
				if(jtree != NULL && sresult > 1 && json_object_is_type(jtree, json_type_array)) {
					int idx = atoi(f2);
					jtree = json_object_array_get_idx(jtree, idx);
					if(jtree != NULL) {
						char *value = (char*)json_object_get_string(jtree);
						LM_DBG("JTREE IDX OK %s\n", value);
					}
				}
        	}
        }

        for(i = 0;i < parts; i++) {
            LM_DBG("FREE %d\n", i);
            pkg_free(keys[i]->s);
            pkg_free(keys[i]);
        }

        pkg_free(keys);
    }



	if(jtree != NULL)
		ret = json_object_get(jtree);

	json_object_put(j);

	return ret;
}

int kz_json_get_count(str* json, str* field, pv_value_p dst_val)
{

  struct json_object *jtree = kz_json_get_field_object(json, field);


	dst_val->flags = PV_TYPE_INT | PV_VAL_INT;
    dst_val->rs = kz_pv_str_empty;
    dst_val->ri = 0;
	if(jtree != NULL) {
		if(json_object_is_type(jtree, json_type_array)) {
			dst_val->ri = json_object_array_length(jtree);
		}
        json_object_put(jtree);
	}
	return 1;
}


int kz_json_get_field_ex(str* json, str* field, pv_value_p dst_val)
{

  struct json_object *jtree = kz_json_get_field_object(json, field);


	if(jtree != NULL) {
		char *value = (char*)json_object_get_string(jtree);
		int len = strlen(value);
		dst_val->rs.s = pkg_malloc(len+1);
		memcpy(dst_val->rs.s, value, len);
		dst_val->rs.s[len] = '\0';
		dst_val->rs.len = len;
		dst_val->flags = PV_VAL_STR | PV_VAL_PKG;
        dst_val->ri = 0;
        json_object_put(jtree);
	} else {
		dst_val->flags = PV_VAL_NULL;
        dst_val->rs = kz_pv_str_empty;
        dst_val->ri = 0;
	}
	return 1;
}


int kz_json_get_field(struct sip_msg* msg, char* json, char* field, char* dst)
{
  str json_s;
  str field_s;
  pv_spec_t *dst_pv;
  pv_value_t dst_val;

	if (fixup_get_svalue(msg, (gparam_p)json, &json_s) != 0) {
		LM_ERR("cannot get json string value\n");
		return -1;
	}

	if (fixup_get_svalue(msg, (gparam_p)field, &field_s) != 0) {
		LM_ERR("cannot get field string value\n");
		return -1;
	}

	if(kz_json_get_field_ex(&json_s, &field_s, &dst_val) != 1)
		return -1;

	dst_pv = (pv_spec_t *)dst;
	dst_pv->setf(msg, &dst_pv->pvp, (int)EQ_T, &dst_val);
	if(dst_val.flags & PV_VAL_PKG)
		pkg_free(dst_val.rs.s);
	else if(dst_val.flags & PV_VAL_SHM)
		shm_free(dst_val.rs.s);

	return 1;
}

struct json_object* kz_json_parse(const char *str)
{
    struct json_tokener* tok;
    struct json_object* obj;

    if (str == NULL || str[0] == 0) {
      LM_ERR("Error parsing json: empty string\n");
      return NULL;
    }

    tok = json_tokener_new();
    if (!tok) {
      LM_ERR("Error parsing json: could not allocate tokener\n");
      return NULL;
    }

    obj = json_tokener_parse_ex(tok, str, -1);
    if(tok->err != json_tokener_success) {        
      LM_ERR("Error parsing json: %s\n", json_tokener_error_desc(tok->err));
      LM_ERR("%s\n", str);
      if (obj != NULL)
	   json_object_put(obj);
      obj = NULL;
    }

    json_tokener_free(tok);
    return obj;
}

struct json_object* kz_json_get_object(struct json_object* jso, const char *key)
{
	struct json_object *result = NULL;
	json_object_object_get_ex(jso, key, &result);
	return result;
}

int kz_json_get_keys(struct sip_msg* msg, char* json, char* field, char* dst)
{
  str json_s;
  str field_s;
  int_str keys_avp_name;
  unsigned short keys_avp_type;
  pv_spec_t *avp_spec;

	if (fixup_get_svalue(msg, (gparam_p)json, &json_s) != 0) {
		LM_ERR("cannot get json string value\n");
		return -1;
	}

	if (fixup_get_svalue(msg, (gparam_p)field, &field_s) != 0) {
		LM_ERR("cannot get field string value\n");
		return -1;
	}

	if(dst == NULL){
		LM_ERR("avp spec is null\n");
		return -1;
	}

	avp_spec = (pv_spec_t *)dst;

	if(avp_spec->type != PVT_AVP) {
		LM_ERR("invalid avp spec\n");
		return -1;
	}

	if(pv_get_avp_name(0, &avp_spec->pvp, &keys_avp_name, &keys_avp_type)!=0)
	{
		LM_ERR("invalid AVP definition\n");
		return -1;
	}

	struct json_object *jtree = kz_json_get_field_object(&json_s, &field_s);

	if(jtree != NULL) {
		json_foreach_key(jtree, k) {
			LM_DBG("ITERATING KEY %s\n", k);
			int_str v1;
			v1.s.s = k;
			v1.s.len = strlen(k);
			if (add_avp(AVP_VAL_STR|keys_avp_type, keys_avp_name, v1) < 0) {
				LM_ERR("failed to create AVP\n");
			    json_object_put(jtree);
				return -1;
			}
		}
	    json_object_put(jtree);
	}

	return 1;
}