/*!
 * Copyright (c) 2015-2018, NADAL Jean-Baptiste. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA
 *
 * @Author:  NADAL Jean-Baptiste
 * @Date: 20/06/2017
 */

/*------------------------------- INCLUDES ----------------------------------*/

#include <json-c/json.h>

//#include <mozart/event/messages.h>

extern "C" {
#include <uhttpd/uhttpd.h>
#include <uhttpd/plugin.h>
}

#include <ubus-cpp/ubus-call.h>

#include "core/http-reason.h"
#include "core/http-parameter.h"
#include "core/http-header.h"
#include "core/ubus-reason.h"
#include "core/uri-transform.h"

#include "rest/rest-controller.h"

#include "rest/rest-connection.h"

/*!  ----------------------------------------------------------------------------
 *  @fn RestConnection
 *
 *  @brief constructor of the rest connection object.
 */
RestConnection::RestConnection(struct uhttpd_ops *an_ops, struct client *a_client, RestController *a_controller, const std::string &a_parameters) : WebConnection(an_ops, a_client),
																																					m_controller(a_controller), m_ctx(0)
{
	if (!a_parameters.empty())
	{
		HttpParameter the_http_parm;
		m_connection_data = the_http_parm.parse_from_uri(a_parameters);
	}
	// printf("RestConnection:: constructor....\n");
}

/*!  ----------------------------------------------------------------------------
 *  @fn ~RestConnection
 *
 *  @brief destructor of the rest connection object.
 */
RestConnection::~RestConnection(void)
{
	abort(m_ctx);
}

/*!  ----------------------------------------------------------------------------
 *  @fn complete
 *
 *  @brief method called when the async call has been done.
 */
void RestConnection::complete(void)
{
	int the_result_code;
	// printf("resultat: <%s>\n", get_data().c_str());

	the_result_code = UbusReason::get(get_result());

	send_response(the_result_code, get_data().c_str());
}

/*!  ----------------------------------------------------------------------------
 *  @fn invoke
 *
 *  @brief invoke an async connection.
 */
void RestConnection::invoke(struct ubus_context *a_ctx)
{
	uint32_t the_id = 0;
	UBusCall the_cmd;
	std::string the_method;

	m_ctx = a_ctx;

	the_method = m_controller->get_method(m_client->request.method);

	// printf("RestConnection::invoke(object: %s)....\n", m_controller->get_path().c_str());
	if (!ubus_lookup_id(a_ctx, m_controller->get_path().c_str(), &the_id))
	{
		json_object *the_parameter_doc;

		the_parameter_doc = json_tokener_parse(m_connection_data.c_str());

		if (the_parameter_doc == 0)
			the_parameter_doc = parse_form_encoded_data();

		// printf("RestConnection::invoke launch async call (%d)....\n", the_id);
		the_cmd.exec_async(a_ctx, the_id, the_method, json_object_to_json_string(the_parameter_doc), this);

		json_object_put(the_parameter_doc);
	}
	else
	{
		send_response(404);
	}
}

/*!  ----------------------------------------------------------------------------
 *  @fn parse_form_encoded_data
 *
 *  @brief Parse the document content as form encoded parameters
 *   and return them as a json object with an attribute for each parameter.
 */
json_object *RestConnection::parse_form_encoded_data()
{
	json_object *the_parsed_document;
	char *the_parameters = strdup(m_connection_data.c_str());
	const char *the_encoded_name;
	const char *the_encoded_value;

	the_parsed_document = json_object_new_object();

	for (the_encoded_name = strtok(the_parameters, "="); the_encoded_name != NULL; the_encoded_name = strtok(NULL, "="))
	{
		std::string the_name, the_value;

		the_encoded_value = strtok(NULL, "&");

		if (the_encoded_value == NULL)
			the_encoded_value = "";

		the_name = UriTransform::decode(std::string(the_encoded_name));
		the_value = UriTransform::decode(std::string(the_encoded_value));

		json_object_object_add(the_parsed_document, the_name.c_str(), json_object_new_string(the_value.c_str()));
	}

	free(the_parameters);

	return the_parsed_document;
}

/*!  ----------------------------------------------------------------------------
 *  @fn send_response
 *
 *  @brief Send a response to the client
 */
void RestConnection::send_response(int a_result_code, std::string a_content_document)
{
	std::string the_status;
	struct json_object *the_root_node;
	HttpHeader the_http_header;

	if (m_controller->is_raw_response())
	{
		the_http_header.send(m_ops, m_client, a_result_code);
		m_ops->chunk_printf(m_client, "%s", get_data().c_str());
		m_ops->request_done(m_client);
		return;
	}

	if (a_result_code == 200)
		the_status = kPassed;
	else
		the_status = kFailed;

	if (a_content_document.empty())
	{
		the_root_node = json_object_new_object();
	}
	else
	{
		the_root_node = json_tokener_parse(get_data().c_str());
	}

	json_object_object_add(the_root_node, "id", json_object_new_string(m_controller->get_name().c_str()));
	json_object_object_add(the_root_node, kEvent_status_entry, json_object_new_string(the_status.c_str()));
	json_object_object_add(the_root_node, "response_code", json_object_new_int(a_result_code));

	the_http_header.send(m_ops, m_client, a_result_code);
	m_ops->chunk_printf(m_client, "%s", json_object_to_json_string(the_root_node));
	m_ops->request_done(m_client);

	json_object_put(the_root_node);
}
