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

//FRAMEWORK FOR MESSAGE BEFORE FUNCTIONS
// method: computeLatticeBurg
//
// arguments:
//  VectorFloat& pred_coef: (output) prediction coefficients
//  float& err_energy: (output) error energy  
//  const VectorFloat& input: (input) input data
//
// return: a boolean value indicating status
//
// this method computes the prediction coefficients using the burg
// analysis method. it also outputs the error energy.
//

/*************************************************************************
 *                                                                       * 
 *                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 Sybase DB Server Interface               * 
 *                                                                       *
 *   This server is an interface to the Sybase Adaptive Server Anywhere  *
 *   for Unix software.  It has been tested for Solaris x86 v2.6.  The   *
 *   server is designed using the MITRE DB server as a guide and	 *
 *   provides the same functionalities.  				 *
 *									 *
 *   Usage:								 *
 *     db_server -uid <username> -pwd <password> -name <database> 	 *
 *	         -travelocity <filename>				 *
 *									 *
 *   The "travelocity" command-line argument points to a file containing *
 *   sequences of logins and passwords for www.travelocity.comw.         *
 *   Each row of this file should contain a unique login along with a    *
 *   corresponding password (separated by white space).  During	 	 *
 *   operation, each login is used for 50 queries before going onto the  *
 *   next login.  Once the last login is reached, the code switches to   *
 *   the first login in the list (in a round-robin format).		 *
 *									 *
 *   The code expects an input frame containing an SQL query:		 *
 *									 *
 *   {c foobar								 *
 *     :db_query "select airline, flight_id, depart_time, arrive_time,   *
 *	       stop_nonstop, meal, equip_type from airline_flight 	 *
 *	       where depart_airport = 'BOS' and arrive_airport = 'LAX'"  * 
 *	       :maxrows 10 }						 *
 *									 *
 *   It returns a frame like,						 *
 *									 *
 *   {c foobar								 *
 *	:query_result {c result						 *
 *	     :column_names ( "airline" "flight_id" ... )		 *
 *	     :nfound 2							 *
 *	     :values ( ( "AA" "11" "815" "1148" "0" "B" "757" ) ( .. )   *
 *									 *
 *									 *
 *   Special Return Values:						 *
 *									 *
 *      :nfound is set to -1 when a SQL query is malformed.  It is 	 *
 *      set to -2 when an internet time-out error occurs.		 *
 *									 *
 *   Ambiguous Input:							 *
 *									 *
 *      When ambiguous input is detected (e.g., "Paris France or Paris   *
 *      Kentucky"??), the system returns 0 records and adds 3 key-value  *
 *      operators onto the returned frame.  The keys are:		 *
 *									 *
 *	      :ambiguity 	  <- string describing the problem	 *
 *	      :ambiguity_choices  <- colon delimited list of choices     *
 *	      :ambiguity_leg	  <- the leg number of the trip where    *
 *				     the ambiguity has been detected.    *
 *									 *
 *   We would like to extend a special thanks to Andrei A. Pokrovsky     *
 *   (andrei@research.att.com) for his helpful comments and bug-fixes.   *
 *									 *
 *   Bryan L. Pellom (bp@cslu.colorado.edu)                              *
 *   Center for Spoken Language Understanding                            *
 *   University of Colorado at Boulder                                   *
 *                                                                       *
 *-----------------------------------------------------------------------*/

#include "isip_comm_dialog_navigation_db.h"

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}  
#endif

// These prototypes and declarations must be right here because this is
// the only file that knows what a Gal_Object is.Because if the .h of the
// galaxy is included elsewhere, there are linking errors because
// of the data contained within it.
//
Gal_Object *tlist = NULL;

Gal_Object build_column_list(char *str, int *num_cols);
Gal_Object list_from_line(char *str, int num_cols);
Gal_Frame process_query(char *sql_query, void *server_data);
Gal_Frame sql_query(Gal_Frame frame, 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_QUERY";
  case 6: return "WAIT_FOR_QUERY_ACK";
    
  case 12: return "PROCESSING_QUERY";
    
  case 13: return "PROCESSING_WEB_QUERY";
  case 14: return "WAITING_FOR_WEB_RESULT";
  }
  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) {

  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;
}


// check if the database parameters are needed and send a database
// query message
//
Gal_Frame check_database_parameters(Gal_Frame frame_a, void *server_data_a) {

  char *program_name = "main";
   
  printf("before sending");
  Gal_Frame query_f = Gal_MakeFrame(program_name, GAL_CLAUSE);
  Gal_SetProp(query_f, ":database_parameters", Gal_StringObject("database_parameters"));
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data_a, query_f, 0);
  Gal_FreeFrame(query_f);
  printf("after sending");

  // }
  
  //else {
    // do nothing
  //}
  
  return (Gal_Frame) NULL;
}


// used to set the database parameters
//
Gal_Frame set_database_parameters(Gal_Frame frame_a, void *server_data_a) {

  db_username = Gal_GetString(frame_a, ":username");
  db_password = Gal_GetString(frame_a, ":password");
  db_database = Gal_GetString(frame_a, ":database");
  db_host = Gal_GetString(frame_a, ":hostname");
  
  init_sql();
}
 
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
    //
    init_sql();
    change_state(WAIT_FOR_QUERY, 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_QUERY) {
    return sql_query(frame_a, server_data_a);
  }
  else if (server_state_d == WAITING_FOR_WEB_RESULT ||
	   server_state_d == PROCESSING_WEB_QUERY ||
	   server_state_d == PROCESSING_QUERY) {
    
    // 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! Database 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_QUERY\n");
    write_debug_to_client(server_data_a, "Server state was unknown state!"
			  " Resetting state to WAIT_FOR_QUERY");
    change_state(WAIT_FOR_QUERY, server_data_a);
  }
  
  return (Gal_Frame) NULL;
}
// END FUNCTIONS TO HELP WITH DEBUGGING


// method:  _GalSS_print_usage
//
// arguments:
//  int argc: (input) number of arguments
//  char ** argv: (input) arguments in the command line
//  
// return value: none
//
// This function prints out the usage message for this server.  It is
// called by Galaxy Communicator structure when users using -help
// option.
//
void _GalSS_print_usage(int argc, char **argv) {
  
  GalUtil_OAPrintOptions(argc, argv, oas, NULL);
  printf("\n");
  
  // return none
  //
  return;
}

/*
  // code to test out multithreaded stuff. I put the parse out of nlp
  // equal to this. that way i could easily send two values
  //
  // result: I could not get the server to print out two things at one time
  // so perhaps the server is not multithreaded. the galaxy comm was nice
  // enough to just hold the frames until the server was accessible though
  //
int gval;
Gal_Frame test_multi_thread(Gal_Frame frame, void *server_data) {
  static int value = 0;
  static int threadval[5] = {55,55,55,55,55};
  static int threadnum = 0;

  printf("entering test_multi\n");
  fflush(stdout);

  char *in = Gal_GetString(frame, ":input_string");
  if (!in) {
    printf("test multi thread -- Did not contain an input_string!!!\n");
    fflush(stdout);
    return (Gal_Frame)NULL;
  }

  int thread = threadnum;
  threadnum = (threadnum+1)%5;

  printf("inside thread1 (even) with thread[%i]\n", thread);
  fflush(stdout);
  if (thread%2 == 0) {
    printf("exiting\n"); fflush(stdout);
    return (Gal_Frame)NULL;
  }
  
  threadval[thread] = (int)in[0];
  gval = value = threadval[thread];  

  for (int i=0;i<15;i++) {
    printf("[%i] Thread1 %i static_value[%i] gval[%i] threadval[%i]\n",
	   i+1,
	   threadnum,
	   value,
	   gval,
	   threadval[threadnum]);
    fflush(stdout);
    sleep(3);
  } 

  return (Gal_Frame)NULL;
}

Gal_Frame test_multi_thread2(Gal_Frame frame, void *server_data) {
  static int value = 0;
  static int threadval[5] = {55,55,55,55,55};
  static int threadnum = 0;

  printf("entering test_multi\n");
  fflush(stdout);

  char *in = Gal_GetString(frame, ":input_string");
  if (!in) {
    printf("test multi thread -- Did not contain an input_string!!!\n");
    fflush(stdout);
    return (Gal_Frame)NULL;
  }

  int thread = threadnum;
  threadnum = (threadnum+1)%5;

  printf("inside thread2 (odd) with thread[%i]\n",thread);
  fflush(stdout);
  if (thread%2 != 0) {
    printf("exiting\n"); fflush(stdout);
    return (Gal_Frame)NULL;
  }

  threadval[thread] = (int)in[0];
  gval = value = threadval[thread];  

  for (int i=0;i<15;i++) {
    printf("[%i] Thread2 %i static_value[%i] gval[%i] threadval[%i]\n",
	   i+1,
	   threadnum,
	   value,
	   gval,
	   threadval[thread]);
    fflush(stdout);
    sleep(3);
  } 

  return (Gal_Frame)NULL;
}
*/

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

Gal_Frame reinitialize(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, "db_server");
  
  fprintf(stdout, "db_started in reinitialize\n");  
  fflush(stdout);
  
  new_f = Gal_MakeFrame(program_name, GAL_CLAUSE);
  Gal_SetProp(new_f, ":restart", Gal_StringObject(server_name));
  Gal_SetProp(new_f, ":program", Gal_StringObject(program_name));
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data, new_f, 0);
  Gal_FreeFrame(new_f);

  server_state_d = INITIAL_STATE;
  
  return (Gal_Frame) 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, "db_server");

  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);

  // Goto the next server state
  //
  change_state(WAIT_FOR_QUERY, server_data);

  return (Gal_Frame) NULL;
}


/*-----------------------------------------------------------------------*
 * Routines which connect and disconnect client from database server     * 
 *-----------------------------------------------------------------------*/

// this function only returns on success. It exits with exit(0) otherwise.
//
void init_sql() {
  sql_connect();
}

void exit_sql() {
  sql_disconnect();
}

/*-----------------------------------------------------------------------*
 * Main routine which performs the SQL query of the database		 * 
 *-----------------------------------------------------------------------*/

Gal_Frame sql_query(Gal_Frame frame, void *server_data) {
  
  //debug 
  printf("Entering sql_query in state[%s]\n",
	 find_name_of_state(server_state_d));
  //end debug

  char *sql_string;
  char **props;

  int nfound;
  int num_keys = 0;
  int i;
  int result;
  int access_web;
  
  char key[20];
  char *task;

  // set the new state to processing SQL
  //
  change_state(PROCESSING_QUERY, server_data);
  
  Gal_Frame result_frame = NULL;

  printf("\nsql_query(): received request frame:\n");
  Gal_PPFrame(frame); 
  TIME_OUT_SECONDS = DEFAULT_TIME_OUT_SECONDS;

  /*----------- get the keys from the input frame --------------------*/

  sql_string = task = NULL;
  access_web = 1;
  props = Gal_GetProperties(frame, &num_keys);

  fflush(stdout);
  printf("\nread frame. num_keys=%d\n", num_keys);
  for (i = 0; i < num_keys; i++) {
    printf("[%i][%s]\n", i, props[i]);
    // Look through all properties and find out data we need from the frame
    //
    if (strcmp(props[i],":db_query") == 0) {
       sql_string = Gal_GetString(frame, ":db_query");
       strcpy(key,":db_query");
    }

    else if (strcmp(props[i],":input_string") == 0) {
       sql_string = Gal_GetString(frame, ":input_string");
       strcpy(key,":input_string");
    }

    else if (strcmp(props[i],":maxrows") == 0)
       request_max_rows = Gal_GetInt(frame, ":maxrows");

    else if (strcmp(props[i],":task") == 0)
       task = Gal_GetString(frame, ":task");

    else if (strcmp(props[i],":access_web") == 0)
       access_web = Gal_GetInt(frame, ":access_web");

    else if (strcmp(props[i],":time_out_duration") == 0)
       TIME_OUT_SECONDS = Gal_GetInt(frame, ":time_out_duration");
  }

  // Check bounds of requested max rows
  if ((request_max_rows <= 0) || (request_max_rows > server_max_rows))
    request_max_rows = server_max_rows;

  // If no sql string was present then this is a major error
  //
  if (sql_string == NULL) {
     printf("ERROR: no SQL query string was provided with frame!\n");
     printf("Use the :db_query or :input_string keys for your SQL\n");
     fprintf(stderr,"ERROR: no SQL query string was provided with frame!\n");
     write_debug_to_client(server_data, "The frame to the DB did not have"
			   " a query with it! Error!");
     fflush(stdout);
     fflush(stderr);
     change_state(WAIT_FOR_QUERY, server_data);
     
     return (Gal_Frame)NULL;
  }
  
  fflush(stdout);

  // ----- first try performing a selection of records in the DB ---

  if (access_web < 2) {
     result_frame = process_query(sql_string, server_data);
     nfound = Gal_GetInt(result_frame, ":nfound");
  }  
  else
    nfound = 0;
  
  // ----- update tables using web information if necessary --------

  // compare task to cityguide
  // do something different if nfound == 0 and task == "Query_List"
  // do something different if nfound == 0 and task == "Query_Address"

  // This is when the web is used.

  // if no results from previous process_query(), there is a task defined
  // and access_web says to do it
  if ((nfound == 0) && task && (access_web > 0)) {
    printf("nfound=0 and access_web>0 so making web_call() -- task= %s\n",
	   task);

    // Web call!
    //
    change_state(PROCESSING_WEB_QUERY, server_data);
    result = update_tables_from_web(frame, server_data, get_sql_connection());
    //TODO, check to see if this will return -1

    printf("in sql_query(): returned from update_tables_from_web()\n");
    fflush(stdout);
    change_state(PROCESSING_QUERY, server_data);
   
    //
    // This is where we would check for (result == AMBIGUOUS_INPUT)
    // but our code does not do that at all
    //

    // NO_RESPONSE_FROM_INTERNET == -2
    //
    if (result == NO_RESPONSE_FROM_INTERNET) {
      printf("Entering no result from inet\n");
      fflush(stdout);
      Gal_SetProp(result_frame, ":nfound",
		  Gal_IntObject(NO_RESPONSE_FROM_INTERNET));
    }
    else {
      // This else means one of two things.
      // if result == -1 then the query is malformed
      // else there is more than likely the data we originally asked for
      // now loaded into the database. Go ahead and run the same query again
      // and hope that the information was found from the web
      //
      printf("running \"process_query\"\n");
      fflush(stdout);
      result_frame = process_query(sql_string, server_data);
      nfound = Gal_GetInt(result_frame, ":nfound");
    }
  }

  printf("sql_query(): the result frame being sent back is:\n");
  fflush(stdout);
  Gal_PPFrame(result_frame);
  fflush(stdout);  

  // Put the result frame into the frame and delete a couple other properties
  //
  write_debug_to_client(server_data, "Packing result frame into frame to "
			"return to the navigation mgr.");
  Gal_SetProp(frame, ":query_result", Gal_FrameObject(result_frame));
  Gal_DelProp(frame, key);
  Gal_DelProp(frame, ":maxrows");
  
  printf("\nsending reply frame with %d matching records\n", nfound);
  fflush(stdout);
  request_max_rows = server_max_rows;

  change_state(WAIT_FOR_QUERY, server_data);
  return frame;
}

/*-----------------------------------------------------------------------*
 * Routine which packs up a row returned from a DB server and puts it in *
 * a frame structure.							 *
 *-----------------------------------------------------------------------*/

Gal_Frame process_query(char *sql_query, void *server_data) {

  Gal_Frame nf;
  Gal_Object no;
  char str[4048];
  Gal_Object column_list;
  int result;
  int num_cols = 0;
  int nlines = 0;

  //
  result = do_sql_query(sql_query);
  nf = Gal_MakeFrame("result", GAL_CLAUSE);

  // Result is -1 if an error occurred above in "do_sql_query"

  // if there is no error, pack up these results into a frame
  // and return the frame
  if (result != -1) {
     fetch_header(rowbuf);
     column_list = build_column_list(rowbuf, &num_cols);

     while (fetch_row(rowbuf) > 0)  {
        strcpy(str, rowbuf);
        no = list_from_line(str, num_cols);
        if (no && (nlines < request_max_rows))
          tlist[nlines++] = no;
     } 
     
     Gal_SetProp(nf,":column_names", column_list);
     Gal_SetProp(nf,":nfound",Gal_IntObject(nlines));
     if (nlines > 0)
       Gal_SetProp(nf,":values", Gal_ListObject(tlist,nlines));
  }
  else
    Gal_SetProp(nf,":nfound", Gal_IntObject(-1));

  // Cannot do deallocation here because this frame is used within
  // the other functions that called this function.
  // TODO, follow this function and deallocate the frame
  //
  //GalSS_EnvWriteFrame((GalSS_Environment *) server_data, nf, 0);
  //Gal_FreeFrame(nf);
  //return (Gal_Frame) NULL;
  
  return nf;
}

/*-----------------------------------------------------------------------*
 * Puts a string into lower case format.			 	 *  
 *-----------------------------------------------------------------------*/

static void string_downcase (char *string) {

  // check for null pointer
  if (string == NULL)
    return;
  
  //unsigned int i;
  //for (i = 0; i < strlen(string); i++)
  //  string[i] = tolower ((int)string[i]);
  char *c = string;
  for (;*c; c++) {
    *c = tolower((int)*c);
  } 
}

//-----------------------------------------------------------------------
// Pulls column names from header and puts them in a list object.        
//-----------------------------------------------------------------------
Gal_Object build_column_list(char *str, int *num_cols) {
  
  char *cp;
  char token[1128];
  char buf[4048];
  Gal_Object colnames[MAX_COLS];
  Gal_Object collist;
  int ncols = 0;

  // make a copy of the global buffer and put into local buffer here
  //
  memcpy(buf, rowbuf, 4048);
  
  cp = buf;

  while ((cp = Gal_NextToken(cp, token, 1128)) != NULL) {
    if (token[0] == '(' || token[0] == ')')
      continue;

    string_downcase(token);

    colnames[ncols] = Gal_StringObject(token);
    ncols++;
  }
  
  collist = Gal_ListObject(colnames, ncols);

  // Set number of columns
  *num_cols = ncols;
  
  return collist;
}

/*-----------------------------------------------------------------------*
 * grabs the next token from the result set buffer.  differentiates	 *
 * between strings and ints						 *
 *-----------------------------------------------------------------------*/

char *get_next_value(char *str, int *type, int *ival, char *cval) {
  
  char *cp;
  char temp[1028];
  int i = 0;

  // Error checking
  //
  if (str == NULL || type == NULL || ival == NULL || cval == NULL)
    return NULL;

  // Find first non-space or end of text
  //
  for (cp = str; *cp; cp++) {
    if(!isspace((int)*cp))
      break;
  }
  // If end of text, return
  //
  if (*cp == 0)
    return(NULL);

  // If this is a "
  //
  if (*cp == '"') {
    cp++;

    // Copy all characters until next "
    // into 'temp'
    //
    while(*cp && *cp != '"')
      temp[i++] = *cp++;

    // If never found another ", return
    //
    if(*cp == 0)
      return(NULL);

    // This means we found another ", so pass it and set a null char
    // for end of string
    //
    cp++;
    temp[i] = 0;
    
    *type = TYPE_STRING;

    // This will leave a single 'space' if it is entirely
    // filled with spaces, is that what is desired?
    // 
    //for (i = strlen(temp)-1; i > 0; i--){
    //  if(isspace((int)temp[i])) temp[i] = 0;
    //  else break;
    //}
    // Remove all trailing whitespace
    for(i = strlen(temp)-1; isspace(temp[i]); --i);
    temp[i+1] = 0;

    strcpy(cval,temp);
    return(cp);
  }

  else {
    cp = Gal_NextToken(cp, temp, 1028);
    *ival = atoi(temp);
    *type = TYPE_INT;
    
    return(cp);
  }
  // Unreachable code
  //
  return(NULL);
}

// generates a list object from a result row
Gal_Object list_from_line(char *str, int num_cols) {
  
  char *cp = str;
  char cval[1128];
  int ival;
  int type;
  Gal_Object nf;
  Gal_Object no[MAX_COLS];

  if (cp == NULL)
    return (Gal_Object)NULL;
  
  for (int i = 0; i < num_cols; i++) {
    
    cp = get_next_value(cp, &type, &ival, cval);
    if (cp == NULL) {
      printf("Bad column %s\n",str);
      return(NULL);
    }
    if (type == TYPE_INT) {
      no[i] = Gal_IntObject(ival);
    }
    else if (type == TYPE_STRING) {
      no[i] = Gal_StringObject(cval);
    }
    else {
      printf("Unreachable code! If you get here it's bad! #3343\n");
      fflush(stdout);
      exit(-1);
    }

  }
  nf = Gal_ListObject(no, num_cols);
  return(nf);
}

// signal handler that disconnects server from database and exits
void sig_handler(int signum) {
  printf("Caught signal! Disconnecting from the database "
	 "and freeing memory\n");
  fflush(stdout);  

  exit_sql();
  if (tlist)
    free(tlist);
  exit(0);
}

// 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) {

  //init_sql();
  change_state(WAIT_FOR_QUERY, server_data_a);
    
  return (Gal_Frame) NULL;
  
}

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

  Gal_AddSignalHandler(SIGINT, sig_handler);
  
  if (GalUtil_OACheckUsage(argc, argv, oas, NULL) == 0)
    exit(-1);


 
  GalUtil_OAExtract(argc, argv, oas, "-user", GAL_OA_STRING, &db_username);
  GalUtil_OAExtract(argc, argv, oas, "-password", GAL_OA_STRING, &db_password);
  GalUtil_OAExtract(argc, argv, oas, "-database", GAL_OA_STRING, &db_database);
  GalUtil_OAExtract(argc, argv, oas, "-host", GAL_OA_STRING, &db_host);  
  GalUtil_OAExtract(argc, argv, oas, "-perlscript", GAL_OA_STRING,
		    &perlscript);
  //GalUtil_OAExtract(
  //argc, argv, oas, "-travelocity", GAL_OA_STRING, &travelocity);
  GalUtil_OAExtract(argc, argv, oas, "-maxrows", GAL_OA_INT, &server_max_rows);
  GalUtil_OAExtract(argc, argv, oas, "-unique_num",
		    GAL_OA_INT, &unique_output_number);

  GalUtil_OAExtract(argc, argv, oas, "-clean",
		    GAL_OA_INT, &remove_output_files);
  request_max_rows = server_max_rows;


  // setup the output path also
  //
  {
    char *temp_env_path = getenv(OUTPUT_ENV_PATH);
    if (!temp_env_path) {
      printf("ERROR: $" OUTPUT_ENV_PATH " undefined. Please setup environment\n");
      exit(1);
    }
    output_path = new char[strlen(temp_env_path)+5];
    if (!output_path) {
      printf("ERROR: Unable to allocate space for char* output_path\n");
      exit(1);
    }    
    // Put the final '/' on it if it doesn't exist
    //
    sprintf(output_path,"%s%s",
	    temp_env_path,
	    (temp_env_path[strlen(temp_env_path)-1] == '/')?"":"/");
  }
  
  // the perl script must be a valid file so evaluate it and check to make sure
  // the file exists
  //
  {
    if (perlscript[0] == '$') {
      char *temp_env_path = getenv(PERL_PATH1);
      if (!temp_env_path) {
	printf("ERROR: $" PERL_PATH1 " undefined. Please setup environment\n");
	exit(1);
      }
      perlscript = new char[strlen(temp_env_path) + 5 + // 5 is for buffer room
			    strlen(PERL_PATH2) + strlen(PERL_SCRIPT)];
      if (!perlscript) {
	printf("ERROR: Unable to allocate space for char* perlscript\n");
	exit(1);
      }    
      // Put the final '/' on it if it doesn't exist
      //
      sprintf(perlscript,"%s%s%s%s",
	      temp_env_path,
	      (temp_env_path[strlen(temp_env_path)-1] == '/')?"":"/",
	      PERL_PATH2,
	      PERL_SCRIPT
	      );
    }

    struct stat stbuf;
    if (stat(perlscript, &stbuf) != 0) {
      printf("ERROR: Cannot find file [%s] to execute as the perl script\n",
	     perlscript);
      exit(1);
    }
  }

  // setup the "cleanup" string
  //
  sprintf(clean_up_string,"rm -rf ");
  sprintf(clean_up_string,"%s%s*.done ", clean_up_string, output_path);
  sprintf(clean_up_string,"%s%sflatmap_* ", clean_up_string, output_path);
  sprintf(clean_up_string,"%s%sflat_*_val* ", clean_up_string, output_path);
  sprintf(clean_up_string,"%s%soutpage_* ", clean_up_string, output_path);

  //debug
  printf("user[%s] pass[%s] db[%s] perl[%s] out[%s]\n",
	 db_username, db_password, db_database, perlscript, output_path);
  //end debug

  tlist  = (Gal_Object*)malloc(server_max_rows*sizeof(Gal_Object));
  if (tlist == NULL) {
     fflush(stdout);
     printf("%s:  malloc() failed for max_server_rows "
	     "* sizeof(Gal_Object).\nDecrease and try again.\n", argv[0]);
     fprintf(stderr, "%s:  malloc() failed for max_server_rows "
	     "* sizeof(Gal_Object).\nDecrease and try again.\n", argv[0]);
     exit(1);
  }

  printf("db_started. attempting to connect to DB\n");
  fflush(stdout);

  //init_sql();

  fflush(stdout);
  
  return (void *) NULL;
}
