// file: $isip/util/demo/isip_comm_server_dialog_nlp/isip_comm_dialog_nlp.cc
// version: $Id: isip_comm_dialog_nlp.cc 10171 2005-07-29 19:12:01Z stanley $
//


/*************************************************************************
 *                                                                       * 
 *                Center for Speech and Language Research		 *
 *                   University of Colorado, Boulder                     *
 *                       Copyright (c) 1999,2000                         *
 *                        All Rights Reserved.                           *
 *                                                                       *
 *  The University of Colorado ("CU") hereby grants to you an            *
 *  irrevocable, nonexclusive, nontransferable, perpetual, royalty-free  *
 *  and worldwide license to use this source code solely for educational,*
 *  research, personal use, or evaluation.  Limitations of Use are       *
 *  described below.  A commercial license can be obtained only through  *
 *  expressed written permission of The Center for Speech and Language   *
 *  Research, University of Colorado at Boulder.			 *
 *									 *
 *  	Non-Commercial License Limitations and Use Requirements		 *
 *									 *
 *  Limitations on Use: The License is limited to noncommercial use.     *
 *  Noncommercial use relates only to educational, research, personal    *
 *  use, or evaluation purposes.  Any other use is commercial use.  You  *
 *  may not use the Software in connection with any business activities. *
 *  You may distribute and/or allow others to use a) the Software or b)  *
 *  the applications you create with the Software only if each new user  *
 *  is bound by the provisions of this Agreement.			 *
 *									 *
 *  Conditions of Use: This work is furnished to you under the following *
 *  conditions:								 *
 *									 *
 *   1. The code must retain the above copyright notice, this list of    *
 *      conditions and the following disclaimer.                         *
 *   2. Any modifications must be clearly marked as such.                *
 *   3. Original authors' names are not deleted.                         *
 *   4. The authors' names are not used to endorse or promote products   *
 *      derived from this software without specific prior written        *
 *      permission.                                                      *
 *   5. You agree to acknowledge CU-Boulder's Center for Speech and      *
 *      Language Reseach with appropriate citations in any publication or*
 *      presentation containing research results obtained in whole or in *
 *      part through the use of the Software.				 *
 *									 *
 *  Term of License: The License is effective upon receipt by you of the *
 *  Software and shall continue until terminated.  The License will      *
 *  terminate immediately without notice by CU if you fail to comply     *
 *  with the terms and conditions of this Agreement.  Upon termination   *
 *  of this License, you shall immediately discontinue all use of the    *
 *  Software provided hereunder, and return to CU or destroy the         *
 *  original and all copies of all such Software.  All of your           *
 *  obligations under this Agreement shall survive the termination of    *
 *  the License.							 *
 *									 *
 *  DISCLAIMER:                                                          *
 *                                                                       *
 *  THE UNIVERSITY OF COLORADO AND THE CONTRIBUTORS TO THIS WORK         *
 *  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      *
 *  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   *
 *  SHALL THE UNIVERSITY OF COLORADO NOR THE CONTRIBUTORS BE LIABLE      *
 *  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    *
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   *
 *  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          *
 *  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       *
 *  THIS SOFTWARE.                                                       *
 *                                                                       *
 *************************************************************************/
/*-----------------------------------------------------------------------*
 *                                                                       *
 *		     CU Communicator Phoenix Server		         * 
 *                                                                       *
 *                                                                       *
 *   Wayne Ward (whw@cslu.colorado.edu)                              	 *
 *   Center for Spoken Language Understanding				 *
 *   University of Colorado at Boulder					 *
 *   (c) 1999 All Rights Reserved					 *
 *                                                                       *
 *-----------------------------------------------------------------------*/

#include "isip_comm_dialog_nlp.h"

// Prototype functions for all the Galaxy functions
//
Gal_Frame send_to_parse(Gal_Frame f, void *server_data);
Gal_Frame reinitialize(Gal_Frame f, void *server_data);
Gal_Frame get_process_id(Gal_Frame frame, void *server_data);

// FUNCTIONS TO HELP WITH DEBUGGING

// method: write_debug_to_client
//
// arguments:
//  void *server_data_a: (input) a pointer to the galaxy hub
//  char *msg_a: (input) a pointer to a string to write to the client
//
// return: -1 on error (invalid input) or 0 if successful
//
// uses no global variables
// Does initial error checking of variables != NULL, then composes a frame
// with a few values and writes this frame to the hub. This function has
// a counter that will attempt to help to help the user determine which order
// the frames were sent
//
int write_debug_to_client(void *server_data_a, char *msg_a) {
  static unsigned int message_sent_count = 0;
  
  if (!msg_a || !server_data_a)
    return -1;
  
  Gal_Frame f_new = Gal_MakeFrame("main", GAL_CLAUSE);
  Gal_SetProp(f_new, ":debug_msg", Gal_IntObject(1));
  Gal_SetProp(f_new, ":debug_msg_count", Gal_IntObject(message_sent_count++));
  Gal_SetProp(f_new, ":source_server",
	      Gal_StringObject(SERVER_NAME_STRING));
  Gal_SetProp(f_new, ":debug_string", Gal_StringObject(msg_a));
  
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data_a, f_new, 0);

  Gal_FreeFrame(f_new);
  
  return 0;
}

// method: find_name_of_state
//
// arguments:
//  int state: (input) the #define'd new server value of state to find
//
// return: pointer to a created frame
//
// uses no global variables
// returns pointer to a string -- guaranteed to *not* be a null pointer
// when new states are added, this function must be edited so that it
// contains those new states or else it will return "UNKNOWN STATE"
//
// WARNING: the returned string is in data section of .exe so don't attempt
// to write to it or bad things will happen
//
char* find_name_of_state(int state) {
  switch (state) {
  case 0: return "GET_PROCESS_ID";
    
  case 5: return "WAIT_FOR_TEXT";
  case 6: return "WAIT_FOR_TEXT_ACK";
  case 10: return "PROCESSING_TEXT";
  }
  return "UNKNOWN STATE";
}

// method: change_state
//
// arguments:
//  int new_state_a: (input) the #define'd new server value
//  char *name: (input) the information of the server to send the data to
//
// return: none.
//
// uses global variable "server_state_d"
// no error checking is done to determine whether this is a value state or not
// the function will write to screen a string telling which state is coming
// out of and which is going in. Then creates a frame with certain values
// and writes to hub. Frame is freed afterwards, and the server is set into
// the new state value.
//
/*void change_state(int new_state_a, void *server_data_a) {
  char *old_state_str;
  char *new_state_str;
  
  old_state_str = find_name_of_state(server_state_d);
  new_state_str = find_name_of_state(new_state_a);
  
  // print out what change is being made
  printf("STATE-CHANGE: Changing from state [%s] to [%s]\n",
	 old_state_str, new_state_str);
  
  Gal_Frame f_new = Gal_MakeFrame("main", GAL_CLAUSE);
  Gal_SetProp(f_new, ":debug_msg", Gal_IntObject(1));
  Gal_SetProp(f_new, ":source_server",
	      Gal_StringObject(SERVER_NAME_STRING));
  Gal_SetProp(f_new, ":state_change_old", Gal_StringObject(old_state_str));
  Gal_SetProp(f_new, ":state_change_new", Gal_StringObject(new_state_str));
  
  //write to debug window
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data_a, f_new, 0);  
  
  server_state_d = new_state_a;
  }*/
void change_state(int new_state_a, void *server_data_a) {

  static void *saved_server_data;
  static int state_number = 0;

  // Save the most recent copy of this data for any functions that
  // may not have access to server_data.
  //
  if (server_data_a != NULL)
    saved_server_data = server_data_a;
     
  char *old_state_str;
  char *new_state_str;
  
  old_state_str = find_name_of_state(server_state_d);
  new_state_str = find_name_of_state(new_state_a);
  
  // print out what change is being made
  printf("STATE-CHANGE: Changing from state [%s] to [%s]\n",
	 old_state_str, new_state_str);
  
  Gal_Frame f_new = Gal_MakeFrame("main", GAL_CLAUSE);
  Gal_SetProp(f_new, ":debug_msg", Gal_IntObject(1));
  Gal_SetProp(f_new, ":state_change_number", Gal_IntObject(state_number++));
  Gal_SetProp(f_new, ":source_server",
	      Gal_StringObject(SERVER_NAME_STRING));
  Gal_SetProp(f_new, ":state_change_old", Gal_StringObject(old_state_str));
  Gal_SetProp(f_new, ":state_change_new", Gal_StringObject(new_state_str));
  
  // write to debug window
  // writing to saved_server_data because either
  // 1) we want to write to the same as the current (in which saved was set)
  // 2) we want to write to most recently saved
  //
  //GalSS_EnvWriteFrame((GalSS_Environment *) server_data_a, f_new, 0);
  GalSS_EnvWriteFrame((GalSS_Environment *) saved_server_data, f_new, 0);  

  Gal_FreeFrame(f_new);
  
  server_state_d = new_state_a;
}

Gal_Frame receive_data(Gal_Frame frame_a, void *server_data_a) {
  
  //debug 
  printf("Entering receive_data in state[%s]\n",
	 find_name_of_state(server_state_d));
  //end debug
  
  // First check if this is a reset flag frame
  //
  if (Gal_GetString(frame_a, ":reset")) {
    //this means to reset the db state
    //
    start_session();
    change_state(WAIT_FOR_TEXT, server_data_a);
    
    return (Gal_Frame) NULL;
  }//end reset
  
  // need a function call here to write a message to the
  // debug window stating entered with state = WHAT? and
  // containing this frame
  
  if (server_state_d == GET_PROCESS_ID) {
    return get_process_id(frame_a, server_data_a);
  }
  else if (server_state_d == WAIT_FOR_TEXT) {
    return send_to_parse(frame_a, server_data_a);
  }
  else if (server_state_d == PROCESSING_TEXT) {
    // if I am waiting  and I get this, return a frame saying
    // that I can only process one sql query at a time
    //
    char strTemp[512];
    sprintf(strTemp,"ERROR! NLP server received a galaxy frame when "
	    "it is currently in state [%i][%s]. This should NOT happen.\n",
	    server_state_d,
	    find_name_of_state(server_state_d));
    write_debug_to_client(server_data_a, strTemp);
    return (Gal_Frame)NULL;
  }
  // Else error if somehow server_state_d is something else!
  else {
    printf("ERROR: Entered receive_data in an UNKNOWN STATE!!! "
	   "Changing to WAIT_FOR_TEXT\n");
    write_debug_to_client(server_data_a, "Server state was unknown state!"
			  " Resetting state to WAIT_FOR_TEXT");
    change_state(WAIT_FOR_TEXT, server_data_a);
  }
  
  return (Gal_Frame) NULL;
}
// END FUNCTIONS TO HELP WITH DEBUGGING


/*-----------------------------------------------------------------------*
 * Main routine which takes a string as input and returns a parse string * 
 *-----------------------------------------------------------------------*/
Gal_Frame send_to_parse(Gal_Frame f, void *server_data) {

  //debug
  printf("entering send_to_parse with state [%i][%s]\n",
	 server_state_d, find_name_of_state(server_state_d));
  //end debug

  // change to processing text
  change_state(PROCESSING_TEXT, server_data);
  
  int i;
  int MaxParses;
  char *in_string;
  char *out_string;
  char *sysid;
  
  Gal_Frame f_new = Gal_MakeFrame("main", GAL_CLAUSE);
  
  MaxParses = 10;
  
  /*---- get the input string to parse from the key's value ---*/
  
  in_string  = Gal_GetString(f, ":input_string");
  sysid      = Gal_GetString(f, ":sysid");
  
  if (in_string == NULL)
    in_string = Gal_GetString(f, ":parse_input");
  
  /*---- acknowledge receipt of frame by returning it to hub --*/
  /*
    GalSS_EnvWriteFrame((GalSS_Environment *) server_data, f, 0);
  */
  /* strip out punctuation, comments, etc, to uppercase */
  strip_line(in_string);
  
  if (!strncmp(in_string, "QUIT", 4))
    exit(1);
  
  /*---- Call Phoenix Parse function ----------------------------*/
  parse(in_string, gram);
  
  /* print parses to buffer */
  if (num_parses > MaxParses)
    num_parses = MaxParses;
  
  if (num_parses < 1 ) {
    strcpy(outbuf, "No Parse");
  }
  else {
    out_string = outbuf;
    // This for loop prints out everything like
    // PARSE_1:
    // <PARSE HERE>
    // END_PARSE
    // PARSE_2:
    // <ANOTHER PARSE>
    // END_PARSE
    // \n
    for (i = 0; i < num_parses; i++ ) {
      
      sprintf(out_string, "PARSE_%d:\n", i);
      out_string += strlen(out_string);
      print_parse(i, out_string, extract, gram);
      out_string += strlen(out_string);
      sprintf(out_string, "END_PARSE\n");
      out_string += strlen(out_string);
    }
    
    sprintf(out_string, "\n");
    out_string = outbuf;
  }
  
  /* clear parser temps */
  reset(num_nets);
  
  /* whw */
  printf("-%s-\n", outbuf);
  fflush(stdout);
  
  /*---- now, return a new frame containing the parse ---------*/ 
  
  Gal_SetProp(f_new, ":parse_input", Gal_StringObject(in_string));
  Gal_SetProp(f_new, ":parse_output", Gal_StringObject(outbuf));

  if (sysid == NULL)
    sysid = "";
  Gal_SetProp(f_new, ":sysid", Gal_StringObject(sysid));
  
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data, f_new, 0);

  // clean up since already written to galaxy
  //
  Gal_FreeFrame(f_new);

  change_state(WAIT_FOR_TEXT, server_data);
  
  return (Gal_Frame) NULL;
}

/*-----------------------------------------------------------------------*
 * server reinitialization routine.  This is called once the server is   *
 * started.                                                              *
 *-----------------------------------------------------------------------*/

Gal_Frame reinitialize(Gal_Frame f, void *server_data) {
  
  start_session();
  
  server_state_d = INITIAL_STATE;
  
  return(f);
}


// method: reset
//
// arguments:
//
//  Gal_Frame  f: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: Galaxy Frame
//

// this function resets the server
//
Gal_Frame reset(Gal_Frame frame_a, void *server_data_a) {

  //this means to reset the db state
    //
    start_session();
    change_state(WAIT_FOR_TEXT, server_data_a);
    
    return (Gal_Frame) NULL;
}

void *_GalSS_init_server(Gal_Server *s, int argc, char **argv) {
  
  char *config_file = NULL;
  
  // 'oas' only contains '-config' but this doesn't seem to error here
  // if something besides '-config' is there. It will catch if
  // '-config' does not have following information (such as "-config a.cfg")
  //
  if (GalUtil_OACheckUsage(argc, argv, oas, NULL) == 0)
    exit(1);
  
  GalUtil_OAExtract(argc, argv, oas, "-config", GAL_OA_STRING, &config_file);
  if (config_file == NULL){
    printf("No config file specified... exiting\n");
    exit(1);
  }
  
  /* set config file parms */
  config(argc, argv, (Config_t*)parse_conf, config_file);
  
  /* initialize parser, malloc space, etc */
  init_parse(dir, dict_file, grammar_file, frames_file, priority_file);
  
  return (void *) NULL;
}

/*-----------------------------------------------------------------------*
 * Required intialization function for Hub compliance.			 * 
 *-----------------------------------------------------------------------*/

Gal_Frame get_process_id(Gal_Frame frame, void *server_data) {
  
  Gal_Frame new_f;
  
  char *program_name = Gal_GetString(frame, ":program");
  char server_name[60];
  
  if (!program_name)
    program_name = "main";
  
  strcpy(server_name, "isip_nlp");
  
  int pid = getpid();
  
  new_f = Gal_MakeFrame(program_name, GAL_CLAUSE);
  Gal_SetProp(new_f, ":server_name", Gal_StringObject(server_name));
  Gal_SetProp(new_f, ":pid", Gal_IntObject(pid));
  
  Gal_SetProp(new_f, ":program", Gal_StringObject(program_name));
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data, new_f, 0);
  Gal_FreeFrame(new_f);

  change_state(WAIT_FOR_TEXT, server_data);  
  
  return (Gal_Frame) NULL;
}


void strip_line(char *line) {
  
  char *from = line;
  char *to = line;
  
  for(; ;from++) {
    
    // Stop at end of line
    if(!*from)
      break;
    
#ifdef SNOR
    /* for SNOR input */
    if( *from == '(' ) {
      strcpy(utt_name, from);
      break;
    }
#endif
    
    switch(*from) {
      
      /* filter these out */
#ifndef SNOR
    case '(' :
    case ')' :
    case '[' :
    case ']' :
#endif
    case ':' :
    case ';' :
    case '?' :
    case '!' :
    case '\n' :
      break;
      
      /* replace with space */
    case ',' :
    case '\\' :
      *to++ = ' ';
      break;
      
      /* strip noise annotation */
      // This removes all the data from a '/' through end of string
      // or to the next '/' including '/'
      // Example: This is /a/ string with /two/ sets included
      //    goes to
      //          This is string with sets included
      //
    case '/' :
      for( ++from; *from != '/' && *from; from++);
      if( *from == '/' )
	from++;
      break;
      
      // Same as above, it removes between #'s
      //
    case '#' :
      for( ++from; *from != '#' && *from; from++);
      if(*from == '#')
	from++;
      break; 
      
      // If "- " or "-\n" exists then go backwards and remove
      // whitespace.
    case '-' :
      /* if partial word, delete word */
      if( isspace( *(from+1) ) ) {
	printf("Found case of hyphen!\n");
	printf("Before 'line': [%s]\n",line);
	printf("Before 'to': [%s]\n",to);	
	while( (to != line) && !isspace( *(--to) ) ) ;
	/* replace with space */
	*to++ = ' ';
	printf("After the value of to: [%s]\n",to);
      }
      else {
	/* copy char */
	*to++ = *from;
      }
      break;
      
#ifdef SNOR
      // If [ is found, remove everything from [ to ]
    case '[' :
      // skip past comment
      while (*from != ']') {
	if( !*from ) break;
	from++;
      }
      break;
#endif
      
    default:
      // copy char
      *to++ = toupper(*from);
    }
    if( !from ) break;
    
  }
  
  // Set final null character
  *to = 0;
}
