/*
 * Asterisk -- A telephony toolkit for Linux.
 *
 * Trivial application to use cepstral swift tts
 * 
 * Copyright (C) 2004, Andy Powell
 *
 * Andy Powell <asterisk@automated.it>
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License
 */
 
#include <asterisk/lock.h>
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/options.h>
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
#include <asterisk/module.h>
#include <asterisk/translate.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "../asterisk.h"
#include <asterisk/cli.h>
#include <asterisk/utils.h>



#include <swift.h>

static char *tdesc = "Trivial Cepstral TTS Application";

static char *app = "Cepstral";

static char *synopsis = "TTS conversion and playback";

static char *descrip = 
"Cepstral(text):  Converts text to speech using Cepstral Swift engine.\n"
"The default voice is used unless specific tags are included in the\n"
"parameter. Multiple voices in a single command are supported."
"\n\nUsage examples:\n\nexten=> 1,1,Cepstral(hello world)\n\n"
"exten=> 1,1,Cepstral(<voice name=\"William\">hello world</voice>)\n\n"
"\n";

STANDARD_LOCAL_USER;

LOCAL_USER_DECL;

void list_voices(swift_port *port, int fd, const char *required, const char *optional, int listtype);

static int myswift_create_file(char *sayit, char *fname)
{
	swift_engine *engine;
	swift_port *port;
	swift_params *params;
	swift_result_t rv = 0;
	swift_voice *voice;

	/* Open the Swift TTS Engine */
	if ( (engine = swift_engine_open(NULL)) == NULL) {
		ast_log(LOG_WARNING, "Failed to open Swift Engine.\n");
		goto all_done;
	}
    
  /* Open a Swift Port through which to make TTS calls */
  params = swift_params_new("audio/output-file",  swift_val_string(fname), 
			    "audio/sampling-rate", swift_val_int(8000), 
			    NULL);

	if ( (port = swift_port_open(engine, params)) == NULL) {
		ast_log(LOG_WARNING, "Failed to open Swift Port.\n");
		goto all_done;
	}

	if ( NULL == (voice = swift_port_find_first_voice(port, NULL, NULL)) )  {
		ast_log(LOG_WARNING, "Failed to find any voices at all\n");
	} else {
		if  ( SWIFT_FAILED(rv=swift_port_set_voice(port, voice)) ) {
			ast_log(LOG_WARNING, "Failed to set voice: %s\n",swift_strerror(rv));
		
	        } else {
			if (SWIFT_FAILED(rv = swift_port_speak_text(port, sayit, 0, NULL, NULL, NULL))) {
				ast_log(LOG_WARNING, "Failed to speak text: %s\n", swift_strerror(rv));
			}
    		}
   	}	

 all_done:
  if (NULL != port) swift_port_close(port);
  if (NULL != engine) swift_engine_close(engine);
	
  return rv;
}

static int cepstral_exec(struct ast_channel *chan, void *data)
{
	int res = 0;
	struct localuser *u;
	char text[256]="";
	char *c;
	char options[32]="";
	int noanswer = 0;
	char *stringp=NULL;
	char sfname[256]="";
	char nsfname[256]="";
	char hdate[256]="";
	time_t t;
        struct tm tm;
	

	LOCAL_USER_ADD(u);
	
	if (!data || !strlen((char *)data)) {
		ast_log(LOG_WARNING, "Cepstral requires an argument (text)\n");
		return -1;
	}

	stringp=(char*)data;
	c = strsep(&stringp, "|");
	/* get our text here */
	strncpy(text, c, sizeof(text)-1);
	c = strsep(&stringp, "|");
	/* find our options here */
	while(c && strlen(c)) {
		if (!strcasecmp(c, "noanswer"))
			noanswer = 1;
		else
		{
			strncat(text, ",", sizeof(text) - strlen(text));
			strncat(text, c, sizeof(text) -  strlen(text));
		}
		c = strsep(&stringp, "|");
        }
	/* noanswer = 1 we don't do this */
	if (chan->_state != AST_STATE_UP && noanswer == 0) {
		res = ast_answer(chan);
        
	}

//	localtime(&t,&tm,NULL);
	time(&t);
        localtime_r(&t,&tm);
	
	
        strftime(hdate, sizeof(hdate), "%A, %d %B, %Y. %H %M and %S seconds", &tm);

	pbx_builtin_setvar_helper(chan,"HDATETIME",hdate);

	ast_stopstream(chan);
	pbx_substitute_variables_helper(chan, "${UNIQUEID}", options, sizeof(options) - 1);

	snprintf(sfname, sizeof(sfname)-1, "/tmp/%s.wav", options);
	snprintf(nsfname, sizeof(nsfname)-1, "/tmp/%s", options);

	res = myswift_create_file(text,sfname);
	
	if(res == SWIFT_SUCCESS)
	{
		res = ast_safe_sleep(chan, 100);
		if (!res)
		{
			res = ast_streamfile(chan, nsfname, chan->language);
		}
		if (!res)
		{
			res = ast_waitstream(chan, AST_DIGIT_ANY);
		}
		else
		{
			ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
			res = 0;
		}
		ast_stopstream(chan);
	}
	
	LOCAL_USER_REMOVE(u);
	
	ast_filedelete(nsfname,"wav");
	
	return 0; // Always continue to the next exten priority
}

void list_voices(swift_port *port, int fd, const char *required, const char *optional, int listtype) {
        swift_voice *voice;
        const char *license_status;
        char splat[90]="";
        char *output_format  = "%5s %-10s %-9s %-5s %-20s %-11s%s %12s\n";
        char *output_formatb = "%5s %-10s %-9s %-5s %-20s %11s%s %12s\n";
	int llisttype=0;
	int vcounter=0;

        /* Find the first voice on the system */
        if ( (voice = swift_port_find_first_voice(port, required, optional)) == NULL) {
                if (fd != -1) {
                        ast_cli(fd, "Failed to find any voices!\n");
                } else {
                        ast_log(LOG_WARNING, "Failed to find any voices!\n");
                }

                return;
        }
        /* Go through all of the voices on the system and print some info about each */
        if (fd != -1) {
                ast_cli(fd, "\n\nAvailable Cepstral voices:\n\n");
                ast_cli(fd, output_format, " ", "Name", "Gender", "Age", "Language", "Sample Rate","  ", "License");
                ast_cli(fd, output_format, " ", "==========", "=========", "=====", "====================", "===========", "==", "============");
        } else {
               ast_verbose( VERBOSE_PREFIX_2 "Available Cepstral voices for app_cepstral:\n");
        }

        for (; voice; voice = swift_port_find_next_voice(port))
        {
	   if (voice != NULL) {	
	
                if (swift_voice_get_attribute(voice, "license/key")) {
                    license_status = "licensed";
		    llisttype=1;
                } else {
                    license_status = "unlicensed";
		    llisttype=2;
		}
		if ((llisttype == listtype) || (listtype == 0)) {
                	if (fd != -1) {
                        	snprintf(splat, sizeof(splat)-1, output_formatb," ",
	                                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"),
	                                "Hz",
        	                        license_status);
				vcounter++;
                                ast_cli(fd,"%s",splat);
				
	                } else {
	                        ast_verbose( VERBOSE_PREFIX_1 "%s, age %s, %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);
				vcounter++;
			}	
		}                
	   }		
        }

	if (vcounter == 0) {
	        if (fd != -1) {
		        ast_cli(fd, "      No unlicensed voices found.\n\n");
		} else {
			ast_verbose( VERBOSE_PREFIX_1 "No unlicensed voices found\n");
		}	
	}

        if (fd != -1) {
	        ast_cli(fd, output_formatb, " "," "," "," "," "," ", " ", "\n");
	}							
	

}


static int general_voice_conn(int fd, char *portListLimiters, char *portListOrder, int special)
{
        swift_engine *engine;
        swift_port *port = NULL;

        /* Open the Swift TTS Engine */
        if ( (engine = swift_engine_open(NULL)) == NULL ) {
		if (fd != -1) {
	                ast_cli(fd, "Failed to open Swift Engine.\n");
		} else {
			ast_log(LOG_WARNING, "Failed to open Swift Engine\n");
		}	
			
        } else {
        /* Open a Swift Port through which to find voices */
                if ( (port = swift_port_open(engine, NULL)) == NULL ) {
			if (fd != -1) {
	                       	ast_cli(fd, "Failed to open Swift Port.\n");
			} else {
				ast_log(LOG_WARNING, "Failed to open Swift Port\n");
			}
                } else {
			if (special == 0) {
	                        list_voices(port, fd, portListLimiters, portListOrder,0);
			} else {
				list_voices(port, fd, portListLimiters, portListOrder, special);
			}
                }
        }

        if (NULL != port) swift_port_close(port);
        if (NULL != engine) swift_engine_close(engine);


        return 0;
}


static int handle_cli_showvoices_exec(int fd, int argc, char **argv)
{
	general_voice_conn(fd, NULL, NULL,0);
        return 0;
}

static int handle_cli_showmvoices_exec(int fd, int argc, char **argv)
{
        general_voice_conn(fd,"speaker/gender=male", NULL,0);
        return 0;
}

static int handle_cli_showfvoices_exec(int fd, int argc, char **argv)
{
        general_voice_conn(fd,"speaker/gender=female", NULL,0);
        return 0;
}
		
static int handle_cli_shownvoices_exec(int fd, int argc, char **argv)
{
        general_voice_conn(fd,"speaker/gender=neuter", NULL,0);
	return 0;
}

static int handle_cli_showlvoices_exec(int fd, int argc, char **argv)
{
        general_voice_conn(fd, NULL,NULL,1);
	
        return 0;
}
		
static int handle_cli_showuvoices_exec(int fd, int argc, char **argv)
{
        general_voice_conn(fd, NULL, NULL,2);
        return 0;
}
		


static char showvoices_usage[] =
"show cepstral <licensed|male|female|neuter|voices> [voices].\n";

static struct ast_cli_entry cli_showvoices = {
        { "show", "cepstral","voices" }, handle_cli_showvoices_exec,
	        "Show available Cepstral voices.", showvoices_usage, NULL };
		
static struct ast_cli_entry cli_showfvoices = {
        { "show", "cepstral","female","voices" }, handle_cli_showfvoices_exec,
	          "Show available female Cepstral voices.", showvoices_usage, NULL };

static struct ast_cli_entry cli_showmvoices = {
        { "show", "cepstral","male","voices" }, handle_cli_showmvoices_exec,
                  "Show available male Cepstral voices.", showvoices_usage, NULL };
			  
static struct ast_cli_entry cli_shownvoices = {
        { "show", "cepstral","neuter","voices" }, handle_cli_shownvoices_exec,
                  "Show available neuter Cepstral voices.", showvoices_usage, NULL };
			  
static struct ast_cli_entry cli_showlvoices = {
        { "show", "cepstral","licensed","voices" }, handle_cli_showlvoices_exec,
                  "Show available licensed Cepstral voices.", showvoices_usage, NULL };
	
static struct ast_cli_entry cli_showuvoices = {
        { "show", "cepstral","unlicensed","voices" }, handle_cli_showuvoices_exec,
                  "Show available livensed Cepstral voices.", showvoices_usage, NULL };
			  
	
int unload_module(void)
{
	STANDARD_HANGUP_LOCALUSERS;
	ast_cli_unregister(&cli_showvoices);
	return ast_unregister_application(app);
}

int load_module(void)
{
	general_voice_conn(-1,NULL,"name",0);

	ast_cli_register(&cli_showvoices);
        ast_cli_register(&cli_showfvoices);
        ast_cli_register(&cli_showmvoices);
        ast_cli_register(&cli_shownvoices);
        ast_cli_register(&cli_showlvoices);
        ast_cli_register(&cli_showuvoices);

	return ast_register_application(app, cepstral_exec, synopsis, descrip);
}

char *description(void)
{
	return tdesc;
}

int usecount(void)
{
	int res;
	STANDARD_USECOUNT(res);
	return res;
}

char *key()
{
	return ASTERISK_GPL_KEY;
}

