/*
 * 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_server_plugin.h"
#include "mrcp_synthesizer.h"
#include "mrcp_generic_header.h"
#include "mrcp_audio_channel.h"
#include "audio_buffer.h"

#include "swift.h"

typedef struct swift_plugin_t swift_plugin_t;

struct swift_plugin_t {
	mrcp_server_plugin_t base;
	
	/* swift engine */
	swift_engine        *mrcp_swift_engine; 
};


typedef struct swift_channel_t swift_channel_t;

struct swift_channel_t {
	apr_pool_t           *pool;

	int                   running;
	mrcp_message_t       *message;

	audio_buffer_t       *audio_buffer;
	float                 last_write_time;

	/* swift port */
	swift_port           *mrcp_swift_port;
	swift_background_t    mrcp_tts_stream;
};

static const int swift_prosody_volume_table[PROSODY_VOLUME_COUNT] = {
	25,  /* PROSODY_VOLUME_SILENT */
	50,  /* PROSODY_VOLUME_XSOFT */
	75,  /* PROSODY_VOLUME_SOFT */
	100, /* PROSODY_VOLUME_MEDIUM */
	125, /* PROSODY_VOLUME_LOUD */
	150, /* PROSODY_VOLUME_XLOUD */
	100  /* PROSODY_VOLUME_DEFAULT */
};

static const int swift_prosody_rate_table[PROSODY_RATE_COUNT] = {
	85,  /* PROSODY_RATE_XSLOW */
	113, /* PROSODY_RATE_SLOW */
	170, /* PROSODY_RATE_MEDIUM */
	225, /* PROSODY_RATE_FAST */
	340, /* PROSODY_RATE_XFAST */
	170  /* PROSODY_RATE_DEFAULT */
};


static swift_result_t swift_write_audio(swift_event *event, swift_event_t type, void *udata);
static apr_status_t synthesizer_read_frame(audio_source_t *source, media_frame_t *frame);

static const audio_source_method_set_t audio_source_method_set = {
	NULL,
	NULL,
	NULL,
	synthesizer_read_frame
};

static void swift_list_voices(swift_engine *engine) 
{
	swift_port *port;
	swift_voice *voice;
	const char *license_status;

	/* Open swift port*/ 
	if((port = swift_port_open(engine, NULL)) == NULL) 
	{
		apt_log(APT_PRIO_WARNING,"Failed to Open Swift Port\n");
		return;    
	}

	/* Find the first voice on the system */
	if((voice = swift_port_find_first_voice(port, NULL, NULL)) == NULL) {
		apt_log(APT_PRIO_WARNING,"Failed to Find Any Voices!");
		swift_port_close(port);
		return;
	}
	/* Go through all of the voices on the system and print some info about each */
	apt_log(APT_PRIO_INFO,"Swift Available Voices:\n");
	for(; voice; voice = swift_port_find_next_voice(port))
	{
		if (swift_voice_get_attribute(voice, "license/key"))
			 license_status = "licensed";
		else
			 license_status = "unlicensed";
		apt_log(APT_PRIO_INFO,"- %s: %s, age %s, %s, %sHz, %s\n",
			   swift_voice_get_attribute(voice, "name"),
			   swift_voice_get_attribute(voice, "speaker/gender"),
			   swift_voice_get_attribute(voice, "speaker/age"),
			   swift_voice_get_attribute(voice, "language/name"),
			   swift_voice_get_attribute(voice, "sample-rate"),
			   license_status);
	}

	swift_port_close(port);
}

static mrcp_module_state_t synthesizer_on_engine_open(mrcp_module_t *module)
{   
	swift_plugin_t *swift_plugin = (swift_plugin_t*)module;
	
	/* Open swift engine */
	swift_engine * engine = NULL;
	apt_log(APT_PRIO_INFO,"Open Swift Engine\n");
	if ((engine = swift_engine_open(NULL)) ==  NULL) {
		apt_log(APT_PRIO_WARNING,"Failed to Open Swift Engine\n");
		return MRCP_STATUS_FAILURE;
	}
	swift_list_voices(engine);
	swift_plugin->mrcp_swift_engine = engine;
	return MODULE_STATE_OPENED;
}

static mrcp_module_state_t synthesizer_on_engine_close(mrcp_module_t *module)
{
	swift_plugin_t *swift_plugin = (swift_plugin_t*)module;
	/* Close swift engine */
	apt_log(APT_PRIO_INFO,"Close Swift Engine\n");
	if(NULL != swift_plugin->mrcp_swift_engine)
		swift_engine_close(swift_plugin->mrcp_swift_engine); 
	return MODULE_STATE_CLOSED;
}

static mrcp_status_t synthesizer_on_channel_open(mrcp_server_plugin_t *server_plugin, mrcp_server_channel_t *channel, apr_pool_t *pool)
{
	audio_source_t *audio_source;
	swift_params *params;
	swift_plugin_t *swift_plugin = (swift_plugin_t*)server_plugin;
	swift_channel_t *synth_channel = apr_palloc(pool,sizeof(swift_channel_t));
	synth_channel->pool = pool;
	mrcp_server_channel_handle_set(channel,synth_channel);

	params = swift_params_new(NULL);
	swift_params_set_string(params, "audio/encoding", "pcm16");
	swift_params_set_int(params, "audio/sampling-rate", 8000);
	/* Open swift port */ 
	apt_log(APT_PRIO_INFO,"Open Swift Port\n");
	if((synth_channel->mrcp_swift_port = swift_port_open(swift_plugin->mrcp_swift_engine, params)) == NULL) 
	{
		apt_log(APT_PRIO_WARNING,"Failed to Open Swift Port\n");
		return MRCP_STATUS_FAILURE;    
	}

	/* Set swift_write_audio as a callback, with the output file as its param */
	swift_port_set_callback(synth_channel->mrcp_swift_port,&swift_write_audio,SWIFT_EVENT_AUDIO | SWIFT_EVENT_END,channel);

	audio_source = apr_palloc(pool,sizeof(audio_source_t));
	audio_source->object = synth_channel;
	audio_source->method_set = &audio_source_method_set;
	mrcp_server_channel_audio_source_set(channel,audio_source);

	synth_channel->audio_buffer = audio_buffer_create(3200,pool);

	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_on_channel_close(mrcp_server_plugin_t *server_plugin, mrcp_server_channel_t *channel)
{
	swift_channel_t *synth_channel = mrcp_server_channel_handle_get(channel);
	apt_log(APT_PRIO_INFO,"Close Swift Port\n");
	if(NULL != synth_channel->mrcp_swift_port) 
		/* Close swift port */ 
		swift_port_close(synth_channel->mrcp_swift_port);
	return MRCP_STATUS_SUCCESS;
}

static swift_result_t swift_write_audio(swift_event *event, swift_event_t type, void *udata)
{
	void *buf;
	int len;
	mrcp_server_channel_t *channel = udata;
	swift_channel_t *synth_channel = mrcp_server_channel_handle_get(channel);
	swift_event_t rv = SWIFT_SUCCESS;

	if(!synth_channel->running) {
		return rv;
	}

	if(type & SWIFT_EVENT_END) {
		audio_buffer_wait_for_read_complete(synth_channel->audio_buffer);
		if(synth_channel->running) {
			mrcp_message_t *message = mrcp_event_create(synth_channel->message,SYNTHESIZER_SPEAK_COMPLETE,synth_channel->pool);
			apt_log(APT_PRIO_INFO,"Swift SPEAK-COMPLETE\n");
			mrcp_server_channel_signal(channel,message);

			synth_channel->running = 0;
		}
		return rv;
	}

	rv = swift_event_get_audio(event, &buf, &len);
	if(!SWIFT_FAILED(rv)) {
		/* Get the event times */
		float time_start, time_len; 
		swift_event_get_times(event, &time_start, &time_len);
#if 0
		apt_log(APT_PRIO_DEBUG,"Swift Engine: Write Audio [%d | %0.4f | %0.4f]\n",len, time_start, time_len);
#endif
		if(time_start > synth_channel->last_write_time) {
			char silence_buf[1500];
			apr_size_t silence_len = (apr_size_t)(time_start - synth_channel->last_write_time) * 16;
			if(silence_len > sizeof(silence_buf)) {
				silence_len = sizeof(silence_buf);
			}
			memset(silence_buf,0,silence_len);
			audio_buffer_write(synth_channel->audio_buffer,silence_buf,silence_len);
		}
		synth_channel->last_write_time = time_start + time_len;
		audio_buffer_write(synth_channel->audio_buffer,buf,len);
	}

	return rv;
}

static apr_status_t synthesizer_read_frame(audio_source_t *source, media_frame_t *frame)
{
	swift_channel_t *synth_channel = source->object;

	frame->type = MEDIA_FRAME_TYPE_AUDIO;
	audio_buffer_read(synth_channel->audio_buffer,frame->codec_frame.buffer,frame->codec_frame.size);
	return APR_SUCCESS;
}

static APR_INLINE size_t search_criteria_operator_add(char *search_criteria, int initial)
{
	size_t offset = 0;
	if(!initial) {
		offset += sprintf(search_criteria," & ");
	}
	return offset;
}

static mrcp_status_t swift_channel_set_voice(swift_channel_t *synth_channel, mrcp_message_t *message)
{
	mrcp_synthesizer_header_t *synth_header = message->header.resource_header.data;
	char search_criteria[1024] = "";
	size_t offset = 0;
	swift_voice *voice;
	swift_result_t res;

	if(!synth_header) {
		/* no params to set */
		return MRCP_STATUS_SUCCESS;
	}

	if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_NAME) == MRCP_STATUS_SUCCESS) {
		offset += search_criteria_operator_add(search_criteria+offset,offset == 0);
		offset += sprintf(search_criteria+offset,"speaker/name=%s",synth_header->voice_param.name);
	}
	if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_GENDER) == MRCP_STATUS_SUCCESS) {
		switch(synth_header->voice_param.gender) {
			case VOICE_GENDER_MALE:
				offset += search_criteria_operator_add(search_criteria+offset,offset == 0);
				offset += sprintf(search_criteria+offset,"speaker/gender=male");
				break;
			case VOICE_GENDER_FEMALE:
				offset += search_criteria_operator_add(search_criteria+offset,offset == 0);
				offset += sprintf(search_criteria+offset,"speaker/gender=female");
				break;
			default:
				break;
		}
	}
	if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_AGE) == MRCP_STATUS_SUCCESS) {
		offset += search_criteria_operator_add(search_criteria+offset,offset == 0);
		offset += sprintf(search_criteria+offset,"speaker/age=%d",synth_header->voice_param.age);
	}
	if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_SPEECH_LANGUAGE) == MRCP_STATUS_SUCCESS) {
		offset += search_criteria_operator_add(search_criteria+offset,offset == 0);
		offset += sprintf(search_criteria+offset,"language/name=%s",synth_header->speech_language);
	}

	if(offset > 0) {
		apt_log(APT_PRIO_DEBUG,"Find Voices Matching the Criteria %s\n",search_criteria);
		if((voice = swift_port_find_first_voice(synth_channel->mrcp_swift_port,search_criteria,NULL)) == NULL) {
			apt_log(APT_PRIO_INFO,"Failed to Find Any Voices Matching the Criteria %s!\n",search_criteria);
			voice = swift_port_find_first_voice(synth_channel->mrcp_swift_port,NULL,NULL);
		}
		if(SWIFT_FAILED(res = swift_port_set_voice(synth_channel->mrcp_swift_port,voice)) ) {
			const char *error_string = swift_strerror(res);
			apt_log(APT_PRIO_WARNING,"%s\n",error_string);
			return MRCP_STATUS_FAILURE;
		} 
	}
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t swift_channel_set_param(swift_channel_t *synth_channel, const char *name, swift_val *val)
{
	swift_result_t res;
	if(SWIFT_FAILED(res = swift_port_set_param(synth_channel->mrcp_swift_port,name,val,synth_channel->mrcp_tts_stream)) ) {
		const char *error_string = swift_strerror(res);
		apt_log(APT_PRIO_WARNING,"Swift Param %s: %s\n",name,error_string);
		return MRCP_STATUS_FAILURE;
	}
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t swift_channel_set_params(swift_channel_t *synth_channel, mrcp_message_t *message)
{
	mrcp_synthesizer_header_t *synth_header = message->header.resource_header.data;
	mrcp_generic_header_t *generic_header = message->header.generic_header.data;
	const char *name;

	if(synth_header) {
		if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_PROSODY_VOLUME) == MRCP_STATUS_SUCCESS) {
			if(synth_header->prosody_param.volume < PROSODY_VOLUME_COUNT) {
				int volume = swift_prosody_volume_table[synth_header->prosody_param.volume];
				name = "audio/volume";
				apt_log(APT_PRIO_DEBUG,"Swift Param %s=%d\n",name,volume);
				swift_channel_set_param(synth_channel,name,swift_val_int(volume));
			}
		}
		if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_PROSODY_RATE) == MRCP_STATUS_SUCCESS) {
			if(synth_header->prosody_param.rate < PROSODY_RATE_COUNT) {
				int rate = swift_prosody_rate_table[synth_header->prosody_param.rate];
				name = "speech/rate";
				apt_log(APT_PRIO_DEBUG,"Swift Param %s=%d\n",name,rate);
				swift_channel_set_param(synth_channel,name,swift_val_int(rate));
			}
		}
	}

	if(generic_header) {
		if(mrcp_generic_header_property_check(message,GENERIC_HEADER_CONTENT_TYPE) == MRCP_STATUS_SUCCESS) {
			name = "tts/content-type";
			apt_log(APT_PRIO_DEBUG,"Swift Param %s=%s\n",name,generic_header->content_type);
			swift_channel_set_param(synth_channel,name,swift_val_string(generic_header->content_type));
		}
		if(mrcp_generic_header_property_check(message,GENERIC_HEADER_CONTENT_ENCODING) == MRCP_STATUS_SUCCESS) {
			name = "tts/text-encoding";
			apt_log(APT_PRIO_DEBUG,"Swift Param %s=%s\n",name,generic_header->content_encoding);
			swift_channel_set_param(synth_channel,name,swift_val_string(generic_header->content_encoding));
		}
	}
	
	return MRCP_STATUS_SUCCESS;
}


static mrcp_status_t synthesizer_on_speak(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	swift_channel_t *synth_channel = mrcp_server_channel_handle_get(channel);

	swift_channel_set_voice(synth_channel,request_message);
	swift_channel_set_params(synth_channel,request_message);
	apt_log(APT_PRIO_INFO,"Swift %s\n",request_message->start_line.method_name);
	if(request_message->body) {
		apt_log(APT_PRIO_DEBUG,"%s\n",request_message->body);
	}

	synth_channel->message = request_message;
	synth_channel->running = 1;
	audio_buffer_open(synth_channel->audio_buffer);
	swift_port_speak_text(synth_channel->mrcp_swift_port, request_message->body, 0, NULL, &synth_channel->mrcp_tts_stream, NULL);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_on_stop(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	swift_channel_t *synth_channel = mrcp_server_channel_handle_get(channel);
	if(!synth_channel->running) {
		return MRCP_STATUS_FAILURE;
	}
	if(request_message) {
		apt_log(APT_PRIO_INFO,"Swift %s\n",request_message->start_line.method_name);
	}
	
	synth_channel->running = 0;
	audio_buffer_close(synth_channel->audio_buffer);
	swift_port_stop(synth_channel->mrcp_swift_port,synth_channel->mrcp_tts_stream,SWIFT_EVENT_NOW);
	if(synth_channel->message) {
		mrcp_message_t *message = mrcp_response_create(synth_channel->message,synth_channel->pool);
		apt_log(APT_PRIO_INFO,"Swift STOP Complete\n");
		mrcp_server_channel_signal(channel,message);
	}
	synth_channel->message = NULL;
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_on_pause(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	swift_channel_t *synth_channel = mrcp_server_channel_handle_get(channel);
	if(!synth_channel->running) {
		return MRCP_STATUS_FAILURE;
	}
	apt_log(APT_PRIO_INFO,"Swift %s\n",request_message->start_line.method_name);
	audio_buffer_pause(synth_channel->audio_buffer);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_on_resume(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	swift_channel_t *synth_channel = mrcp_server_channel_handle_get(channel);
	if(!synth_channel->running) {
		return MRCP_STATUS_FAILURE;
	}
	apt_log(APT_PRIO_INFO,"Swift %s\n",request_message->start_line.method_name);
	audio_buffer_resume(synth_channel->audio_buffer);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_on_barge_in_occurred(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	synthesizer_on_stop(channel,request_message,response_message);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_on_control(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	swift_channel_t *synth_channel = mrcp_server_channel_handle_get(channel);
	apt_log(APT_PRIO_INFO,"Swift %s\n",request_message->start_line.method_name);

	swift_channel_set_params(synth_channel,request_message);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_on_define_lexicon(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	apt_log(APT_PRIO_INFO,"Swift %s\n",request_message->start_line.method_name);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_on_setparams(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	apt_log(APT_PRIO_INFO,"Swift %s\n",request_message->start_line.method_name);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_status_t synthesizer_on_getparams(mrcp_server_channel_t *channel, mrcp_message_t *request_message, mrcp_message_t *response_message)
{
	apt_log(APT_PRIO_INFO,"Swift %s\n",request_message->start_line.method_name);
	return MRCP_STATUS_SUCCESS;
}

static mrcp_server_method_plugin_t synthesizer_method_plugin_array[] = {
	synthesizer_on_setparams,
	synthesizer_on_getparams,
	synthesizer_on_speak,
	synthesizer_on_stop,
	synthesizer_on_pause,
	synthesizer_on_resume,
	synthesizer_on_barge_in_occurred,
	synthesizer_on_control,
	synthesizer_on_define_lexicon
};


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

static mrcp_module_method_set_t module_method_set = {
	synthesizer_destroy,
	synthesizer_on_engine_open,
	synthesizer_on_engine_close,
	NULL /*process*/
};

MRCP_PLUGIN_DECLARE(mrcp_server_plugin_t*) mrcp_synthesizer_plugin_create(apr_pool_t *pool)
{
	swift_plugin_t *swift_plugin = apr_palloc(pool,sizeof(swift_plugin_t));

	mrcp_module_init(&swift_plugin->base.module,&module_method_set);

	swift_plugin->base.channel_open = synthesizer_on_channel_open;
	swift_plugin->base.channel_close = synthesizer_on_channel_close;
	swift_plugin->base.method_plugin_array = synthesizer_method_plugin_array;
	swift_plugin->base.method_count = SYNTHESIZER_METHOD_COUNT;

	/* Set log priority for the plugin (should be configurable) */
	apt_log_priority_set(APT_PRIO_DEBUG);
	
	return &swift_plugin->base;
}
