// file: $ISIP_BASE/util/demo/isip_comm_server_dialog_manager/isip_comm_dialog_manager.cc
// version: $Id: isip_comm_dialog_navigation_mgr.cc 10171 2005-07-29 19:12:01Z stanley $
/*-----------------------------------------------------------------------*
 *                                                                       *
 *		CSLR Communicator Dialogue Manager 		         * 
 *		Hub Dialog Server Main		   		         * 
 *                                                                       *
 *                                                                       *
 *   Wayne Ward (whw@cslr.colorado.edu)                              	 *
 *   Center for Spoken Language Research				 *
 *   University of Colorado at Boulder					 *
 *   (c) 1999 All Rights Reserved					 *
 *                                                                       *
 *-----------------------------------------------------------------------*/

#include "isip_comm_dialog_navigation_mgr.h"
#include "dm_conf.h"

extern "C" {
#include <galaxy/galaxy_all.h>
#define SERVER_FUNCTIONS_INCLUDE "isip_comm_dialog_navigation_mgr_server.h"
#define USE_SERVER_DATA
#include <galaxy/server_functions.h>
}

// local functions
//
// Must define here because
// 1) not used in external files
// 2) need to have 'Gal_Frame' defined which is done above. Due to the
//    galaxy headers having variables defined which then
//    make it very difficult to link the entire project if more than one
//    file includes them.
//
Gal_Frame package_db_data(int row_in_focus);
void unpack_db_data(Gal_Frame src_frame, struct db_query *db_result);

Gal_Frame receive_data(Gal_Frame frame_a, void *server_data_a);
Gal_Frame receive_parse(Gal_Frame f, void *server_data);
Gal_Frame receive_db_result(Gal_Frame f, void *server_data);
Gal_Frame get_process_id(Gal_Frame f, void *server_data);

static int	enable_input_ok= 0;
//static int	user_logged_on = 0;
//static int	pricing = 0;
static Gal_Frame save_gf;

static struct db_query db_result;

// 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);
  
  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_PARSE";
  case 6: return "WAIT_FOR_PARSE_ACK";
  case 10: return "WAIT_FOR_DB";
  case 11: return "WAIT_FOR_DB_ACK";

  case 12: return "PROCESSING_PARSE";
  }
  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 dm state
    //reset_dm(), etc;
    //
    enable_input_ok = 0;
    reset_dm();
    reset_task();
    change_state(WAIT_FOR_PARSE, 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_PARSE) {
    // the state stays as "WAIT_FOR_PARSE" unless
    // "build_dbq" sets it as WAIT_FOR_DB
    //
    return receive_parse(frame_a, server_data_a);
  }
  else if (server_state_d == WAIT_FOR_DB) {
    // the receive_db_result function will change the state back to
    // WAIT_FOR_PARSE when it get data
    //
    return receive_db_result(frame_a, server_data_a);
  }
  else {
    printf("ERROR: Entered receive_data in an UNKNOWN STATE!!! "
	   "Changing to WAIT_FOR_PARSE\n");
    write_debug_to_client(server_data_a, "Server state was unknown state!"
			  " Resetting state to WAIT_FOR_PARSE");
    change_state(WAIT_FOR_PARSE, server_data_a);
  }
  
  return (Gal_Frame) NULL;
}

/*-----------------------------------------------------------------------*
 * Routine which receives a parse from Phoenix from the Hub		 * 
 *-----------------------------------------------------------------------*/

// uses global "global_debug_msg" -- a buffer for holding some text
Gal_Frame receive_parse(Gal_Frame f, void *server_data) {
  
  //debug
  printf("entering 'receive_parse'\n"); fflush(stdout);
  sprintf(global_debug_msg, "Entering receive_parse with state equal to %s",
	  find_name_of_state(server_state_d));
  write_debug_to_client(server_data, global_debug_msg);
  //end debug

  change_state(PROCESSING_PARSE, server_data);
  
  struct parse_item parse_result;   // storing parse results
  
  char *parse_in = NULL;   // input which was sent to Phoenix
  char *parse_out = NULL;  // output returned from Phoenix   
  char pbuf[10000];        // temporary buffer to hold parses
  char *s = NULL;
  int ret;
  
  // set variables to be clean
  //
  memset(&parse_result, 0, sizeof(struct parse_item));
  memset(&pbuf, 0, sizeof(pbuf));
  
  // disable timeout
  wait_for_parse = 0;
  Gal_RemoveTimedTask((void*)no_response_from_user, NULL);
  
  // get the input and output strings from frame sent by Phoenix
  parse_in  = Gal_GetString(f, ":parse_input");
  parse_out = Gal_GetString(f, ":parse_output");
  
  // error checking
  //
  if (parse_in == NULL) {
    sprintf(global_debug_msg, "ERROR! navigation manager: receive_parse() "
	    ":parse_in is null value!");
    write_debug_to_client(server_data, global_debug_msg);
    printf(global_debug_msg);
    
    change_state(WAIT_FOR_PARSE, server_data);
    return (Gal_Frame)NULL;
  }
  if (parse_out == NULL) {
    sprintf(global_debug_msg, "\nERROR! navigation manager: receive_parse() "
	   ":parse_out is null value!");
    write_debug_to_client(server_data, global_debug_msg);
    printf(global_debug_msg);

    change_state(WAIT_FOR_PARSE, server_data);    
    return (Gal_Frame)NULL;
  }
  
  printf("strlen of: parse_in[%i] parse_out[%i]\n", 
	 (int)strlen(parse_in),
	 (int)strlen(parse_out));
  fflush(stdout);
  
  // buffer overflow checking
  //
  if (strlen(parse_in) >= sizeof(parse_result.parse_in)) {
    sprintf(global_debug_msg, "ERROR! manager: receive_parse(): parse_in is "
	    "larger than parse_result[%i bytes]\n",
	    (int)strlen(parse_result.parse_in));
    write_debug_to_client(server_data, global_debug_msg);

    printf(global_debug_msg);

    change_state(WAIT_FOR_PARSE, server_data);    
    return (Gal_Frame)NULL;
  }
  
  if (strlen(parse_out) >= sizeof(pbuf)) {
    sprintf(global_debug_msg, "ERROR! manager: receive_parse(): parse_out "
	    "is larger than pbuf[%i bytes]\n", (int)strlen(pbuf));
    write_debug_to_client(server_data, global_debug_msg);

    printf(global_debug_msg);

    change_state(WAIT_FOR_PARSE, server_data);    
    return (Gal_Frame)NULL;
  }
  
  // save the data got from the frame
  //
  strcpy(parse_result.parse_in, parse_in);
  strcpy(pbuf, parse_out);
  
  printf("Entering 'get_best_parse'\n");
  fflush(stdout);
  
  // get best parse from buffer
  s = get_best_parse(pbuf);
  
  printf("Returned from 'get_best_parse'\n");
  fflush(stdout);
  
  // clear out prompt_text buffer
  prompt_text[0] = (char)0;
  
  // set rest of values to null
  prompt = (char *)0;
  action = (char *)0;
  task = (char *)0;
  
  // if no parse
  if( !s || !strcmp("No Parse", s) ) {
    prompt = "I don't understand";
    nl_request("prompt_user", prompt, NULL, 1, server_data, (char *)0);

    change_state(WAIT_FOR_PARSE, server_data);    
    return (Gal_Frame)NULL;
    //return(f);
  }
  //save the parse output in the parse result
  strcpy(parse_result.parse_out, s);
  
  // extract information and set global variables
  if (extract_values(parse_result.parse_out) < 0) {
    prompt = "I don't understand";
    nl_request("prompt_user", prompt, NULL, 1, server_data, (char *)0);

    change_state(WAIT_FOR_PARSE, server_data);    
    return (Gal_Frame)NULL;
    //return(f);
  }
  
  if( !task && focus )
    set_task(focus->name);
  
  // merge extracted frames with current context frames
  printf("***** entering merge context\n");
  merge_context(context, extracts);
  printf("***** exiting merge context\n");
  
  if (focus)
    action = focus->name;
  
  // 0 means a db query was sent
  // 1 means no db query was sent
  printf("***** entering action switch\n");
  ret = action_switch(server_data);
  printf("***** exiting action switch\n");
  if (ret == 0) {
    change_state(WAIT_FOR_DB, server_data);
  }
  else {
    change_state(WAIT_FOR_PARSE, server_data);
  }

  // When getting here, if a frame needs to be returned, the action_switch()
  // command returned it. Therefore just return null
  //
  return (Gal_Frame)NULL;
}


/*-----------------------------------------------------------------------*
 * Routine which sends an NL generation request to the Hub		 * 
 *-----------------------------------------------------------------------*/

void nl_request(char *spch_act, char *prompt, char *task,
		int enable_input_after_prompt, void *server_data,
		char *voice) {

  //debug
  printf("entered nl_request with values:\n"
	 "spch_act[%s] prompt[%s] task[%s] enable_in[%i], voice[%s]\n",
	 spch_act, prompt, task, enable_input_after_prompt, voice);
  fflush(stdout);
  //end debug
  
  // Error checking
  if (!prompt) {
    sprintf(global_debug_msg, "ERROR! manager: nl_request(): entered with "
	    "prompt == NULL\n");
    write_debug_to_client(server_data, global_debug_msg);
    return;
  }
  
  char prompt_string[5000];
  char prompt_slot[100];
  char speech_act[LINE_LEN];
  Gal_Frame f_new;
  
  // possible problem here. never found to be an issue but just documenting
  // in case someone finds there is memory leaking.
  // db_frame is never allocated in this file
  // but is a return value from another thing. How do i make sure I
  // don't have a memory leak?
  //
  Gal_Frame db_frame;

  f_new = Gal_MakeFrame("main", GAL_CLAUSE);
  Gal_SetProp(f_new, ":nl_request", Gal_IntObject(1));
  
  if (!spch_act)
    return;
  
  strcpy(speech_act, spch_act);
  
  if (strcmp(speech_act,"prompt_user") == 0) {
    // get the prompt text from function input
    //
    strcpy(prompt_string, prompt);
    str2upper(prompt_string);
    Gal_SetProp(f_new, ":prompt", Gal_StringObject(prompt_string));
    
    if( !prompted.key )  strcpy(prompt_slot, "none");
    else if( !prompted.frame )  strcpy(prompt_slot, prompted.key);
    else sprintf(prompt_slot, "%s.%s", prompted.frame->name, prompted.key);
    
    Gal_SetProp(f_new, ":slot", Gal_StringObject(prompt_slot));
  }
  else if (strcmp(speech_act,"error_msg")==0) {
    strcpy(speech_act, "prompt_user");
    strcpy(prompt_string, prompt);
    str2upper(prompt_string);
    Gal_SetProp(f_new, ":prompt", Gal_StringObject(prompt_string));
    Gal_SetProp(f_new, ":error_msg", Gal_IntObject(1));
  }
  else if (!strcmp(speech_act, "inquiry")) {
    Gal_SetProp(f_new, ":task", Gal_StringObject(task));
    db_frame = package_db_data(row_in_focus);
    Gal_SetProp(f_new, ":db_info", Gal_FrameObject(db_frame));
    strcpy(prompt_string, prompt);
    Gal_SetProp(f_new, ":prompt", Gal_StringObject(prompt_string));
  }
  else if (!strcmp(speech_act, "repeat")) {
    // nothing needed here, copied in speech_act below
    printf("nl_request: repeat\n"); 
  }
  
  Gal_SetProp(f_new, ":speech_act", Gal_StringObject(speech_act));
  
  enable_input_ok = enable_input_after_prompt;

  // Write the frame back to the hub
  //
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data, f_new, 0);
  
  // Need to free frame allocated at the beginning
  //
  Gal_FreeFrame(f_new);
  
  printf("exited nl_request by running out of it\n");
  fflush(stdout);
}

/*-----------------------------------------------------------------------*
 * No response dispatch function					 * 
 *-----------------------------------------------------------------------*/

void no_response_from_user(void *server_data) {
  
  Gal_Frame f_new, f_x;
  
  printf("DM: NO RESPONSE FROM USER\n");
  fflush(stdout);
  
  Gal_RemoveTimedTask((void*)no_response_from_user, NULL);
  
  // send timeout event 
  f_x = Gal_MakeFrame("main", GAL_CLAUSE);
  Gal_SetProp(f_x, ":timeout", Gal_StringObject("no_response_from_user"));
  GalIO_CommWriteFrame((GalIO_CommStruct *)server_data, f_x, 0);
  Gal_FreeFrame(f_x);
    
  f_new = Gal_MakeFrame("main", GAL_CLAUSE);
  Gal_SetProp(f_new, ":nl_request", Gal_IntObject(1));
  Gal_SetProp(f_new, ":speech_act", Gal_StringObject("repeat"));
  enable_input_ok = 1;
  GalIO_CommWriteFrame((GalIO_CommStruct *)server_data, f_new, 0);
  Gal_FreeFrame(f_new);
}

/*-----------------------------------------------------------------------*
 * Routine which sends a database query request to the Hub		 * 
 *-----------------------------------------------------------------------*/
// returns 0 if it did send the query to the database
// returns 1 if it did not send the query
//
int query_database(char *sql_query, aFrame *frame, void *server_data,
		    int access_web) {

  //debug
  printf("entering query_database()\n");
  fflush(stdout);
  //end debug

  // simple error checking
  if (!sql_query || !frame || !server_data) {
    sprintf(global_debug_msg, "ERROR! manager: query_database(): entered with "
	    "sql[%i] frame[%i] server_data[%i]\n",
	    (int)sql_query, (int)frame, (int)server_data);
    write_debug_to_client(server_data, global_debug_msg);
    return 1;
  }

  // Variables needed
  //
  Gal_Frame f_new;
  char *val;
  
  // build frame for db request
  f_new = Gal_MakeFrame("main", GAL_CLAUSE);
  
  // three properties that should always be there
  Gal_SetProp(f_new, ":access_web", Gal_IntObject(access_web));
  Gal_SetProp(f_new, ":db_query", Gal_StringObject(sql_query));
  Gal_SetProp(f_new, ":task", Gal_StringObject(task));
  
  // add additional frame values here
  if( !strcmp(frame->name, "Query_Address") ||
      !strcmp(frame->name, "Query_List") ||
      !strcmp(frame->name, "Query_Direction") ||
      !strcmp(frame->name, "Query_Building") ||      
      !strcmp(frame->name, "Query_Distance") ) {
    
    // if     1) web_query
    //        2) question_type
    // and\or 3) merged_slot      exists in aFrame, add to Gal_Frame
    if( (val = getVal(frame, "[web_query]")) )
      Gal_SetProp(f_new, ":query_params", Gal_StringObject(val));      
    
    if( (val = getVal(frame, "[question_type]")) )
      Gal_SetProp(f_new, ":question_type", Gal_StringObject(val));
    
    if( (val = getVal(frame, "[merged_slot]")) )
      Gal_SetProp(f_new, ":merged_slot", Gal_StringObject(val));
  }
  
  /*
    if( !strcmp(frame->name, "Query_Address") ) {
    if( (val = getVal(frame, "[web_query]")) )
    Gal_SetProp(f_new, ":query_params", Gal_StringObject(val));      
    if( (val = getVal(frame, "[question_type]")) )
    Gal_SetProp(f_new, ":question_type", Gal_StringObject(val));
    }
    
    else if( !strcmp(frame->name, "Query_List") ) {
    if( (val = getVal(frame, "[web_query]")) )
    Gal_SetProp(f_new, ":query_params", Gal_StringObject(val));
    if( (val = getVal(frame, "[question_type]")) )
    Gal_SetProp(f_new, ":question_type", Gal_StringObject(val));
    }
    
    else if( !strcmp(frame->name, "Query_Direction") ) {
    if( (val = getVal(frame, "[web_query]")) )
    Gal_SetProp(f_new, ":query_params", Gal_StringObject(val));
    if( (val = getVal(frame, "[question_type]")) )
    Gal_SetProp(f_new, ":question_type", Gal_StringObject(val));
    }
    
    else if( !strcmp(frame->name, "Query_Distance") ) {
    if( (val = getVal(frame, "[web_query]")) )
    Gal_SetProp(f_new, ":query_params", Gal_StringObject(val));
    if( (val = getVal(frame, "[question_type]")) )
    Gal_SetProp(f_new, ":question_type", Gal_StringObject(val));
    }
  */
  else {
    printf("Unable to find type of frame to send to database! [%s]\n",
	   frame->name);
    return 1;
  }
  
  printf("==finished creating frame in query_database()===========:\n");
  print_frame(frame);
  printf("=================================================\n");
  
  
  // save in case of retry
  // These are global variables, I doubt they will ever be used, but they
  // are simple to do and keep around.
  //
  save_task = task;
  save_sql = sql_query;
  save_frame = frame;
  save_prompted = prompted;
  save_gf = f_new;
  
  // send to hub
  //
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data, f_new, 0);

  // don't forget to free the frame
  //
  Gal_FreeFrame(f_new);    

  // the state will be changed back in the receive_parse function
  // change_state(WAIT_FOR_DB, server_data);

  return 0; 
}

void retry_query(void *server_data) {
  Gal_Frame f_new;
  
  // This is an object and not a pointer. Is this object overloaded?
  //
  if( !save_gf )
    return;
  
  f_new = save_gf;
  
  // send to hub
  GalIO_CommWriteFrame(GalIO_GetUniqueConnection((GalIO_ServerStruct *)
						 server_data), f_new, 0);
}

/*-----------------------------------------------------------------------*
 * Routine which receives a database query result from the Hub		 * 
 *-----------------------------------------------------------------------*/

Gal_Frame receive_db_result(Gal_Frame f, void *server_data) {
  
  printf("entering 'receive_db_result'\n");
  fflush(stdout);
  
  char *local_task = NULL;
  char *frame_name = NULL;
  Gal_Frame result_frame;
  
  int num_keys;
  char **props;
  //  int	i;
  //  int ambiguous_input =0;
  //  int	xx;
    
  db_rows = 0;
  row_in_focus = -1;
  
  campus_depart = 0;
  campus_arrive = 0;
  
  // get frame of db result
  result_frame = Gal_GetFrame(f, ":query_result");
  if (result_frame == NULL) {
    sprintf(global_debug_msg, "ERROR! manager: receive_db_result(): Unable "
	    "to get :query_result from frame. Reseting to wait for parse.");
    write_debug_to_client(server_data, global_debug_msg);

    printf(global_debug_msg);
    change_state(WAIT_FOR_PARSE, server_data);
    return (Gal_Frame)NULL;
  }
  
  // get the properties of this frame
  props = Gal_GetProperties(result_frame, &num_keys);
  
  local_task = Gal_GetString(f, ":task");
  if (local_task == NULL) {
    sprintf(global_debug_msg, "ERROR! manager: receive_db_result(): "
	    "local_task has a NULL value! Reseting to wait for parse.");
    write_debug_to_client(server_data, global_debug_msg);

    printf(global_debug_msg);
    change_state(WAIT_FOR_PARSE, server_data);
    return (Gal_Frame)NULL;
  }
  
  //debug
  printf("Found local_task as [%s]\n",local_task);
  fflush(stdout);
  //end debug
  
  // Set the name of the frame. These are 'Query_Address', 'Query_Map', etc
  //
  if(!scmp("drive", local_task))
    frame_name = "Drive";
  else
    frame_name = (char *)local_task;

  // Find the number of rows returned
  //
  db_result.num_rows = Gal_GetInt(result_frame, ":nfound");

  //debug
  printf("DM: %d database records incoming\n", db_result.num_rows);
  fflush(stdout);
  //end debug

  // Since this is global, reset it
  //
  db_rows = 0;
  
  // :nfound is set to -1 when a SQL query is malformed.  It is
  // set to -2 when an internet time-out error occurs.
  //
  if (db_result.num_rows == -2) {
    prompt = "There was no response from the internet. "
      "Please try your query again.";
    set_prompted( (aFrame *)0, "[retry]", "yes");
    nl_request("prompt_user", prompt, NULL, 1, server_data, (char *)0);

    // failed with the db, so next thing that should be coming is another parse
    //
    change_state(WAIT_FOR_PARSE, server_data);
    return (Gal_Frame)NULL;
    //return f;
  }
  
  if (db_result.num_rows == -1) {
    prompt = "There was an error with the database.";
    nl_request("error_msg", prompt, NULL, 1, server_data, (char *)0);
    
    change_state(WAIT_FOR_PARSE, server_data);
    return (Gal_Frame)NULL;
    //return f;
  }
  
  db_rows = db_result.num_rows;
  // If no results from the database server, make sure there wasn't an error
  // with the type of query
  //
  if (db_rows == 0) {
    if( !strcmp(local_task, "Query_Address") || 
	!strcmp(local_task, "Query_List") || 
	!strcmp(local_task, "Query_Direction") ||
	!strcmp(local_task, "Query_Building") || 	
	!strcmp(local_task, "Query_Distance") ) {
      strcpy(prompt_text, "No records satisfy your request. "
	     "Please try again?");
      
      set_prompted( (aFrame *)0, "[retry]", "yes");
      nl_request("prompt_user", prompt_text, NULL, 1, server_data, (char *)0);
      data_current = (aFrame *)0;
      
      change_state(WAIT_FOR_PARSE, server_data);      
      return (Gal_Frame)NULL;
    }
    else {
      printf("local_task is not one of the "
	     "Query_Address/List/Direction/Distance\n");
      printf("this is an error in receive_db_result\n");
      sprintf(global_debug_msg, "error in receive_db_result(): 0 db_rows "
	      "returned and local_task is not one of the Query_XXX names. Val "
	      "is [%s]\n", local_task);

      write_debug_to_client(server_data, global_debug_msg);
      
      change_state(WAIT_FOR_PARSE, server_data);      
      return (Gal_Frame)NULL;
    }
  }
  
  // if we get here, must have some data (dm_rows > 0)
  //
  row_in_focus = 0;
  
  // unpack the returned data
  unpack_db_data(result_frame, &db_result);
  /*{
    int i;
    int r;
    int c;
    int nrows;
    int ncols;
    Gal_Object *gal_column_list;
    Gal_Object *gal_row;
    Gal_Object *gal_records;
    
    // put the column headings into the data structure
    gal_column_list = Gal_GetList(result_frame, ":column_names", &ncols);
    db_result.num_cols = ncols;
    for(i = 0; i < ncols; i++) {
    strcpy(db_result.headings[i], Gal_StringValue(gal_column_list[i]));
    }
    
    // get the rows placed into the data structure
    gal_records = Gal_GetList(result_frame, ":values", &nrows);
    
    // For each row ....
    //
    for(r = 0; r < nrows; r++) {
    gal_row = Gal_ListValue(gal_records[r], 0);
    // For each column, copy to the db_result structure
    //
    for(c = 0; c < ncols; c++)
    strcpy(db_result.item[r][c], Gal_StringValue(gal_row[c]));
    }
    
    printf("DM: %d database records received\n", db_result.num_rows);
    printf("Here's the first: %s\n", db_result.item[0][0]);
    fflush(stdout);
    
    }
  */
  
  // Right now db_result has all of the data returned from the database
  // in an easy-to-read structure-- see dm.h of dialog manager 'library'
  //
  // 'result_frame' still contains all the returned data
  // 'f' is still the original frame (which contains result_frame as an entry)
  
  // generate prompt string
  {
    char *adr;
    char *question_type;
    //char *val;
    //aFrame *cf;
    //int i;
    //int pos = 0;
    
    adr = db_result.item[0][0];
    question_type = Gal_GetString(f, ":question_type");
    if (question_type == NULL) {
      printf("in receive_db_result: question_type is NULL!\n");
      
      sprintf(global_debug_msg, "in receive_db_result: "
	      "question_type is NULL!\n");
      write_debug_to_client(server_data, global_debug_msg);
      change_state(WAIT_FOR_PARSE, server_data);
      return (Gal_Frame)NULL;
    }
    
    //debug
    printf("Entering 'generate_prompt_string':--\n");
    printf("frame_name[%s] ques_type[%s] adr[%s]\n",
	   frame_name, question_type, adr);
    fflush(stdout);
    //debug end
    
    // prompt or store the results
    
    // If the frame is 'Query_Address' and can find the context
    /*if (!scmp(frame_name, "Query_Address") && (cf = getContext(frame_name))) {
      
    // query where the place is
    if ( !scmp(question_type, "query_where") || 
    !scmp(question_type, "query_isthere") ||
    scmp(question_type, "query_list") ) {
    
    strcpy(prompt_text, db_result.item[0][1]);
    strcat(prompt_text, " is at ");
    strcat(prompt_text, adr);
    
    // store the address to the frame 
    //	  setKey(cf, "[Arrive_Loc].[Address]", adr);
    //	  setKey(cf, "[Arrive_Loc].[address]", adr);
    printf("update %s frame #1:\n", frame_name);
    
    print_frame(cf);
    }//end if 'query_where' or 'query_isthere' or if NOT 'query_list'
    
    // query place list
    //
    else if ( !scmp(question_type, "query_list") ) {
    sprintf(prompt_text, "%d records meet your request.",  db_rows);
    nl_request("prompt_user", prompt_text, NULL, 1,
    server_data, (char *)0);
    prompt_text[0] = 0;	
    // output names
    //
    for (i = 0; i < db_rows; i++) {
    if (i > 10) break;
    sprintf(prompt_text, "%s at %s.", 
    db_result.item[i][1], db_result.item[i][0]);
    nl_request("prompt_user", prompt_text, NULL, 1,
    server_data, (char *)0);
    prompt_text[0] = 0;
    }//end for output
    }//end elseif 'query_list'
    }//end if Query_Address
    */
    if (process_db_result_Query_Address(frame_name, question_type,
					server_data) > 0) {
      printf("Query_Address process returned successful!\n");
      //nl_request();
    }
    /*
      else if (!scmp(frame_name, "Query_List") && (cf=getContext(frame_name))) {
      
      // query where the place is 
      if ( !scmp(question_type, "query_where") || 
      !scmp(question_type, "query_isthere") ||
      scmp(question_type, "query_list") ) {
      strcpy(prompt_text, db_result.item[0][1]);
      strcat(prompt_text, " is at ");
      strcat(prompt_text, adr);
      
      // store the address to the frame 
      //	  setKey(cf, "[Arrive_Loc].[Address]", adr);
      //	  setKey(cf, "[Arrive_Loc].[address]", adr);
      printf("update %s frame:\n", frame_name);
      print_frame(cf);
      }// end query_where query_isthere or NOT query_list
      
      // query place list
      //
      else if ( !scmp(question_type, "query list") ) {
      sprintf(prompt_text, "%d records meet your request.",  db_rows);
      nl_request("prompt_user", prompt_text, NULL, 1, server_data, 
      (char *)0);
      prompt_text[0] = 0;	
      // output names
      //
      for (i = 0; i < db_rows; i++) {
      sprintf(prompt_text, "%s at %s.", 
      db_result.item[i][1], db_result.item[i][0]);
      nl_request("prompt_user", prompt_text, NULL, 1, server_data, 
      (char *)0);
      prompt_text[0] = 0;
      }//end for output
      }//end elseif 'query_list'
      }//end if Query_List
    */
    else if (process_db_result_Query_List(frame_name, question_type,
					  server_data) > 0) {
      printf("Query_List process returned successful!\n");
      //nl_request();
    }
    
    /*
      else if ( !scmp(frame_name, "Query_Direction") && 
      (cf= getContext(frame_name)) ) {
      
      // get prompted text "from * to *"
      //
      if( (val = getVal(cf, "[Depart_Loc].[Place_Name]")) ) {
      pos = sprintf(prompt_text, "From %s ", val);
      }
      else if ( (val = getVal(cf, "[Depart_Loc].[address]")) ) {
      pos = sprintf(prompt_text, "From %s ", val);
      }
      
      if( (val = getVal(cf, "[Arrive_Loc].[Place_Name]")) ) {
      pos += sprintf(prompt_text + pos, "to %s : ", val);
      }
      else if ( (val = getVal(cf, "[Arrive_Loc].[address]")) ) {
      pos += sprintf(prompt_text + pos, "to %s : ", val);
      }	
      
      //nl_request("prompt_user", prompt_text, NULL, 1, server_data, 
      //(char *)0);
      
      // query directions
      //
      if ( !scmp(question_type, "query_map") || 
      !scmp(question_type, "query_ispossible") ||
      scmp(question_type, "query_distance")) {
      //	  strcpy(prompt_text, adr);
      //	  sprintf(prompt_text+pos, "%s .", adr);
      
      sprintf(prompt_text, 
      "Your route is %s miles and contains %d segments:",  
      db_result.item[0][2], db_rows);
      nl_request("prompt_user", prompt_text, NULL, 1, server_data, 
      (char *)0);
      prompt_text[0] = 0;	
      // output each directions
      //
      for (i = 0; i < db_rows; i++) {
      sprintf(prompt_text, "%d:%s about %s miles.", i+1,
      db_result.item[i][0], db_result.item[i][1]);
      nl_request("prompt_user", prompt_text, NULL, 1, server_data, 
      (char *)0);
      prompt_text[0] = 0;	  
      }//end for
      }//end if map, or ispossible, or NOT query_distance
      
      // query distance
      //
      else if ( !scmp(question_type, "query_distance") ) {
      sprintf(prompt_text+pos, "the distance is %s miles .",  
      db_result.item[0][2]);
      }//end if distance
      }//end Query_Direction
    */
    else if (process_db_result_Query_Direction(frame_name, question_type,
					       server_data) > 0) {
      printf("Query_Direction process returned successful!\n");
      //nl_request();
    }
    
    /*
      else if ( !scmp(frame_name, "Query_Distance") && 
      (cf= getContext(frame_name)) ) {
      
      // get prompted text "from * to *"
      //
      if( (val = getVal(cf, "[Depart_Loc].[Place_Name]")) ) {
      pos = sprintf(prompt_text, "From %s ", val);
      }
      else if ( (val = getVal(cf, "[Depart_Loc].[address]")) ) {
      pos = sprintf(prompt_text, "From %s ", val);
      }
      else if ( (val = getVal(cf, "[Depart_Loc].[Campus_Name]")) ) {
      pos = sprintf(prompt_text, "From %s ", val);
      }
      else if ( (val = getVal(cf, "[Depart_Loc].[Building_Name]")) ) {
      pos = sprintf(prompt_text, "From %s ", val);
      }
      
      if( (val = getVal(cf, "[Arrive_Loc].[Place_Name]")) ) {
      pos += sprintf(prompt_text + pos, "to %s : ", val);
      }
      else if ( (val = getVal(cf, "[Arrive_Loc].[address]")) ) {
      pos += sprintf(prompt_text + pos, "to %s : ", val);
      }	
      else if ( (val = getVal(cf, "[Arrive_Loc].[Campus_Name]")) ) {
      pos += sprintf(prompt_text + pos, "to %s : ", val);
      }	
      else if ( (val = getVal(cf, "[Arrive_Loc].[Building_Name]")) ) {
      pos += sprintf(prompt_text + pos, "to %s : ", val);
      }	
      
      // query distance sub-types
      //
      if ( !scmp(question_type, "query_map") || 
      !scmp(question_type, "query_ispossible") ||
      scmp(question_type, "query_distance")) {
      //	  strcpy(prompt_text, adr);
      //	  sprintf(prompt_text+pos, "%s .", adr);
      
      sprintf(prompt_text, "Your route is %s and contains %d segments:", 
      db_result.item[0][2], db_rows);
      nl_request("prompt_user", prompt_text, NULL, 1, server_data, 
      (char *)0);
      prompt_text[0] = 0;
      
      // output each directions
      //
      for (i = 0; i < db_rows; i++) {
      sprintf(prompt_text, "%d:%s about %s miles.", i+1,
      db_result.item[i][0], db_result.item[i][1]);
      nl_request("prompt_user", prompt_text, NULL, 1, server_data, 
      (char *)0);
      prompt_text[0] = 0;
      }//end for
      }//end map, or ispossible, or NOT distance
      
      // query distance
      //
      else if ( !scmp(question_type, "query_distance") ) {
      sprintf(prompt_text+pos, "the distance is %s miles .",  
      db_result.item[0][2]);
      }//end distance
      }//end Query_Distance
    */
    else if (process_db_result_Query_Distance(frame_name, question_type,
					      server_data) > 0) {
      printf("Query_Distance process returned successful!\n");
      //nl_request();
    }
    
    // if no frame name
    else {
      printf("frame_name was not found to be 'Query_XXX'. Major error!\n");
      strcpy(prompt_text, adr);
    }
    
    // Only call this if prompt_text is not empty. This is the only prompt
    // that will be sent from all of the above db_result queries
    //
    if (prompt_text[0] != 0) {
      nl_request("prompt_user", prompt_text, NULL, 1, server_data, (char *)0);
    }
    
    // This frame has been used so remove all the data so
    // nothing bad can happen
    //
    Gal_DelProp(f,":db_query");
    Gal_DelProp(f,":task");
    Gal_DelProp(f,":query_result");
    Gal_DelProp(f,":merged_slot");
    Gal_DelProp(f,":question_type");
    Gal_DelProp(f,":access_web");
    
    //return original parameter frame..
    //
    change_state(WAIT_FOR_PARSE, server_data);
    return (Gal_Frame)NULL;
    //return f;
  }
  
  printf("UNREACHABLE CODE REACHED! #8857\n");
  fprintf(stderr,"UNREACHABLE CODE REACHED! #8857\n");
  fflush(stdout);
  fflush(stderr);
  
  /*
  // This is unreachable code from here down because the above has a
  // return statement and it is not a conditional brace.
  
  // generate prompt string
  prompt_text[0] = (char) 0; // clear it out
  
  // set frame in focus
  if (!(focus = data_current)) {
  if (frame_name)
  focus = getFrame(context, frame_name, "first", &xx);
  }
  
  // set prompted to accept
  if (focus) {
  one_row = set_selection(focus, row_in_focus);
  }
  
  // send to nlg server for output
  //if (!nl_task) {
  //printf("no nl_task and we are not doing summarize, so what now?\n");
  //nl_task= "summarize";
  //}
  //mwe-added this because above check would fix this
  //if (nl_task)
  //nl_request(nl_task, NULL, local_task, 1, server_data, (char *)0);
  //else {
  //printf("Unreachable code. If this is printed out "
  //"there are problems! #112\n");
  //}
  
  //backoff_cnt= 0;
  //backoff_tried= 0;
  */
  return (Gal_Frame)NULL;
  //return f;
}

// DONE. This can easily be re-written using
// 1) shortcircuiting technique of "if ( a || b)"
// 2) combining the logic because the top 8 lines are overwritten if
// it is query_map or query_ispossible...
int process_db_result_Query_Distance(char *frame_name, char *question_type,
				     void *server_data) {
  
  int i = 0;
  aFrame *cf;
  char *val;
  
  if (!scmp(frame_name, "Query_Distance") && (cf = getContext(frame_name))) {

    //erase prompt
    //
    prompt_text[0] = 0;
    
    // This will short circuit when it gets the first val and
    // basically try all 4 of these until it gets one
    //
    if( (val = getVal(cf, "[Depart_Loc].[Place_Name]")) ||
	(val = getVal(cf, "[Depart_Loc].[address]")) ||
	(val = getVal(cf, "[Depart_Loc].[Campus_Name]")) ||
	(val = getVal(cf, "[Depart_Loc].[Building_Name]")) ) {
      
      sprintf(prompt_text, "From %s ", val);
    }
    
    // This will short circuit when it gets the first val and
    // basically try all 4 of these until it gets one
    //
    if( (val = getVal(cf, "[Arrive_Loc].[Place_Name]")) ||
	(val = getVal(cf, "[Arrive_Loc].[address]")) ||
	(val = getVal(cf, "[Arrive_Loc].[Campus_Name]")) ||
	(val = getVal(cf, "[Arrive_Loc].[Building_Name]")) ) {
      
      sprintf(prompt_text, "%sto %s : ", prompt_text, val);
    }	

    // If the sub-type is "query_distance"
    if ( !scmp(question_type, "query_distance") ) {
      sprintf(prompt_text, "%sThe distance is %s miles.", prompt_text,
	      db_result.item[0][2]);
    }//end distance

    // else all other sub-types
    //
    else {
      //    if ( !scmp(question_type, "query_map") || 
      //         !scmp(question_type, "query_ispossible") ||
      //         scmp(question_type, "query_distance")) {
      
      sprintf(prompt_text, "%s\nYour route is %s and contains %d segments:", 
	      prompt_text, db_result.item[0][2], db_rows);
      
      // output each direction
      //
      for (i = 0; i < db_rows; i++) {
	sprintf(prompt_text, "%s\n%d:%s about %s miles.", prompt_text,
		i+1, db_result.item[i][0], db_result.item[i][1]);
      }//end for
    }//end map, or ispossible, or NOT distance
        
    return 1;
  }//end Query_Distance
  
  return 0;
}

int process_db_result_Query_Direction(char *frame_name, char *question_type,
				      void *server_data) {
  int i;
  aFrame *cf;
  char *val;
  
  if ( !scmp(frame_name, "Query_Direction") && (cf= getContext(frame_name)) ) {

    // erase the prompt because it's going to be overwritten
    prompt_text[0] = 0;
    
    // get prompted text "from * to *"
    //
    if( (val = getVal(cf, "[Depart_Loc].[Place_Name]")) ||
	(val = getVal(cf, "[Depart_Loc].[address]")) ) {
      sprintf(prompt_text, "From %s ", val);
    }
    
    if( (val = getVal(cf, "[Arrive_Loc].[Place_Name]")) ||
	(val = getVal(cf, "[Arrive_Loc].[address]")) ) {
      sprintf(prompt_text, "%sto %s : ", prompt_text, val);
    }	
    
    if ( !scmp(question_type, "query_distance") ) {
      sprintf(prompt_text, "%sThe distance is %s miles.",
	      prompt_text, db_result.item[0][2]);
    }//end if distance

    // query directions
    //
    else {
      //if ( !scmp(question_type, "query_map") || 
      //     !scmp(question_type, "query_ispossible") ||
      //     scmp(question_type, "query_distance")) {
      //	  strcpy(prompt_text, adr);
      //	  sprintf(prompt_text+pos, "%s .", adr);
      
      sprintf(prompt_text, 
	      "%s\nYour route is %s miles and contains %d segments:",
	      prompt_text, db_result.item[0][2], db_rows);
      // output each directions
      //
      for (i = 0; i < db_rows; i++) {
	sprintf(prompt_text, "%s\n%d:%s about %s miles.",
		prompt_text, i+1, db_result.item[i][0], db_result.item[i][1]);
      }//end for
    }//end if map, or ispossible, or NOT query_distance
    
    return 1;
  }//end Query_Direction
  return 0;
}

int process_db_result_Query_List(char *frame_name, char *question_type,
				 void *server_data) {
  
  aFrame *cf;
  int i;
  
  if (!scmp(frame_name, "Query_List") && (cf=getContext(frame_name))) {

    // This was originally written
    // if 'query_where' or 'query_isthere' or NOT 'query_list'
    // else if 'query_list'
    //
    // so basically if (A || B || !C) else if (C)
    // so it is a catch-all if\else statement, so for clarity, I am
    // rewriting to be
    // if (C) else { }
    //    
    if ( !scmp(question_type, "query list") ) {
      sprintf(prompt_text, "%d records meet your request.",  db_rows);
      // 2005-02-28 fix
      //nl_request("prompt_user", prompt_text, NULL, 1, server_data, 
      //		 (char *)0);
      //      prompt_text[0] = 0;
      // end 2005-02-28 fix      
      // output names
      //
      for (i = 0; i < db_rows; i++) {
	sprintf(prompt_text, "%s\n%s at %s.",
		prompt_text, db_result.item[i][1], db_result.item[i][0]);
	// 2005-02-28 fix
	//	nl_request("prompt_user", prompt_text, NULL, 1, server_data, 
	//		   (char *)0);
	//	prompt_text[0] = 0;
	// end 2005-02-28 fix
      }//end for output
    }//end if 'query_list'
        
    // query where the place is 
    else {
      //if ( !scmp(question_type, "query_where") || 
      //     !scmp(question_type, "query_isthere") ||
      //     scmp(question_type, "query_list") ) {
      sprintf(prompt_text, "%s is at %s",
	      db_result.item[0][1],
	      db_result.item[0][0]);
      
      // store the address to the frame 
      //	  setKey(cf, "[Arrive_Loc].[Address]", adr);
      //	  setKey(cf, "[Arrive_Loc].[address]", adr);
      printf("update %s frame:\n", frame_name);
      print_frame(cf);
    }// end NOT query_list
       
    return 1;
  }//end if Query_List
  
  return 0;
}

// prompt_text is global var of ~5000char
int process_db_result_Query_Address(char *frame_name, char *question_type,
				    void *server_data) {
  
  aFrame *cf;
  int i;

  // Either Query_Address or Query_Building
  //
  if ((!scmp(frame_name, "Query_Address") ||
       !scmp(frame_name, "Query_Building")) && (cf=getContext(frame_name))) {

    // This was originally written
    // if 'query_where' or 'query_isthere' or NOT 'query_list'
    // else if 'query_list'
    //
    // so basically if (A || B || !C) else if (C)
    // so it is a catch-all if\else statement, so for clarity, I am
    // rewriting to be
    // if (C) else { }
    //    
    if ( !scmp(question_type, "query list") ) {
      sprintf(prompt_text, "%d records meet your request.\n",  db_rows);

      // output names
      //
      for (i = 0; i < db_rows; i++) {
	if (i > 10) break;
	sprintf(prompt_text, "%s\n%s at %s.",
		prompt_text, db_result.item[i][1], db_result.item[i][0]);
      }//end for output
    }//end if 'query_list'
        
    // query where the place is 
    else {
      //if ( !scmp(question_type, "query_where") || 
      //     !scmp(question_type, "query_isthere") ||
      //     scmp(question_type, "query_list") ) {
      // Note: This is safe right now because both Query_Building
      // Query_Address have two items. (Query_Address has 3 though, so make
      // sure if you decide to use it, you change this function for
      // Query_Building.
      //
      sprintf(prompt_text, "%s is at %s",
	      db_result.item[0][1],
	      db_result.item[0][0]);
      
      // store the address to the frame 
      //	  setKey(cf, "[Arrive_Loc].[Address]", adr);
      //	  setKey(cf, "[Arrive_Loc].[address]", adr);
      printf("update %s frame:\n", frame_name);
      print_frame(cf);
    }// end NOT query_list
        
    return 1;
  }//end if Query_Address

  return 0;
}


/*-----------------------------------------------------------------------*
 * Routine which ends the session                                        *
 *-----------------------------------------------------------------------*/

//DELETE_ME
/*void hangup_phone(void *server_data) {
  
Gal_Frame f_new = Gal_MakeFrame("audio", GAL_CLAUSE);
Gal_SetProp(f_new, ":call_finished", Gal_IntObject(1));

GalIO_CommWriteFrame(GalIO_GetUniqueConnection(
(GalIO_ServerStruct *) server_data), f_new, 0);
Gal_RemoveTimedTask((void*)no_response_from_user, NULL);
}
*/

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

Gal_Frame reinitialize(Gal_Frame f, void *server_data) {
  
  printf("DM: Resetting dm via 'reinitialize'\n");
  fflush(stdout);
  
  Gal_RemoveTimedTask((void*)no_response_from_user, NULL);
  
  enable_input_ok = 0;
  //user_logged_on = 0;
  reset_dm();
  reset_task();

  server_state_d = INITIAL_STATE;
  
  prompt = "How may I help you? - Dialog navigation manager started";
  
  nl_request("prompt_user", prompt, NULL, 1, server_data, (char *)0);
  
  return f;
}


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

Gal_Frame get_process_id(Gal_Frame f, void *server_data) {
  
  Gal_Frame f_new;
  
  char *program_name = Gal_GetString(f, ":program");
  char server_name[60];
  
  if (!program_name)
    program_name = "main";
  
  strcpy(server_name, "dialogue_serv");
  
  int pid = getpid();
  
  f_new = Gal_MakeFrame(program_name, GAL_CLAUSE);
  
  Gal_SetProp(f_new, ":server_name", Gal_StringObject(server_name));
  Gal_SetProp(f_new, ":pid", Gal_IntObject(pid));
  Gal_SetProp(f_new, ":program", Gal_StringObject(program_name));
  
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data, f_new, 0);
  
  Gal_FreeFrame(f_new);
  
  // Goto the next server state
  //
  //server_state_d = WAIT_FOR_PARSE;
  change_state(WAIT_FOR_PARSE, server_data);
  
  return (Gal_Frame) NULL;
}

Gal_Frame reset_task(Gal_Frame f, void *server_data) {
  
  enable_input_ok = 0;
  //user_logged_on = 0;
  reset_dm();
  reset_task();
  fflush(stdout);
  
  prompt = "Where would you like to go?";
  nl_request("prompt_user", prompt, NULL, 1, server_data, (char *)0);
  
  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) {

enable_input_ok = 0;
reset_dm();
reset_task();
change_state(WAIT_FOR_PARSE, server_data_a);
return (Gal_Frame) NULL;
}

//void *init_server(Gal_Server *s, int argc, char **argv) {
void *_GalSS_init_server(Gal_Server *s, int argc, char **argv) {
  
  printf("Entering init_server() function\n");
  
  main_server_data = s;
  if (argc == 1) {
    pusage(argv[0], (Config_t*)dm_conf);
    exit(-1);
  }
  
  config(argc, argv, (Config_t*)dm_conf, NULL);
  
  init_dm(dir);
  
  return (void *) NULL;
}

/*-----------------------------------------------------------------------*
 * Routine which logs the end of the session                             *
 *-----------------------------------------------------------------------*/

/*
  void log_session_end(void *server_data) {
  Gal_Frame f_new = Gal_MakeFrame("main", GAL_CLAUSE);
  Gal_SetProp(f_new, ":end_task", Gal_IntObject(1));
  
  printf("Entering log_session_end and about to write a frame\n");
  fflush(stdout);
  
  GalIO_CommWriteFrame(GalIO_GetUniqueConnection(
  (GalIO_ServerStruct *) server_data), f_new, 0);
  }
*/

/*-----------------------------------------------------------------------*
 * Puts column names from header and puts them in a list object.         *
 *-----------------------------------------------------------------------*/

Gal_Object build_column_list() {
  
  int i;
  Gal_Object colnames[MAX_COLS];
  Gal_Object collist;
  
  for(i = 0; i < db_result.num_cols; i++)
    colnames[i] = Gal_StringObject(db_result.headings[i]);
  
  collist = Gal_ListObject(colnames, db_result.num_cols);
  
  return collist;
}

/*-----------------------------------------------------------------------*
 * Puts column info from ith row into a list object.                     *
 *-----------------------------------------------------------------------*/

Gal_Object list_from_line(int i) {
  
  Gal_Object no[MAX_COLS], nf;
  int j;
  
  for(j=0;j<db_result.num_cols;j++)
    no[j] = Gal_StringObject(db_result.item[i][j]);
  nf = Gal_ListObject(no, db_result.num_cols);
  
  return nf;
}

/*-----------------------------------------------------------------------*
 * This routine packs up the DB information so that it can be sent as a  *
 * frame to the NL server                                                *
 *-----------------------------------------------------------------------*/

Gal_Frame package_db_data(int row_in_focus) {
  
  int nlines = 0;
  int i;
  Gal_Frame nf;
  Gal_Object column_list;
  Gal_Object tlist[MAX_ROWS];
  
  nf = Gal_MakeFrame("result", GAL_CLAUSE);
  column_list = build_column_list();
  
  for(i = 0; i < db_result.num_rows; i++) {
    tlist[nlines]= list_from_line(i);
    // Moved down for read-ability
    //
    nlines++;
  }
  
  Gal_SetProp(nf,":column_names", column_list);
  Gal_SetProp(nf,":nrows",Gal_IntObject(db_result.num_rows));
  Gal_SetProp(nf,":ncols",Gal_IntObject(db_result.num_cols));
  Gal_SetProp(nf,":row_in_focus",Gal_IntObject(row_in_focus));
  
  if (nlines > 0)
    Gal_SetProp(nf,":values",Gal_ListObject(tlist,nlines));
  
  return nf;
}

/*-----------------------------------------------------------------------*
 * This routine packages up the internal "leg" structure and prepares a  *
 * frame to be transmitted to the NL server.                             *
 *-----------------------------------------------------------------------*/

Gal_Frame package_db_row(struct db_row *row, char *name){
  
  Gal_Frame nf;
  int ncols;
  int j;
  Gal_Object tlist[MAX_ROWS];
  Gal_Object column_list;
  Gal_Object no[MAX_COLS];
  Gal_Object colnames[MAX_COLS];
  
  // Make a new frame and then copy the headers from *row into it
  nf = Gal_MakeFrame(name, GAL_CLAUSE);
  ncols = row->num_cols;
  for(j = 0; j < ncols; j++)
    colnames[j] = Gal_StringObject(row->headings[j]);
  
  column_list = Gal_ListObject(colnames, ncols);
  
  for(j = 0; j < ncols; j++)
    no[j] = Gal_StringObject(row->item[j]);
  
  tlist[0]= Gal_ListObject(no, ncols);
  
  Gal_SetProp(nf,":column_names", column_list);
  Gal_SetProp(nf,":nrows", Gal_IntObject(1));
  Gal_SetProp(nf,":ncols", Gal_IntObject(ncols));
  Gal_SetProp(nf,":values", Gal_ListObject(tlist,1));
  
  return nf;
}

/*-----------------------------------------------------------------------*
 * Routine to copy a row to a db_row structure                           *
 *-----------------------------------------------------------------------*/

struct db_row * copy_db_row(int row_index) {
  
  struct db_row *row;
  int i;
  
  row = (struct db_row *)malloc(sizeof(struct db_row));
  row->num_cols = db_result.num_cols;

  printf("copy_db_row: creating heading matrix\n"); fflush(stdout);
  cCreateMatrix(&(row->headings), MAX_COLS, MAX_STR_LENGTH);

  printf("copy_db_row: creating item matrix\n"); fflush(stdout);
  cCreateMatrix(&(row->item), MAX_COLS, MAX_STR_LENGTH);

  for(i = 0; i < row->num_cols; i++) {
    printf("copy_db_row: copying matrix item [%i]\n", i); fflush(stdout);
    strcpy(row->headings[i], db_result.headings[i]);
    strcpy(row->item[i], db_result.item[row_index][i]);
  }
  return(row);
}

// takes in a result frame and a db_result structure and moves from
// one to the other
void unpack_db_data(Gal_Frame src_frame, struct db_query *db_result) {
  
  // Error check
  if (!db_result)
    return;
  
  Gal_Object *column_list;
  Gal_Object *row;
  Gal_Object *records;
  int r;
  int c;
  int nrows;
  int ncols;
  
  // put the column headings into the data structure
  column_list = Gal_GetList(src_frame, ":column_names", &ncols);
  db_result->num_cols = ncols;
  for(c = 0; c < ncols; c++) {
    strcpy(db_result->headings[c], Gal_StringValue(column_list[c]));
  }
  
  // get the rows placed into the data structure
  records = Gal_GetList(src_frame, ":values", &nrows);
  for(r = 0; r < nrows; r++) {
    row = Gal_ListValue(records[r], 0);
    for(c = 0; c < ncols; c++)
      strcpy(db_result->item[r][c], Gal_StringValue(row[c]));
  }
}

void get_col_info(int row, char *heading_name, char *out) {
  struct db_row *one_row;
  
  out[0]= '\0';
  if( row >= db_rows )
    return;
  
  one_row = copy_db_row(row);
  get_column_info(one_row, heading_name, out);
  free_db_row(one_row);
}

struct db_row *set_selection(aFrame *frame, int row) {

  printf("set_selection: entered with ptr[%08x] row[%i]\n", (int)frame, row);
  fflush(stdout);
  
  struct db_row *one_row;
  char *s;
  char **kp;
  
  if( !(kp = getKeyPtr(frame, "[selection]")) )
    return( (struct db_row *)0 );

  printf("set_selection: found kp\n"); fflush(stdout);
  
  // copy row into struct
  one_row = copy_db_row(row);     

  printf("set_selection: copied row\n"); fflush(stdout);
  if( (s = getVal(frame, "[selection]")) ) {
    // this cannot work. 's' has no member data
    // so free_db_row would die a horrible death.
    //
    // This is a potential reason why the demo would crash if it does at all
    // I'm not sure how it hasn't ever crashed except if this code is never
    // executed.
    //
    printf("set_selection: about to free\n"); fflush(stdout);
    free_db_row( (struct db_row *) s);
    
  }

  printf("set_selection: exiting func\n"); fflush(stdout);  
  *kp = (char *) one_row;
  return(one_row);
}
