/*-----------------------------------------------------------------------*
 *                                                                       *
 *                   CU Communicator Sphinx-II Server                    * 
 *									 *
 *   This code is based on the example TTY interface to Sphinx provided  *
 *   M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University.     *
 *                                                                       *
 *   Bryan Pellom (pellom@cslr.colorado.edu)                             *
 *   Center for Spoken Language Research	                         *
 *   University of Colorado at Boulder                                   *
 *   (c) 1999,2000 All Rights Reserved                                   *
 *                                                                       *
 *-----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/time.h>

#include <galaxy/galaxy_all.h>
#define SERVER_FUNCTIONS_INCLUDE "recognizer_serv_server.h"
#define USE_SERVER_DATA
#include <galaxy/server_functions.h>

/*-----------------------------------------------------------------------*
 * These includes are required for ISIP Recognizer			 * 
 *-----------------------------------------------------------------------*/

#include "isip_recognizer.h"

// declare static variables
//
long fr;
char *hyp;
int   sample_rate = 8000;
char *sys_id, sys_id_value[100];
int   server_active, total_samples, server_enabled;
float audio_duration_sec, cpu_seconds_elapsed;
clock_t time1, time2;

/*-----------------------------------------------------------------------*
 * Usage Information 							 *
 *-----------------------------------------------------------------------*/

static char *oas[] = {
   "-p filename", "use a configuration file for ISIP Recognizer", NULL,
   "-sysid   string", "value of sysid key to represent decoder", "si",
   NULL
};

/*-----------------------------------------------------------------------*
 * Define statements for receiving audio from the MIT/MITRE audio server * 
 *-----------------------------------------------------------------------*/

#define AUDIO_SERVER_NEW_UTT "new_utt"
#define AUDIO_SERVER_SAMPLES_DONE "done"
#define AUDIO_SERVER_SAMPLES_ABORT "cancel"

#define AUDIO_CLIENT_PLAY_SAMPLES_DONE "AUDIO_CLIENT_PLAY_SAMPLES_DONE"
#define AUDIO_CLIENT_PLAY_SAMPLES_START "AUDIO_CLIENT_PLAY_START"

typedef struct __DataHandler {
  GalIO_CommStruct *server_data;
  char *data_buf;
  GalIO_CommStruct *gcomm;
  int size;
} DataHandler;

/*-----------------------------------------------------------------------*
 * This dispatch function tells the recognizer that it is granted 	 *
 * permission to decode the incoming audio stream.			 * 
 *-----------------------------------------------------------------------*/

Gal_Frame enable(Gal_Frame f, void *server_data){
   server_enabled = 1;
   return(f);
}

/*-----------------------------------------------------------------------*
 * This dispatch function tells the recognizer that it is NOT granted 	 *
 * permission to decode the incoming audio stream.			 * 
 *-----------------------------------------------------------------------*/

Gal_Frame disable(Gal_Frame f, void *server_data){

   printf("****************** THIS SERVER IS BEING DISABLED\n");
   fflush(stdout);
   server_enabled = 0;
   return(f);
}

/*-----------------------------------------------------------------------*
 * Routine to send the decoded speech to the Hub			 * 
 *-----------------------------------------------------------------------*/

static void return_recognized_speech_to_hub(DataHandler *c, char *hyp) {

  int len, i, j;
  Gal_Frame f = Gal_MakeFrame("main", GAL_CLAUSE);

  // ---- remove frame and likelihood information from hypothesis ------

  len = strlen(hyp);
  i = 0; 
  j = 0;
  
  while (i < len) {
    
    // if the character is alpha
    //
    if (isalpha(hypo[i]) {
      
      // remove SENT_START and SENT_END
      //
      if (hyp[i - 1] == '!') {
	do {
	  i++;
	} while ( (i < len) && isalpha(hyp[i]) );
      }
      // save the hypothesis words
      //
      else {
	do {
	  hyp[j++] = hyp[i++];
	} while ((i < len) && isalpha(hyp[i]));
	    
	// add a space between words
	//
	hyp[j++] = ' ';
      }
    }
	i++;
	}
    
    // ---- send frame to hub with recognizer output ----------

    Gal_SetProp(f, ":input_string", Gal_StringObject(hyp));
    GalIO_CommWriteFrame((GalIO_CommStruct *) c->server_data, f, 0);
}


/*-----------------------------------------------------------------------*
 * Routine for handling incoming audio data				 * 
 *-----------------------------------------------------------------------*/

static void audio_in_handler(GalIO_BrokerStruct *broker_struct,
                             void *data,
                             Gal_ObjectType data_type,
                             int n_samples){

  //  int uttproc_result() {return -1;}

  DataHandler *c = (DataHandler *) GalIO_GetBrokerCallerData(broker_struct);
  long numsamples;
  // long pathscore;
  char *hypo;

  server_enabled = 1;
 
  switch(data_type) {

  /*--- case 1: we are receiving audio, update the recognition hypothesis --*/

  case GAL_INT_64:
    if (server_enabled){
       printf("Receiving %d MFC SAMPLES from audio server\n", n_samples);
       fflush(stdout);
       numsamples = n_samples;
       total_samples += numsamples;

       if (server_enabled)
         uttproc_mfcdata((double *)data, numsamples, 1); /* ISIP call */
    
       time2 = clock();
       audio_duration_sec  = (float)total_samples/sample_rate;
       cpu_seconds_elapsed = (time2-time1)/(float)CLOCKS_PER_SEC;

       c->data_buf = (char *) realloc(c->data_buf, (2 * n_samples) + c->size);
       bcopy(data, c->data_buf + c->size, 2 * n_samples);
       c->size += (2 * n_samples);
       free(data);
    }
    break;

  case GAL_INT_16:
    if (server_enabled){
       printf("Receiving %d RAW SAMPLES from audio server\n", n_samples);
       fflush(stdout);
       numsamples = n_samples;
       total_samples += numsamples;

       if (server_enabled)
         uttproc_rawdata((short *)data, numsamples, 1); /* ISIP call */
    
       time2 = clock();
       audio_duration_sec  = (float)total_samples/sample_rate;
       cpu_seconds_elapsed = (time2-time1)/(float)CLOCKS_PER_SEC;

       c->data_buf = (char *) realloc(c->data_buf, (2 * n_samples) + c->size);
       bcopy(data, c->data_buf + c->size, 2 * n_samples);
       c->size += (2 * n_samples);
       free(data);
    }
    break;

  /*------ case 2: we are receiving a signaling message --------------------*/

  case GAL_STRING:
    //GalUtil_PInfo1("audio_data_handler: got message %s\n", (char *) data);
    if (!strcmp((char *)data, AUDIO_SERVER_NEW_UTT)) {
      
      if (server_active) uttproc_abort();
      server_active = 1;
      total_samples = 0;
      time1 = clock();
      fprintf(stderr,"====== CALLING BEGIN UTT FUNCTION =====\n"); 
      fflush(stderr);

      /* ISIP Decoder calling */
      if ((server_enabled) && (uttproc_begin () < 0)){
	//         E_FATAL("uttproc_begin() failed\n");
	GalUtil_PInfo1("uttproc_begin() failed\n");
	fflush(stdout);
	exit(1);
      }

      fr = 0;
      hyp = NULL;
      free(c->data_buf);
      c->data_buf = (char *) NULL;
      c->size = 0;
    } 

    else if (!strcmp((char *)data, AUDIO_SERVER_SAMPLES_DONE)) {
      GalIO_BrokerDataDone(broker_struct);

      /*---- before we finish up the recognition, setup a timer ---*/

      audio_duration_sec  = (float)total_samples/sample_rate;
      if (server_enabled){
         uttproc_end();
	 //         if (uttproc_result (&fr, &hyp, 1, &pathscore) < 0)
	   fflush(stdout);
	   hypo = malloc(1000);
         if (!uttproc_result(hypo)) {
	   //  	    E_FATAL("uttproc_result() failed\n");
	   GalUtil_PInfo1("uttproc_result() failed\n");
	   server_active = 0;
	   free(hypo);
	 }
         else{
	   //            printf("*** YOU SAID: [%s] (%d frames) (score = %d)\n", hyp, (int)fr, (int)pathscore);
	   printf("***** YOU SAID: ***********************\n");
	   printf("%s", hypo);
	   fflush(stdout);
	   free(hypo);
	   server_active = 0;
	   return_recognized_speech_to_hub(c, hyp); 
         }
      }
    } 

    else if (!strcmp((char *)data, AUDIO_SERVER_SAMPLES_ABORT)) {
      GalIO_BrokerDataDone(broker_struct);
      if (server_active) uttproc_abort();
      server_active = 0;
    } 

    else {
      GalUtil_Warn("audio_data_handler: Msg is unknown...\n");
    }
    break;

  default:
    GalUtil_Warn("audio_data_handler: unexpected data type %s\n",
		 Gal_ObjectTypeString(data_type));
    break;
  }
}

/*-----------------------------------------------------------------------*
 * Frees the audio buffer						 * 
 *-----------------------------------------------------------------------*/

void __FreeDataHandler(void *data)
{
  DataHandler *d = (DataHandler *) data;
  free(d->data_buf);
  free(d);
}

/*-----------------------------------------------------------------------*
 * Reports audio abort events to the hub				 * 
 *-----------------------------------------------------------------------*/

void __report_abort(GalIO_BrokerStruct *b, void *data)
{
  DataHandler *d = (DataHandler *) GalIO_GetBrokerData(b);
  Gal_Frame f;

  f = Gal_MakeFrame("notify", GAL_CLAUSE);
  Gal_SetProp(f, ":notification", Gal_StringObject("Audio aborted."));
  GalIO_CommWriteFrame(d->gcomm, f, 0);
  Gal_FreeFrame(f);
}

void __report_done(GalIO_BrokerStruct *b, void *data)
{
  DataHandler *d = (DataHandler *) GalIO_GetBrokerData(b);
  Gal_Frame f;

  f = Gal_MakeFrame("notify", GAL_CLAUSE);
  Gal_SetProp(f, ":notification", Gal_StringObject("Audio received."));
  GalIO_CommWriteFrame(d->gcomm, f, 0);
  Gal_FreeFrame(f);
}

/*-----------------------------------------------------------------------*
 * Main routine for recognizing incoming speech				 * 
 *-----------------------------------------------------------------------*/

Gal_Frame recognize_speech(Gal_Frame f, void *server_data){

  char *host = Gal_GetString(f, ":binary_host");
  int port = Gal_GetInt(f, ":binary_port");
  GalIO_BrokerStruct *b;

  //  if (host && port && (Gal_GetInt(f, ":sample_rate") > 0)) {
  if (host && port) {    
    DataHandler *c = (DataHandler *) malloc(sizeof(DataHandler));

    c->server_data = GalSS_EnvComm(server_data);
    c->data_buf = (char *) NULL;
    c->size = 0;
    c->gcomm = GalSS_EnvComm((GalSS_Environment *)server_data);
    b = GalIO_CommBrokerDataInInit(c->gcomm, host, port, f, audio_in_handler, 0, c, __FreeDataHandler);
    if (b){
      
       GalIO_SetBrokerActive(b);
       GalIO_AddBrokerCallback(b, GAL_BROKER_ABORT_EVENT,
			      __report_abort, (void *) NULL);
       GalIO_AddBrokerCallback(b, GAL_BROKER_DATA_DONE_EVENT,
                              __report_done, (void *) NULL);
    }
    else free(c);
  }
  return f;
}

/*-----------------------------------------------------------------------*
 * Server reinitialization function					 * 
 *-----------------------------------------------------------------------*/

Gal_Frame reinitialize(Gal_Frame f, void *server_data){

  GalUtil_PInfo1("**** Received the reinitialize message *****\n");
   if ((server_enabled) && (server_active)) uttproc_abort();
   server_active  = 0;
   server_enabled = 1;
   return(f);
}

/*-----------------------------------------------------------------------*
 * Server initialization function					 * 
 *-----------------------------------------------------------------------*/

void *_GalSS_init_server(Gal_Server *s, int argc, char **argv){

  char* params = NULL;

  sys_id = NULL;
  if (GalUtil_OACheckUsage(argc, argv, oas, NULL) == 0)
     exit(1);

  /* get the parameter file */

  GalUtil_OAExtract(argc, argv, oas, "-p", GAL_OA_STRING, &params);
  if (params == NULL) {
    fprintf(stderr,"Can't find parameter files. Exit!\n"); 
    fflush(stderr);
    exit(1);
  }
  else {
   load_models(params);
   server_active  = 0;
   server_enabled = 1;
  }

  /* get decoder key id */

  GalUtil_OAExtract(argc, argv, oas, "-sysid", GAL_OA_STRING, &sys_id);
  if (sys_id == NULL) strcpy(sys_id_value,"si");
  else strcpy(sys_id_value, sys_id);

  //  fbs_init (argc, argv);
  return (void *) NULL;
}
