/*
 * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2013, Grasshopper
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 *
 * The Initial Developer of the Original Code is Grasshopper
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Chris Rienzo <chris.rienzo@grasshopper.com>
 *
 * rayo_components.c -- Rayo component interface
 *
 */
#include "rayo_components.h"

#include <switch.h>
#include "mod_rayo.h"
#include "iks_helpers.h"

/**
 * Get access to Rayo component data.
 * @param id the component internal ID
 * @return the component or NULL. Call rayo_component_unlock() when done with component pointer.
 */
struct rayo_component *rayo_component_locate(const char *id, const char *file, int line)
{
	struct rayo_actor *actor = rayo_actor_locate_by_id(id, file, line);
	if (actor && is_component_actor(actor)) {
		return RAYO_COMPONENT(actor);
	} else if (actor) {
		 RAYO_RELEASE(actor);
	}
	return NULL;
}

/**
 * Send component start reply
 * @param component the component
 * @param iq the start request
 */
void rayo_component_send_start(struct rayo_component *component, iks *iq)
{
	iks *response = iks_new_iq_result(iq);
	iks *ref = iks_insert(response, "ref");
	iks_insert_attrib(ref, "xmlns", RAYO_NS);
	iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(component));
	RAYO_SEND_REPLY(component, iks_find_attrib(response, "to"), response);
}

/**
 * Create component complete event
 * @param component the component
 * @param reason_str the completion reason
 * @param reason_namespace the completion reason namespace
 * @param meta metadata to add as child
 * @param child_of_complete if true metadata is child of complete instead of reason
 * @return the event
 */
iks *rayo_component_create_complete_event_with_metadata(struct rayo_component *component, const char *reason_str, const char *reason_namespace, iks *meta, int child_of_complete)
{
	iks *response = iks_new("presence");
	iks *complete;
	iks *reason;
	iks_insert_attrib(response, "from", RAYO_JID(component));
	iks_insert_attrib(response, "to", component->client_jid);
	iks_insert_attrib(response, "type", "unavailable");
	complete = iks_insert(response, "complete");
	iks_insert_attrib(complete, "xmlns", RAYO_EXT_NS);
	reason = iks_insert(complete, reason_str);
	iks_insert_attrib(reason, "xmlns", reason_namespace);
	if (meta) {
		meta = iks_copy_within(meta, iks_stack(response));
		if (child_of_complete) {
			iks_insert_node(complete, meta);
		} else {
			iks_insert_node(reason, meta);
		}
	}

	return response;
}

/**
 * Create component complete event
 * @param component the component
 * @param reason the completion reason
 * @param reason_namespace the completion reason namespace
 * @return the event
 */
iks *rayo_component_create_complete_event(struct rayo_component *component, const char *reason, const char *reason_namespace)
{
	return rayo_component_create_complete_event_with_metadata(component, reason, reason_namespace, NULL, 0);
}

/**
 * Send rayo component complete event
 */
void rayo_component_send_complete_event(struct rayo_component *component, iks *response)
{
	component->complete = 1;
	RAYO_SEND_REPLY(component, iks_find_attrib(response, "to"), response);
	RAYO_RELEASE(component);
	RAYO_DESTROY(component);
}

/**
 * Send rayo complete
 */
void rayo_component_send_complete(struct rayo_component *component, const char *reason, const char *reason_namespace)
{
	rayo_component_send_complete_event(component, rayo_component_create_complete_event(component, reason, reason_namespace));
}

/**
 * Send rayo complete
 */
void rayo_component_send_complete_with_metadata(struct rayo_component *component, const char *reason, const char *reason_namespace, iks *meta, int child_of_complete)
{
	rayo_component_send_complete_event(component, rayo_component_create_complete_event_with_metadata(component, reason, reason_namespace, meta, child_of_complete));
}

/**
 * Send rayo complete
 */
void rayo_component_send_complete_with_metadata_string(struct rayo_component *component, const char *reason, const char *reason_namespace, const char *meta, int child_of_complete)
{
	iks *meta_xml = NULL;
	iksparser *p = iks_dom_new(&meta_xml);
	if (iks_parse(p, meta, 0, 1) != IKS_OK) {
		/* unexpected ... */
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "%s Failed to parse metadata for complete event: %s\n",
			RAYO_JID(component), meta);
		/* send without... */
		rayo_component_send_complete(component, reason, reason_namespace);
	} else {
		rayo_component_send_complete_with_metadata(component, reason, reason_namespace, meta_xml, child_of_complete);
	}
	if (meta_xml) {
		iks_delete(meta_xml);
	}
	iks_parser_delete(p);
}

/**
 * Background API data
 */
struct component_bg_api_cmd {
	const char *cmd;
	const char *args;
	switch_memory_pool_t *pool;
	struct rayo_component *component;
};

/**
 * Thread that outputs to component
 * @param thread this thread
 * @param obj the Rayo mixer context
 * @return NULL
 */
static void *SWITCH_THREAD_FUNC component_bg_api_thread(switch_thread_t *thread, void *obj)
{
	struct component_bg_api_cmd *cmd = (struct component_bg_api_cmd *)obj;
	switch_stream_handle_t stream = { 0 };
	switch_memory_pool_t *pool = cmd->pool;
	SWITCH_STANDARD_STREAM(stream);

	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "BGAPI EXEC: %s %s\n", cmd->cmd, cmd->args);
	if (switch_api_execute(cmd->cmd, cmd->args, NULL, &stream) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "BGAPI EXEC FAILURE\n");
		rayo_component_send_complete(cmd->component, COMPONENT_COMPLETE_ERROR);
	} else {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "BGAPI EXEC RESULT: %s\n", (char *)stream.data);
	}
	switch_safe_free(stream.data);
	switch_core_destroy_memory_pool(&pool);
	return NULL;
}

/**
 * Run a background API command
 * @param cmd API command
 * @param args API args
 */
void rayo_component_api_execute_async(struct rayo_component *component, const char *cmd, const char *args)
{
	switch_thread_t *thread;
	switch_threadattr_t *thd_attr = NULL;
	struct component_bg_api_cmd *bg_cmd = NULL;
	switch_memory_pool_t *pool;

	/* set up command */
	switch_core_new_memory_pool(&pool);
	bg_cmd = switch_core_alloc(pool, sizeof(*bg_cmd));
	bg_cmd->pool = pool;
	bg_cmd->cmd = switch_core_strdup(pool, cmd);
	bg_cmd->args = switch_core_strdup(pool, args);
	bg_cmd->component = component;

	/* create thread */
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s BGAPI START\n", RAYO_JID(component));
	switch_threadattr_create(&thd_attr, pool);
	switch_threadattr_detach_set(thd_attr, 1);
	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
	switch_thread_create(&thread, thd_attr, component_bg_api_thread, bg_cmd, pool);
}

/**
 * Handle configuration
 */
switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
{
	if (rayo_input_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
		rayo_output_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
		rayo_prompt_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
		rayo_record_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
		rayo_fax_components_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
		rayo_exec_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS) {
		return SWITCH_STATUS_TERM;
	}
	return SWITCH_STATUS_SUCCESS;
}

/**
 * Handle shutdown
 */
switch_status_t rayo_components_shutdown(void)
{
	rayo_input_component_shutdown();
	rayo_output_component_shutdown();
	rayo_prompt_component_shutdown();
	rayo_record_component_shutdown();
	rayo_fax_components_shutdown();
	rayo_exec_component_shutdown();

	return SWITCH_STATUS_SUCCESS;
}

/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
 */
