/** \brief Unload a local grammar */
static int uni_recog_unload_grammar(struct ast_speech *speech, ast_compat_const char *grammar_name)
{
	uni_speech_t *uni_speech = speech->data;
	mrcp_message_t *mrcp_message;
	mrcp_generic_header_t *generic_header;

	if(uni_speech->is_inprogress) {
		uni_recog_stop(speech);
	}

	ast_log(LOG_NOTICE, "Unload grammar name:%s '%s'\n",
				grammar_name,
				uni_speech_id_get(uni_speech));

	apr_hash_set(uni_speech->active_grammars,grammar_name,APR_HASH_KEY_STRING,NULL);

	mrcp_message = mrcp_application_message_create(
								uni_speech->session,
								uni_speech->channel,
								RECOGNIZER_DEFINE_GRAMMAR);
	if(!mrcp_message) {
		ast_log(LOG_WARNING, "Failed to create MRCP message\n");
		return -1;
	}
	
	/* Get/allocate generic header */
	generic_header = mrcp_generic_header_prepare(mrcp_message);
	if(generic_header) {
		/* Set generic header fields */
		apt_string_assign(&generic_header->content_id,grammar_name,mrcp_message->pool);
		mrcp_generic_header_property_add(mrcp_message,GENERIC_HEADER_CONTENT_ID);
	}

	/* Send MRCP request and wait for response */
	if(uni_recog_mrcp_request_send(uni_speech,mrcp_message) != TRUE) {
		ast_log(LOG_WARNING, "Failed to send MRCP message\n");
		return -1;
	}

	/* Check received response */
	if(!uni_speech->mrcp_response || uni_speech->mrcp_response->start_line.status_code != MRCP_STATUS_CODE_SUCCESS) {
		ast_log(LOG_WARNING, "Received failure response\n");
		return -1;
	}
	return 0;
}
/** \brief Set properties */
static apt_bool_t uni_recog_properties_set(uni_speech_t *uni_speech)
{
	mrcp_message_t *mrcp_message;
	mrcp_message_header_t *properties;
	ast_log(LOG_DEBUG, "Set properties '%s'\n",uni_speech_id_get(uni_speech));
	mrcp_message = mrcp_application_message_create(
								uni_speech->session,
								uni_speech->channel,
								RECOGNIZER_SET_PARAMS);
	if(!mrcp_message) {
		ast_log(LOG_WARNING, "Failed to create MRCP message\n");
		return FALSE;
	}
	
	/* Inherit properties loaded from config */
	if(mrcp_message->start_line.version == MRCP_VERSION_2) {
		properties = uni_engine.v2_properties;
	}
	else {
		properties = uni_engine.v1_properties;
	}

	if(properties) {
#if defined(TRANSPARENT_HEADER_FIELDS_SUPPORT)
		mrcp_header_fields_inherit(&mrcp_message->header,properties,mrcp_message->pool);
#else
		mrcp_message_header_inherit(&mrcp_message->header,properties,mrcp_message->pool);
#endif
	}

	/* Send MRCP request and wait for response */
	if(uni_recog_mrcp_request_send(uni_speech,mrcp_message) != TRUE) {
		ast_log(LOG_WARNING, "Failed to send MRCP message\n");
		return FALSE;
	}

	/* Check received response */
	if(!uni_speech->mrcp_response || uni_speech->mrcp_response->start_line.status_code != MRCP_STATUS_CODE_SUCCESS) {
		ast_log(LOG_WARNING, "Received failure response\n");
		return FALSE;
	}
	return TRUE;
}
/*! \brief Stop the in-progress recognition */
static int uni_recog_stop(struct ast_speech *speech)
{
	uni_speech_t *uni_speech = speech->data;
	mrcp_message_t *mrcp_message;
	
	if(!uni_speech->is_inprogress) {
		return 0;
	}

	ast_log(LOG_NOTICE, "Stop recognition '%s'\n",uni_speech_id_get(uni_speech));
	mrcp_message = mrcp_application_message_create(
								uni_speech->session,
								uni_speech->channel,
								RECOGNIZER_STOP);
	if(!mrcp_message) {
		ast_log(LOG_WARNING, "Failed to create MRCP message\n");
		return -1;
	}
	
	/* Reset last event (if any) */
	uni_speech->mrcp_event = NULL;

	/* Send MRCP request and wait for response */
	if(uni_recog_mrcp_request_send(uni_speech,mrcp_message) != TRUE) {
		ast_log(LOG_WARNING, "Failed to send MRCP message\n");
		return -1;
	}

	/* Check received response */
	if(!uni_speech->mrcp_response || uni_speech->mrcp_response->start_line.status_code != MRCP_STATUS_CODE_SUCCESS) {
		ast_log(LOG_WARNING, "Received failure response\n");
		return -1;
	}
	
	/* Reset media buffer */
	mpf_frame_buffer_restart(uni_speech->media_buffer);
	
	ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
	
	uni_speech->is_inprogress = FALSE;
	return 0;
}
/*! \brief Stop the in-progress recognition */
static int uni_recog_stop(struct ast_speech *speech)
{
	uni_speech_t *uni_speech = speech->data;
	mrcp_message_t *mrcp_message;

	if(!uni_speech->is_inprogress) {
		return 0;
	}

	ast_log(LOG_NOTICE, "(%s) Stop recognition\n",uni_speech->name);
	mrcp_message = mrcp_application_message_create(
								uni_speech->session,
								uni_speech->channel,
								RECOGNIZER_STOP);
	if(!mrcp_message) {
		ast_log(LOG_WARNING, "(%s) Failed to create MRCP message\n",uni_speech->name);
		return -1;
	}

	/* Reset last event (if any) */
	uni_speech->mrcp_event = NULL;

	/* Send MRCP request and wait for response */
	if(uni_recog_mrcp_request_send(uni_speech,mrcp_message) != TRUE) {
		ast_log(LOG_WARNING, "(%s) Failed to stop recognition\n",uni_speech->name);
		return -1;
	}

	/* Reset media buffer */
	mpf_frame_buffer_restart(uni_speech->media_buffer);

	ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);

	uni_speech->is_inprogress = FALSE;
	return 0;
}
/** brief Prepare engine to accept audio */
static int uni_recog_start(struct ast_speech *speech)
{
	uni_speech_t *uni_speech = speech->data;
	mrcp_message_t *mrcp_message;
	mrcp_generic_header_t *generic_header;
	mrcp_recog_header_t *recog_header;

	if(uni_speech->is_inprogress) {
		uni_recog_stop(speech);
	}

	ast_log(LOG_NOTICE, "Start audio '%s'\n",uni_speech_id_get(uni_speech));
	mrcp_message = mrcp_application_message_create(
								uni_speech->session,
								uni_speech->channel,
								RECOGNIZER_RECOGNIZE);
	if(!mrcp_message) {
		ast_log(LOG_WARNING, "Failed to create MRCP message\n");
		return -1;
	}
	
	/* Get/allocate generic header */
	generic_header = mrcp_generic_header_prepare(mrcp_message);
	if(generic_header) {
		apr_hash_index_t *it;
		void *val;
		const char *grammar_name;
		const char *content = NULL;
		/* Set generic header fields */
		apt_string_assign(&generic_header->content_type,"text/uri-list",mrcp_message->pool);
		mrcp_generic_header_property_add(mrcp_message,GENERIC_HEADER_CONTENT_TYPE);

		/* Construct and set message body */
		it = apr_hash_first(mrcp_message->pool,uni_speech->active_grammars);
		if(it) {
			apr_hash_this(it,NULL,NULL,&val);
			grammar_name = val;
			content = apr_pstrcat(mrcp_message->pool,"session:",grammar_name,NULL);
			it = apr_hash_next(it);
		}
		for(; it; it = apr_hash_next(it)) {
			apr_hash_this(it,NULL,NULL,&val);
			grammar_name = val;
			content = apr_pstrcat(mrcp_message->pool,content,"\nsession:",grammar_name,NULL);
		}
		if(content) {
			apt_string_set(&mrcp_message->body,content);
		}
	}

	/* Get/allocate recognizer header */
	recog_header = (mrcp_recog_header_t*) mrcp_resource_header_prepare(mrcp_message);
	if(recog_header) {
		/* Set recognizer header fields */
		if(mrcp_message->start_line.version == MRCP_VERSION_2) {
			recog_header->cancel_if_queue = FALSE;
			mrcp_resource_header_property_add(mrcp_message,RECOGNIZER_HEADER_CANCEL_IF_QUEUE);
		}
		recog_header->start_input_timers = TRUE;
		mrcp_resource_header_property_add(mrcp_message,RECOGNIZER_HEADER_START_INPUT_TIMERS);
	}

	/* Reset last event (if any) */
	uni_speech->mrcp_event = NULL;

	/* Send MRCP request and wait for response */
	if(uni_recog_mrcp_request_send(uni_speech,mrcp_message) != TRUE) {
		ast_log(LOG_WARNING, "Failed to send MRCP message\n");
		return -1;
	}

	/* Check received response */
	if(!uni_speech->mrcp_response || uni_speech->mrcp_response->start_line.status_code != MRCP_STATUS_CODE_SUCCESS) {
		ast_log(LOG_WARNING, "Received failure response\n");
		return -1;
	}
	
	/* Reset media buffer */
	mpf_frame_buffer_restart(uni_speech->media_buffer);
	
	ast_speech_change_state(speech, AST_SPEECH_STATE_READY);
	
	uni_speech->is_inprogress = TRUE;
	return 0;
}
/*! \brief Load a local grammar on the speech structure */
static int uni_recog_load_grammar(struct ast_speech *speech, ast_compat_const char *grammar_name, ast_compat_const char *grammar_path)
{
	uni_speech_t *uni_speech = speech->data;
	mrcp_message_t *mrcp_message;
	mrcp_generic_header_t *generic_header;
	const char *content_type = NULL;
	apt_bool_t inline_content = FALSE;
	char *tmp;
	apr_file_t *file;
	apt_str_t *body = NULL;

	mrcp_message = mrcp_application_message_create(
								uni_speech->session,
								uni_speech->channel,
								RECOGNIZER_DEFINE_GRAMMAR);
	if(!mrcp_message) {
		ast_log(LOG_WARNING, "Failed to create MRCP message\n");
		return -1;
	}

	/* 
	 * Grammar name and path are mandatory attributes, 
	 * grammar type can be optionally specified with path.
	 *
	 * SpeechLoadGrammar(name|path)
	 * SpeechLoadGrammar(name|type:path)
	 * SpeechLoadGrammar(name|uri:path)
	 * SpeechLoadGrammar(name|builtin:grammar/digits)
	 */

	tmp = strchr(grammar_path,':');
	if(tmp) {
		const char builtin_token[] = "builtin";
		const char uri_token[] = "uri";
		if(strncmp(grammar_path,builtin_token,sizeof(builtin_token)-1) == 0) {
			content_type = "text/uri-list";
			inline_content = TRUE;
		}
		else if(strncmp(grammar_path,uri_token,sizeof(uri_token)-1) == 0) {
			content_type = "text/uri-list";
			inline_content = TRUE;
			grammar_path = tmp+1;
		}
		else {
			*tmp = '\0';
			content_type = grammar_path;
			grammar_path = tmp+1;
		}
	}

	if(inline_content == TRUE) {
		body = &mrcp_message->body;
		apt_string_assign(body,grammar_path,mrcp_message->pool);
	}
	else {
		if(apr_file_open(&file,grammar_path,APR_FOPEN_READ|APR_FOPEN_BINARY,0,mrcp_message->pool) == APR_SUCCESS) {
			apr_finfo_t finfo;
			if(apr_file_info_get(&finfo,APR_FINFO_SIZE,file) == APR_SUCCESS) {
				/* Read message body */
				body = &mrcp_message->body;
				body->buf = apr_palloc(mrcp_message->pool,finfo.size+1);
				body->length = (apr_size_t)finfo.size;
				if(apr_file_read(file,body->buf,&body->length) != APR_SUCCESS) {
					ast_log(LOG_WARNING, "Failed to read the content of grammar file: %s\n",grammar_path);
				}
				body->buf[body->length] = '\0';
			}
			apr_file_close(file);
		}
		else {
			ast_log(LOG_WARNING, "No such grammar file available: %s\n",grammar_path);
			return -1;
		}
	}

	if(!body || !body->buf) {
		ast_log(LOG_WARNING, "No content available: %s\n",grammar_path);
		return -1;
	}

	/* Try to implicitly detect content type, if it's not specified */
	if(!content_type) {
		if(strstr(body->buf,"#JSGF")) {
			content_type = "application/x-jsgf";
		}
		else if(strstr(body->buf,"#ABNF")) {
			content_type = "application/srgs";
		}
		else {
			content_type = "application/srgs+xml";
		}
	}

	ast_log(LOG_NOTICE, "Load grammar name:%s type:%s path:%s '%s'\n",
				grammar_name,
				content_type,
				grammar_path,
				uni_speech_id_get(uni_speech));
	/* Get/allocate generic header */
	generic_header = mrcp_generic_header_prepare(mrcp_message);
	if(generic_header) {
		/* Set generic header fields */
		apt_string_assign(&generic_header->content_type,content_type,mrcp_message->pool);
		mrcp_generic_header_property_add(mrcp_message,GENERIC_HEADER_CONTENT_TYPE);
		apt_string_assign(&generic_header->content_id,grammar_name,mrcp_message->pool);
		mrcp_generic_header_property_add(mrcp_message,GENERIC_HEADER_CONTENT_ID);
	}

	/* Send MRCP request and wait for response */
	if(uni_recog_mrcp_request_send(uni_speech,mrcp_message) != TRUE) {
		ast_log(LOG_WARNING, "Failed to send MRCP message\n");
		return -1;
	}

	/* Check received response */
	if(!uni_speech->mrcp_response || uni_speech->mrcp_response->start_line.status_code != MRCP_STATUS_CODE_SUCCESS) {
		ast_log(LOG_WARNING, "Received failure response\n");
		return -1;
	}
	return 0;
}