/**
 * Copyright (C) 2013 Flowroute LLC (flowroute.com)
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * This file 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
 *
 *
 * This file 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
 *
 */

#include "../../core/mod_fix.h"
#include "../../core/pvar.h"
#include "../../core/lvalue.h"
#include "../tm/tm_load.h"

#include "janssonrpc.h"
#include "janssonrpc_request.h"
#include "janssonrpc_io.h"
#include "janssonrpc_funcs.h"


extern struct tm_binds tmb;

int jsonrpc_request(struct sip_msg *_m, char *_conn, char *_method,
		char *_params, char *_options)
{
	str conn;
	str method;
	str params;
	str options;
	str route;
	param_t *pit = NULL;
	param_t *freeme = NULL;
	int retry;
	int timeout;
	int retval = -1;

	/* defaults */
	options = null_str;
	route = null_str;
	timeout = JSONRPC_DEFAULT_TIMEOUT;
	retry = JSONRPC_DEFAULT_RETRY;

	if(get_str_fparam(&conn, _m, (fparam_t *)_conn) != 0) {
		ERR("cannot get connection value\n");
		return -1;
	}

	if(get_str_fparam(&method, _m, (fparam_t *)_method) != 0) {
		ERR("cannot get method value\n");
		return -1;
	}

	if(get_str_fparam(&params, _m, (fparam_t *)_params) != 0) {
		ERR("cannot get params value\n");
		return -1;
	}

	if(_options == NULL) {

	} else if(get_str_fparam(&options, _m, (fparam_t *)_options) != 0) {
		ERR("cannot get options value\n");
		return -1;

	} else {
		if(options.len == 0) {
			goto skip_parse;
		} else if(options.len > 0 && options.s[options.len - 1] == ';') {
			options.len--;
		}

		if(parse_params(&options, CLASS_ANY, NULL, &pit) < 0) {
			ERR("failed parsing params value\n");
			return -1;
		}

		freeme = pit;

		for(; pit; pit = pit->next) {
			if PIT_MATCHES("route") {
				route = pit->body;

			} else if PIT_MATCHES("timeout") {
				timeout = atoi(pit->body.s);

			} else if PIT_MATCHES("retry") {
				retry = atoi(pit->body.s);

			} else {
				ERR("Unrecognized option: %.*s\n", STR(pit->name));
				goto end;
			}
		}
	}
skip_parse:

	/* check options */
	if(timeout < 1) {
		ERR("invalid timeout option (%d). Must be > 0.\n", timeout);
		goto end;
	}

	if(retry < -1) {
		ERR("invalid retry option (%d). Must be > -2.\n", retry);
		goto end;
	}

	retval = mod_jsonrpc_request(_m, /* sip_msg */
			conn,					 /* connection group */
			method,					 /* RPC method */
			params,					 /* JSON param */
			route,					 /* result route */
			false,					 /* notify only */
			retry,					 /* retry attempts */
			(unsigned int)timeout	 /* request timeout */
	);

end:
	if(freeme)
		free_params(freeme);
	return retval;
}

int jsonrpc_notification(
		struct sip_msg *_m, char *_conn, char *_method, char *_params)
{
	str conn;
	str method;
	str params;

	if(get_str_fparam(&conn, _m, (fparam_t *)_conn) != 0) {
		ERR("cannot get connection value\n");
		return -1;
	}

	if(get_str_fparam(&method, _m, (fparam_t *)_method) != 0) {
		ERR("cannot get method value\n");
		return -1;
	}

	if(get_str_fparam(&params, _m, (fparam_t *)_params) != 0) {
		ERR("cannot get params value\n");
		return -1;
	}

	return mod_jsonrpc_request(_m, /* sip_msg */
			conn,				   /* connection group */
			method,				   /* RPC method */
			params,				   /* JSON param */
			null_str,			   /* result route */
			true,				   /* notify only */
			0,					   /* retry attempts */
			0					   /* request timeout */
	);
}

int mod_jsonrpc_request(struct sip_msg *msg, str conn, str method, str params,
		str route, bool notify_only, int retry, unsigned int timeout)
{
	unsigned int hash_index;
	unsigned int label;

	if(retry < -1) {
		ERR("retry can't be less than -1\n");
		return -1;
	}


	jsonrpc_req_cmd_t *req_cmd = create_req_cmd();
	CHECK_MALLOC(req_cmd);

	shm_str_dup(&req_cmd->conn, &conn);
	CHECK_MALLOC_GOTO(req_cmd->conn.s, error);

	shm_str_dup(&req_cmd->method, &method);
	CHECK_MALLOC_GOTO(req_cmd->conn.s, error);

	if(params.s) {
		shm_str_dup(&req_cmd->params, &params);
		CHECK_MALLOC_GOTO(req_cmd->params.s, error);
	}

	if(route.s) {
		shm_str_dup(&req_cmd->route, &route);
		CHECK_MALLOC_GOTO(req_cmd->route.s, error);
	}

	req_cmd->msg = msg;
	req_cmd->retry = retry;
	req_cmd->notify_only = notify_only;
	req_cmd->timeout = timeout;

	if(notify_only || route.len <= 0) {
		req_cmd->route = null_str;
		if(send_pipe_cmd(CMD_SEND, req_cmd) < 0)
			goto error;
		return 1; /* continue script execution */
	}

	tm_cell_t *t = 0;
	t = tmb.t_gett();
	if(t == NULL || t == T_UNDEFINED) {
		if(tmb.t_newtran(msg) < 0) {
			ERR("cannot create the transaction\n");
			goto error;
		}
		t = tmb.t_gett();
		if(t == NULL || t == T_UNDEFINED) {
			ERR("cannot look up the transaction\n");
			goto error;
		}
	}

	if(tmb.t_suspend(msg, &hash_index, &label) < 0) {
		ERR("t_suspend() failed\n");
		goto error;
	}
	req_cmd->t_hash = hash_index;
	req_cmd->t_label = label;

	if(send_pipe_cmd(CMD_SEND, req_cmd) < 0)
		goto error;

	return 0;

error:
	free_req_cmd(req_cmd);
	ERR("failed to send request to io process\n");
	return -1;
}
