/*
 * 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 "mrcp_synthesizer_channel.h"
#include "mrcp_synthesizer.h"
#include "mrcp_generic_header.h"
#include "apt_ptr_list.h"

typedef struct mrcp_synthesizer_channel_t mrcp_synthesizer_channel_t;

struct mrcp_synthesizer_channel_t {
	mrcp_synthesizer_state_t state;
	apt_ptr_list_t          *queue;
};

static mrcp_status_t synthesizer_queue_process(mrcp_server_channel_t *channel, mrcp_server_method_plugin_t method_plugin, mrcp_message_t *response_message);
static mrcp_status_t synthesizer_stop_process(mrcp_server_channel_t *channel, mrcp_server_method_plugin_t method_plugin, mrcp_message_t *request_message, mrcp_message_t *response_message);


static mrcp_server_channel_state_t synthesizer_channel_open(mrcp_server_channel_t *channel, apr_pool_t *pool)
{
	mrcp_server_resource_t *resource = channel->resource;
	mrcp_synthesizer_channel_t *synth_channel = apr_palloc(pool,sizeof(mrcp_synthesizer_channel_t));
	synth_channel->state = SYNTHESIZER_STATE_IDLE;
	synth_channel->queue = apt_list_create(pool);

	apt_log(APT_PRIO_INFO,"Synthesizer Channel Open\n");
	channel->data = synth_channel;
	channel->state = MRCP_CHANNEL_STATE_OPENED;
	if(resource->plugin->channel_open) {
		if(resource->plugin->channel_open(resource->plugin,channel,pool) != MRCP_STATUS_SUCCESS) {
			channel->state = MRCP_CHANNEL_STATE_NONE;
		}
	}
	return channel->state;
}

static mrcp_server_channel_state_t synthesizer_channel_close(mrcp_server_channel_t *channel)
{
	mrcp_server_resource_t *resource = channel->resource;
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	if(channel->state != MRCP_CHANNEL_STATE_OPENED) {
		return channel->state;
	}

	apt_log(APT_PRIO_INFO,"Synthesizer Channel Close\n");
	if(apt_list_head(synth_channel->queue) == NULL) {
		if(synth_channel->state != SYNTHESIZER_STATE_IDLE) {
			apt_log(APT_PRIO_ERROR,"Channel State Error: expected to be idle\n");
		}
		if(resource->plugin->channel_close) {
			resource->plugin->channel_close(resource->plugin,channel);
		}
		channel->state = MRCP_CHANNEL_STATE_CLOSED;
	}
	else {
		if(synth_channel->state != SYNTHESIZER_STATE_IDLE) {
			mrcp_server_method_plugin_t method_plugin = resource->plugin->method_plugin_array[SYNTHESIZER_STOP];
			synthesizer_stop_process(channel,method_plugin,NULL,NULL);
		}
		channel->state = MRCP_CHANNEL_STATE_CLOSE_REQUESTED;
	}

	return channel->state;
}

static mrcp_status_t synthesizer_queue_process(mrcp_server_channel_t *channel, mrcp_server_method_plugin_t method_plugin, mrcp_message_t *response_message)
{
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	mrcp_message_t *request_message = apt_list_head(synth_channel->queue);
	if(!request_message || !method_plugin) {
		return MRCP_STATUS_FAILURE;
	}

	apt_log(APT_PRIO_DEBUG,"Process Speak Request [%d]\n",request_message->start_line.request_id);
	if(method_plugin(channel,request_message,response_message) != MRCP_STATUS_SUCCESS) {
		apt_log(APT_PRIO_WARNING,"Failed to Process Speak Request [%d]\n",request_message->start_line.request_id);
		apt_list_pop_front(synth_channel->queue);
		synth_channel->state = SYNTHESIZER_STATE_IDLE;
		return MRCP_STATUS_FAILURE;
	}
	synth_channel->state = SYNTHESIZER_STATE_SPEAKING;
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_stop_process(mrcp_server_channel_t *channel, mrcp_server_method_plugin_t method_plugin, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	apt_log(APT_PRIO_DEBUG,"Stop Active Speak Request\n");
	if(method_plugin) {
		method_plugin(channel,request_message,response_message);
	}
	synth_channel->state = SYNTHESIZER_STATE_IDLE;
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_speak(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message, mrcp_server_method_plugin_t method_plugin)
{
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	apt_log(APT_PRIO_DEBUG,"Add Speak Request [%d]\n",request_message->start_line.request_id);
	mrcp_header_inherit(&request_message->header,&channel->properties,request_message->pool);
	apt_list_push_back(synth_channel->queue,request_message);

	if(apt_list_head(synth_channel->queue) == request_message) {
		if(synth_channel->state != SYNTHESIZER_STATE_IDLE) {
			apt_log(APT_PRIO_ERROR,"Channel State Error: expected to be idle\n");
		}
		if(synthesizer_queue_process(channel,method_plugin,response_message) == MRCP_STATUS_SUCCESS) {
			response_message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS;
		}
		else {
			response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED;
		}
	}
	else {
		response_message->start_line.request_state = MRCP_REQUEST_STATE_PENDING;
	}

	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t speak_request_id_find(mrcp_request_id_list_t *request_id_list, mrcp_request_id request_id)
{
	size_t i;
	for(i=0; i<request_id_list->count; i++) {
		if(request_id_list->ids[i] == request_id) {
			return MRCP_STATUS_SUCCESS;
		}
	}
	return MRCP_STATUS_FAILURE;
}

static mrcp_status_t synthesizer_stop(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message, mrcp_server_method_plugin_t method_plugin)
{
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	mrcp_request_id_list_t *request_id_list = NULL;
	mrcp_message_t *message;
	apt_list_elem_t *elem;
	if(synth_channel->state == SYNTHESIZER_STATE_IDLE) {
		/* send response with empty complete */
		return MRCP_STATUS_SUCCESS;
	}

	mrcp_generic_header_prepare(response_message);

	if(mrcp_generic_header_property_check(request_message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST) == MRCP_STATUS_SUCCESS) {
		mrcp_generic_header_t *generic_header = request_message->header.generic_header.data;
		if(generic_header->active_request_id_list.ids && generic_header->active_request_id_list.count) {
			/* selective stop request */
			request_id_list = &generic_header->active_request_id_list;
		}
	}

	elem = apt_list_first_elem_get(synth_channel->queue);
	while(elem) {
		message = apt_list_elem_data_get(elem);
		if(!request_id_list || speak_request_id_find(request_id_list,message->start_line.request_id) == MRCP_STATUS_SUCCESS) {
			apt_log(APT_PRIO_DEBUG,"Remove Speak Request [%d]\n",message->start_line.request_id);
			/* append active id list */
			active_request_id_list_append(response_message->header.generic_header.data,message->start_line.request_id);
			if(synth_channel->state != SYNTHESIZER_STATE_IDLE) {
				/* speak request remains in the queue until stop complete response */
				synthesizer_stop_process(channel,method_plugin,request_message,response_message);
				elem = apt_list_next_elem_get(synth_channel->queue,elem);
			}
			else {
				/* remove speak request */
				elem = apt_list_elem_remove(synth_channel->queue,elem);
			}
		}
		else {
			/* speak request remains, just proceed to the next one */
			elem = apt_list_next_elem_get(synth_channel->queue,elem);
		}
	}

	mrcp_generic_header_property_add(response_message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_pause(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message, mrcp_server_method_plugin_t method_plugin)
{
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	if(synth_channel->state != SYNTHESIZER_STATE_IDLE) {
		if(synth_channel->state == SYNTHESIZER_STATE_SPEAKING) {
			mrcp_message_t *speaker = apt_list_head(synth_channel->queue);
			if(!speaker || !method_plugin) {
				response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED;
				return MRCP_STATUS_FAILURE;
			}
			mrcp_generic_header_prepare(response_message);
			active_request_id_list_append(response_message->header.generic_header.data,speaker->start_line.request_id);
			mrcp_generic_header_property_add(response_message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST);

			apt_log(APT_PRIO_DEBUG,"Pause Speak [%d]\n",speaker->start_line.request_id);
			synth_channel->state = SYNTHESIZER_STATE_PAUSED;
			method_plugin(channel,request_message,response_message);
		}
	}
	else {
		response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID;
	}
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_resume(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message, mrcp_server_method_plugin_t method_plugin)
{
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	if(synth_channel->state != SYNTHESIZER_STATE_IDLE) {
		if(synth_channel->state == SYNTHESIZER_STATE_PAUSED) {
			mrcp_message_t *speaker = apt_list_head(synth_channel->queue);
			if(!speaker || !method_plugin) {
				response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED;
				return MRCP_STATUS_FAILURE;
			}
			mrcp_generic_header_prepare(response_message);
			active_request_id_list_append(response_message->header.generic_header.data,speaker->start_line.request_id);
			mrcp_generic_header_property_add(response_message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST);

			apt_log(APT_PRIO_DEBUG,"Resume Speak [%d]\n",speaker->start_line.request_id);
			synth_channel->state = SYNTHESIZER_STATE_SPEAKING;
			method_plugin(channel,request_message,response_message);
		}
	}
	else {
		response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID;
	}
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_barge_in_occurred(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message, mrcp_server_method_plugin_t method_plugin)
{
	mrcp_synthesizer_header_t *synth_header;
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	mrcp_message_t *speaker = apt_list_head(synth_channel->queue);
	apt_bool_t kill_on_barge_in = TRUE;
	if(!speaker) {
		/* send response with empty complete */
		return MRCP_STATUS_SUCCESS;
	}

	apt_log(APT_PRIO_DEBUG,"Speak Barge-In [%d]\n",speaker->start_line.request_id);
	synth_header = speaker->header.resource_header.data;
	if(synth_header) {
		if(mrcp_resource_header_property_check(speaker,SYNTHESIZER_HEADER_KILL_ON_BARGE_IN) == MRCP_STATUS_SUCCESS) {
			kill_on_barge_in = synth_header->kill_on_barge_in;
		}
	}
	
	if(kill_on_barge_in == TRUE) {
		synthesizer_stop(channel,request_message,response_message,method_plugin);
	}
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_control(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message, mrcp_server_method_plugin_t method_plugin)
{
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	if(synth_channel->state == SYNTHESIZER_STATE_SPEAKING) {
		mrcp_message_t *speaker = apt_list_head(synth_channel->queue);
		if(!speaker || !method_plugin) {
			response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED;
			return MRCP_STATUS_FAILURE;
		}
		mrcp_generic_header_prepare(response_message);
		active_request_id_list_append(response_message->header.generic_header.data,speaker->start_line.request_id);
		mrcp_generic_header_property_add(response_message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST);

		apt_log(APT_PRIO_DEBUG,"Control Speak [%d]\n",speaker->start_line.request_id);
		method_plugin(channel,request_message,response_message);
	}
	else {
		response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID;
	}
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_define_lexicon(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message, mrcp_server_method_plugin_t method_plugin)
{
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	if(synth_channel->state == SYNTHESIZER_STATE_IDLE) {
		if(method_plugin) {
			method_plugin(channel,request_message,response_message);
		}
	}
	else {
		response_message->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID;
	}
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_speech_marker(mrcp_server_channel_t *channel, mrcp_message_t *event_message)
{
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	mrcp_message_t *speaker = apt_list_head(synth_channel->queue);
	if(synth_channel->state != SYNTHESIZER_STATE_SPEAKING || !speaker || !event_message) {
		/* unexpected event */
		return MRCP_STATUS_FAILURE;
	}

	if(speaker->start_line.request_id != event_message->start_line.request_id) {
		/* unexpected event */
		return MRCP_STATUS_FAILURE;
	}
	
	apt_log(APT_PRIO_DEBUG,"Speech Marker [%d]\n",speaker->start_line.request_id);
	event_message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS;
	event_message->start_line.status_code = MRCP_STATUS_CODE_SUCCESS;
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_speak_complete(mrcp_server_channel_t *channel, mrcp_message_t *event_message)
{
	mrcp_server_resource_t *resource = channel->resource;
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	mrcp_message_t *speaker = apt_list_head(synth_channel->queue);
	mrcp_synthesizer_header_t *synthesizer_header;
	if(synth_channel->state != SYNTHESIZER_STATE_SPEAKING || !speaker || !event_message) {
		/* unexpected event */
		return MRCP_STATUS_FAILURE;
	}

	if(speaker->start_line.request_id != event_message->start_line.request_id) {
		/* unexpected event */
		return MRCP_STATUS_FAILURE;
	}

	if(!resource) {
		return MRCP_STATUS_FAILURE;
	}
	
	apt_log(APT_PRIO_DEBUG,"Speak Complete [%d]\n",speaker->start_line.request_id);
	event_message->start_line.request_state = MRCP_REQUEST_STATE_COMPLETE;
	synthesizer_header = mrcp_resource_header_prepare(event_message);
	if(synthesizer_header) {
		if(mrcp_resource_header_property_check(event_message,SYNTHESIZER_HEADER_COMPLETION_CAUSE) != MRCP_STATUS_SUCCESS) {
			synthesizer_header->completion_cause = SYNTHESIZER_COMPLETION_CAUSE_NORMAL;
			mrcp_resource_header_property_add(event_message,SYNTHESIZER_HEADER_COMPLETION_CAUSE);
		}
	}

	synth_channel->state = SYNTHESIZER_STATE_IDLE;
	apt_list_pop_front(synth_channel->queue);
	/* process remaining requests */
	synthesizer_queue_process(channel,resource->plugin->method_plugin_array[SYNTHESIZER_SPEAK],NULL);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_response_handler(mrcp_server_channel_t *channel, mrcp_message_t *response_message)
{
	/* processing async response to stop request */
	mrcp_server_resource_t *resource = channel->resource;
	mrcp_synthesizer_channel_t *synth_channel = channel->data;
	mrcp_message_t *speaker = apt_list_head(synth_channel->queue);
	if(synth_channel->state != SYNTHESIZER_STATE_IDLE || !speaker || !response_message) {
		/* unexpected response */
		return MRCP_STATUS_FAILURE;
	}
	if(speaker->start_line.request_id != response_message->start_line.request_id) {
		/* unexpected response */
		return MRCP_STATUS_FAILURE;
	}

	if(!resource) {
		return MRCP_STATUS_FAILURE;
	}

	apt_log(APT_PRIO_DEBUG,"Stop Complete [%d]\n",speaker->start_line.request_id);
	apt_list_pop_front(synth_channel->queue);

	if(channel->state == MRCP_CHANNEL_STATE_CLOSE_REQUESTED) {
		if(resource->plugin->channel_close) {
			resource->plugin->channel_close(resource->plugin,channel);
		}
		channel->state = MRCP_CHANNEL_STATE_CLOSED;
		return MRCP_STATUS_SUCCESS;
	}

	/* process remaining requests */
	synthesizer_queue_process(channel,resource->plugin->method_plugin_array[SYNTHESIZER_SPEAK],NULL);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_setparams(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message, mrcp_server_method_plugin_t method_plugin)
{
	mrcp_header_set(&channel->properties,&request_message->header,request_message->pool);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_getparams(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message, mrcp_server_method_plugin_t method_plugin)
{
	mrcp_header_set(&response_message->header,&request_message->header,response_message->pool);
	mrcp_header_get(&response_message->header,&channel->properties,response_message->pool);
	return MRCP_STATUS_SUCCESS;
}


static mrcp_server_method_handler_t synthesizer_server_method_array[SYNTHESIZER_METHOD_COUNT] = {
	synthesizer_setparams,
	synthesizer_getparams,
	synthesizer_speak,
	synthesizer_stop,
	synthesizer_pause,
	synthesizer_resume,
	synthesizer_barge_in_occurred,
	synthesizer_control,
	synthesizer_define_lexicon
};

static mrcp_server_event_handler_t synthesizer_server_event_array[SYNTHESIZER_EVENT_COUNT] = {
	synthesizer_speech_marker,
	synthesizer_speak_complete
};

/* creates mrcp synthesizer server resource */
mrcp_resource_t* mrcp_synthesizer_server_resource_create(mrcp_version_t version, apr_pool_t *pool)
{
	mrcp_server_resource_t *server_resource = apr_palloc(pool,sizeof(mrcp_server_resource_t));
	mrcp_synthesizer_init(&server_resource->base,version);
	
	server_resource->channel_open = synthesizer_channel_open;
	server_resource->channel_close = synthesizer_channel_close;
	
	server_resource->method_array = synthesizer_server_method_array;
	server_resource->event_array = synthesizer_server_event_array;
	server_resource->response_handler = synthesizer_response_handler;

	server_resource->plugin = NULL;
	return &server_resource->base;
}
