/*
 * OpenMRCP - Open Source Media Resource Control Protocol Stack
 * Copyright (C) 2007, Cepstral LLC
 *
 * 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.
 *
 * Author(s):
 * Arsen Chaloyan <achaloyan@yahoo.com>
 *
 * Contributor(s):
 *
 */

#include <apr_thread_proc.h>
#include <apr_thread_cond.h>
#include "mrcp_server_plugin.h"
#include "mrcp_recognizer.h"

typedef struct recognizer_emul_channel_t recognizer_emul_channel_t;

struct recognizer_emul_channel_t {
	apr_pool_t     *pool;

	int             running;
	apr_thread_t   *thread_handle;
	mrcp_message_t *message;
};

static apr_status_t recognizer_write_frame(audio_sink_t *sink, media_frame_t *frame);

static const audio_sink_method_set_t audio_sink_method_set = {
	NULL,
	NULL,
	NULL,
	recognizer_write_frame
};

static void* APR_THREAD_FUNC recognize_emul_proc(apr_thread_t *thread_handle, void *data)
{
	const size_t recognition_start_timeout = 100; /* ms */
	const size_t recognition_complete_timeout = 5000; /* ms */
	size_t timeout = 0;
	mrcp_server_channel_t *channel = data;
	recognizer_emul_channel_t *recog_channel = mrcp_server_channel_handle_get(channel);
	if(!recog_channel || !recog_channel->message) {
		return NULL;
	}
	
	while(recog_channel->running) {
		if(timeout == recognition_start_timeout) {
			mrcp_message_t *message = mrcp_event_create(recog_channel->message,RECOGNIZER_START_OF_INPUT,recog_channel->pool);
			printf("Recognizer Plugin: Start of Speech\n");
			mrcp_server_channel_signal(channel,message);
		}

		if(timeout >= recognition_complete_timeout) {
			mrcp_message_t *message = mrcp_event_create(recog_channel->message,RECOGNIZER_RECOGNITION_COMPLETE,recog_channel->pool);
			printf("Recognizer Plugin: Recognition Complete\n");
			mrcp_server_channel_signal(channel,message);
			break;
		}
		apr_sleep(20*1000);
		timeout += 20;
	}
	
	recog_channel->running = 0;
	return NULL;
}

static apr_status_t recognizer_write_frame(audio_sink_t *sink, media_frame_t *frame)
{
	/* send media frame to recognition engine to recognize */
	return APR_SUCCESS;
}

static mrcp_status_t recognizer_on_channel_open(mrcp_server_plugin_t *server_plugin, mrcp_server_channel_t *channel, apr_pool_t *pool)
{
	audio_sink_t *audio_sink;
	recognizer_emul_channel_t *recog_channel = apr_palloc(pool,sizeof(recognizer_emul_channel_t));
	recog_channel->pool = pool;
	mrcp_server_channel_handle_set(channel,recog_channel);

	audio_sink = apr_palloc(pool,sizeof(audio_sink_t));
	audio_sink->object = recog_channel;
	audio_sink->method_set = &audio_sink_method_set;
	mrcp_server_channel_audio_sink_set(channel,audio_sink);

	printf("Recognizer Plugin: Open Channel\n");
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t recognizer_on_channel_close(mrcp_server_plugin_t *server_plugin, mrcp_server_channel_t *channel)
{
	printf("Recognizer Plugin: Close Channel\n");
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t recognizer_on_definegrammar(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	printf("Recognizer Plugin: %s\n",request_message->start_line.method_name);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t recognizer_on_recognize(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	recognizer_emul_channel_t *recog_channel = mrcp_server_channel_handle_get(channel);

	printf("Recognizer Plugin: %s\n",request_message->start_line.method_name);
	if(request_message->body) {
		printf(request_message->body);
	}

	recog_channel->message = request_message;
	recog_channel->running = 1;
	apr_thread_create(&recog_channel->thread_handle, NULL, recognize_emul_proc, channel, recog_channel->pool);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t recognizer_on_get_result(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	printf("Recognizer Plugin: %s\n",request_message->start_line.method_name);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t recognizer_on_recognition_start_timers(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	printf("Recognizer Plugin: %s\n",request_message->start_line.method_name);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t recognizer_on_stop(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	apr_status_t status;
	recognizer_emul_channel_t *recog_channel = mrcp_server_channel_handle_get(channel);
	if(!recog_channel->running) {
		return MRCP_STATUS_FAILURE;
	}
	if(request_message) {
		printf("Recognizer Plugin: %s\n",request_message->start_line.method_name);
	}

	recog_channel->running = 0;
	apr_thread_join(&status,recog_channel->thread_handle);
	if(recog_channel->message) {
		mrcp_message_t *message = mrcp_response_create(recog_channel->message,recog_channel->pool);
		printf("Recognizer Plugin: STOP Complete\n");
		mrcp_server_channel_signal(channel,message);
	}
	recog_channel->message = NULL;
	recog_channel->thread_handle = NULL;
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t recognizer_on_setparams(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	printf("Recognizer Plugin: %s\n",request_message->start_line.method_name);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t recognizer_on_getparams(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	printf("Recognizer Plugin: %s\n",request_message->start_line.method_name);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_server_method_plugin_t recognizer_method_plugin_array[] = {
	recognizer_on_setparams,
	recognizer_on_getparams,
	recognizer_on_definegrammar,
	recognizer_on_recognize,
	recognizer_on_get_result,
	recognizer_on_recognition_start_timers,
	recognizer_on_stop
};

static mrcp_status_t recognizer_destroy(mrcp_module_t *module)
{
	return MRCP_STATUS_SUCCESS;
}

static mrcp_module_state_t recognizer_open(mrcp_module_t *module)
{
	return MODULE_STATE_OPENED;
}

static mrcp_module_state_t recognizer_close(mrcp_module_t *module)
{
	return MODULE_STATE_CLOSED;
}

static mrcp_module_method_set_t module_method_set = {
	recognizer_destroy,
	recognizer_open,
	recognizer_close,
	NULL /*process*/
};


MRCP_PLUGIN_DECLARE(mrcp_server_plugin_t*) mrcp_recognizer_plugin_create(apr_pool_t *pool)
{
	mrcp_server_plugin_t *recognizer_plugin = apr_palloc(pool,sizeof(mrcp_server_plugin_t));

	mrcp_module_init(&recognizer_plugin->module,&module_method_set);

	recognizer_plugin->channel_open = recognizer_on_channel_open;
	recognizer_plugin->channel_close = recognizer_on_channel_close;
	recognizer_plugin->method_plugin_array = recognizer_method_plugin_array;
	recognizer_plugin->method_count = RECOGNIZER_METHOD_COUNT;

	return recognizer_plugin;
}
