// file: $isip/util/demo/isip_comm_server_dialog_manager/dbq.cc
// version: $Id: dbq.cc 10113 2005-05-20 20:44:46Z elliott $
//

// Routines to build DB queries 

#include "isip_comm_dialog_navigation_mgr.h"

// Static variables
// Used by the build functions to determine if it needs an "and"
// between the query pieces
//
static int	static_dbq_first = 1;

// Info:
// The only function that accessible by outside files is
//
// void build_dbq(char *dbp, aFrame *frame, void *server_data)

// Internal functions
char* build_constraints(char* p, aFrame* frame);
int build_web_query(aFrame *frame);
char *build_str(char *str, char *val, char *sql, aFrame *frame);

// templates for web query strings

// Begin commenting out airplane

/*
  char	*web_air[] = {
  "[Depart_Loc].[City_Name]",
  "[Depart_Loc].[Airport_Code]",
  "[Depart_Loc].[State]",
  "*",
  "[Arrive_Loc].[City_Name]",
  "[Arrive_Loc].[Airport_Code]",
  "[Arrive_Loc].[State]",
  "*",
  "[Depart_Date].[Date].[Month]",
  "[Depart_Date].[Date].[Day]",
  "*",
  "[Travel_Time].[Depart_Time].[mid_range]",
  "[Travel_Time].[Arrive_Time].[mid_range]",
  "*",
  "*",
  "[Airline].[Airline_Code]",
  "*",
  "*",
  "*",
  "*",
  "[Depart_Loc].[Airport_Name]",
  "[Arrive_Loc].[Airport_Name]",
  (char *)0
  };
  // End commenting out airplane
  
  char*   web_hotel[] ={
  "[City_Name]",
  "*",
  "*",
  "*",
  "[Date].[Month]",
  "[Date].[Day]",
  "*",
  "[Date].[Month]",
  "[Date].[Day]",
  "*",
  "*",
  "*",
  "*",
  "[Hotel_Spec].[Hotel_Name]",
  "[Hotel_Spec].[Hotel_Loc]",
  "*",
  "*",
  "*",
  (char *)0
  };
  
  //char	*web_car[] ={
  "[City_Name]",
  "*",
  "*",
  "*",
  "[Date].[Month]",
  "[Date].[Day]",
  "*",
  "*",
  "[Date].[Month]",
  "[Date].[Day]",
  "*",
  "*",
  "*",
  "[Car_Class]",
  "[Rental_Company]",
  "*",
  "*",
  (char *)0
  };
*/


char	*web_query_direction[] ={
  "[Depart_Loc].[Campus_Name]",
  "[Depart_Loc].[Building_Name]",
  "[Depart_Loc].[Campus_Service_Name]",
  "[Depart_Loc].[Place_Name]",
  "[Depart_Loc].[Address]",
  "[Depart_Loc].[Place_Type]",
  "[Depart_Loc].[City_Name]",
  "[Depart_Loc].[State]",
  "[Depart_Loc].[Zip]",
  "[Depart_Near].[Place_Name]", 
  "[Depart_Near].[Address]", 
  "[Arrive_Loc].[Campus_Name]",
  "[Arrive_Loc].[Building_Name]",
  "[Arrive_Loc].[Campus_Service_Name]",
  "[Arrive_Loc].[Place_Name]",
  "[Arrive_Loc].[Address]",
  "[Arrive_Loc].[Place_Type]",
  "[Arrive_Loc].[City_Name]",
  "[Arrive_Loc].[State]",
  "[Arrive_Loc].[Zip]",
  "[Arrive_Near].[Place_Name]",
  "[Arrive_Near].[Address]",
  (char *)0
};

char	*web_query_distance[] ={
  "[Depart_Loc].[Campus_Name]",
  "[Depart_Loc].[Building_Name]",
  "[Depart_Loc].[Campus_Service_Name]",
  "[Depart_Loc].[Place_Name]",
  "[Depart_Loc].[Address]",
  "[Depart_Loc].[Place_Type]",
  "[Depart_Loc].[City_Name]",
  "[Depart_Loc].[State]",
  "[Depart_Loc].[Zip]",
  "[Depart_Near].[Place_Name]", 
  "[Depart_Near].[Address]",
  "[Arrive_Loc].[Campus_Name]",
  "[Arrive_Loc].[Building_Name]",
  "[Arrive_Loc].[Campus_Service_Name]",
  "[Arrive_Loc].[Place_Name]",
  "[Arrive_Loc].[Address]",
  "[Arrive_Loc].[Place_Type]",
  "[Arrive_Loc].[City_Name]",
  "[Arrive_Loc].[State]",
  "[Arrive_Loc].[Zip]",
  "[Arrive_Near].[Place_Name]",
  "[Arrive_Near].[Address]",
  (char *)0
};

// The bus and the intersection do not have perl scripts
// the the web layout is not needed
char	*web_query_bus[] ={
  (char *)0
};

char	*web_query_intersect[] ={
  (char *)0
};
//not working end

char	*web_query_address[] ={
  "*",
  "*",
  "[Arrive_Loc].[Place_Name]",
  "[Arrive_Loc].[Place_Spec]",
  "[Arrive_Loc].[Place_Type]",
  "[Arrive_Loc].[City_Name]",
  "[Arrive_Loc].[State]",
  "[Arrive_Loc].[Zip]",
  "[Arrive_Near].[Place_Name]",
  "[Arrive_Near].[Address]",
  "[Arrive_Loc].[Campus_Name]",
  "[Arrive_Loc].[Building_Name]",
  "[Arrive_Loc].[Campus_Service_Name]",
  (char *)0
};

char	*web_query_list[] ={
  "*",
  "*",
  "[Arrive_Loc].[Place_Name]",
  "[Arrive_Loc].[Place_Spec]",
  "[Arrive_Loc].[Place_Type]",
  "[Arrive_Loc].[City_Name]",
  "[Arrive_Loc].[State]",
  "[Arrive_Loc].[Zip]",
  "[Arrive_Near].[Place_Name]",
  "[Arrive_Near].[Address]",
  (char *)0
};

// This function takes in a buffer "dbq" that is N-elements big
// it just does sprintf()'s and strcat()'s until the function exits
// and performs no buffer-overflow checking. This function should probably
// be changed someday.
//
// returns 0 if did send the query to the database
// returns 1 if it did not send the query
//
// before changing return parameters, check action_switch
int build_dbq(char *dbp, aFrame *frame, void *server_data) {
  char *p = NULL;
  char *val = NULL;
  int web_access = 1;
  int ret;
  
  // build sql query
  p = dbp;
  static_dbq_first = 1;
  
  //////
  // First set the "SELECT <> FROM <> WHERE" line
  //////
  if ( !scmp("Query_List", frame->name) ) {
    task = "Query_List";
    // set fields and tables
    sprintf(p, "select DISTINCT address, name, distance from address "
	    "where ");
    p += strlen(p);
  }
  else if ( !scmp("Query_Address", frame->name) ) {
    task = "Query_Address";
    // set fields and tables
    sprintf(p, "select DISTINCT address, name, distance from address "
	    "where ");
    p += strlen(p);
  }
  else if ( !scmp("Query_Direction", frame->name) ) {
    task = "Query_Direction";
    // set fields and tables
    if (campus_depart == 1 && campus_arrive == 0) {
      sprintf(p, "select DISTINCT directions, route.distance, totaldistance "
	      "from route, place p1, place p2, campusname c1 where "
	      "p1.id=sid and p2.id = did and ");
      p += strlen(p);
    }
    else if (campus_depart == 0 && campus_arrive == 1) {
      sprintf(p, "select DISTINCT directions, route.distance, totaldistance "
	      "from route, place p1, place p2, campusname c2 where "
	      "p1.id=sid and p2.id = did and ");
      p += strlen(p);
    }
    else if (campus_depart == 1 && campus_arrive == 1) {
      sprintf(p, "select DISTINCT directions, route.distance, totaldistance "
	      "from route, place p1, place p2, campusname c1, campusname c2 "
	      "where p1.id=sid and p2.id = did and ");
      p += strlen(p);
    }
    else if (campus_depart == 0 && campus_arrive == 0) {
      sprintf(p, "select DISTINCT directions, route.distance, totaldistance "
	      "from route, place p1, place p2 where p1.id=sid and p2.id = "
	      "did and ");
      p += strlen(p);
    }
    else {
      printf("Unreachable code reached in dbq.cc! #671\n");
      fflush(stdout);
    }
  } //end of Query_List
  
  else if (!scmp("Query_Distance", frame->name)) {
    
    task = "Query_Distance";
    
    // set fields and tables
    if (campus_depart == 1 && campus_arrive == 0) {
      sprintf(p, "select DISTINCT directions, route.distance, totaldistance "
	      "from route, place p1, place p2, campusname c1 where "
	      "p1.id=sid and p2.id = did and ");
      p += strlen(p);
    }
    else if (campus_depart == 0 && campus_arrive == 1) {
      sprintf(p, "select DISTINCT directions, route.distance, totaldistance "
	      "from route, place p1, place p2, campusname c2 where "
	      "p1.id=sid and p2.id = did and ");
      p += strlen(p);
    }
    else if (campus_depart == 1 && campus_arrive == 1) {
      sprintf(p, "select DISTINCT directions, route.distance, totaldistance "
	      "from route, place p1, place p2, campusname c1, campusname c2 "
	      "where p1.id=sid and p2.id = did and ");
      p += strlen(p);
    }
    else {
      sprintf(p, "select DISTINCT directions, route.distance, totaldistance "
	      "from route, place p1, place p2 where p1.id=sid and p2.id = "
	      "did and ");
      p += strlen(p);
    }
  } // End of Query_Distance
  
    //not working begin
    /*
      else if ( !scmp("Query_Bus", frame->name) ) {
      sprintf(p,"select **information about bus** from **bus_table** where ");
      p += strlen(p);
      }
      
      else if ( !scmp("Query_Intersect", frame->name) ) {
      sprintf(p,"select **information about roads** from **intersect_table** "
      "where ");
      p += strlen(p);      
      }
    */
    //not working end
  
  else if ( !scmp("Query_Building", frame->name) ) {
    task = "Query_Building";
    // set fields and tables 
    sprintf(p, "select DISTINCT building, name from campusname where ");
    p += strlen(p);
  }
  
  else {
    sprintf(global_debug_msg, "Unknown type of frame->name given to "
	    "build_dbq! Name is [%s]\n",
	    frame->name);
        
    return 1;
  }
  
  ///////////
  // End of select\from\where phrase
  ///////////
  
  // build where clause of sql query
  p = build_constraints(p, frame);
  
  ////////
  // if order by distance
  // then put the "order by" clause on the query
  ////////
  
  if ( !scmp("Query_Direction", frame->name) ) {
    strcat(p, " order by route.routeindex ");
  }
  else if ( !scmp("Query_Distance", frame->name) ) {
    strcat(p, " order by route.routeindex");
  }
  else if( !scmp("Query_Address", frame->name) ) {
    strcat(p, " order by distance");
    
    // are we allowed to access the web ? should always be true
    //
    val = getVal(frame, "[question_type]");
    if (val && !strcmp(val, "query list")) {
      web_access = 2;
    }
  }
  else if( !scmp("Query_List", frame->name) ) {
    strcat(p, " order by distance");
    
    // are we allowed to access the web ? should always be true
    //
    val = getVal(frame, "[question_type]");
    if (val && !scmp(val, "query list") ) {
      web_access = 2;
    }
  }
  
  //not working begin
  /*
    else if ( !scmp("Query_Bus", frame->name) ) {
    strcat(p, " *test*order by busnumber");
    }
    
    else if ( !scmp("Query_Intersect", frame->name) ) {
    strcat(p, " *test*order by route.routeindex ");
    }
  */
  //not working end
  
  // build web query for frame
  //
  ret = build_web_query(frame); //0 is successful
  if (ret != 0) {
    printf("Error in build_dbq(): build_web_query() returned unsuccessful "
	   "code of [%i]\n", ret);
    return 1;
  }
  
  // build frame and send to db server
  //
  if (PRINT_SQL) {
    printf("Info DM sql query========:\n%s\n====================\n", db_buf);
  }
  // Since this function is a galaxy related function, the implementation of it
  // goes back to isip_comm_dialog_navigation_mgr.cc
  //
  return query_database(db_buf, frame, server_data, web_access);
}

// add sql key constraint for slot 
char *build_str(char *str, char *val, char *sql, aFrame *frame) {
  char *p;
  char *f;
  char *s;
  char name[LINE_LEN];
  char value[LINE_LEN];

  // Error checking
  if( !sql || !val || !frame || !str) return( (char *) 0);
  
  // check for ' in value string 
  for(p = value, f = val; *f; p++, f++) {
    if( *f == '\'' ) {
      *p++ = *f;
    }
    *p = *f;
  }
  *p = 0;
  
  p = str;
  
  // Looks at static global 'static_dbq_first' to determine if this is
  // the first part of the sql query?
  // If first is not set to 1 then put an 'and'
  //
  if (static_dbq_first) {
    static_dbq_first = 0;
  }
  else {
    sprintf(p, " and ");
    p += strlen(p);
  }
  
  // copy template string until substitute char 
  for( f = sql; *f; f++) {
    switch(*f) {
    case '$':
      if( *(f+1) == '[' ) {
	if( sscanf(f+1, "%s", name) < 1 )
	  break;
	if( !(s = getVal(frame, name)) )
	  break;
	sprintf(p, "%s", s);
	p += strlen(p);
	f += strlen(name);
	break;
      }
      else if( *(f+1) == '(' ) {
	if( !(s = index(f+1, (int) '[')) )
	  break;
	if( sscanf(s, "%s", name) < 1 )
	  break;
	for(s = name+strlen(name)-1; *s && (*s != ']'); s--)
	  *s = (char)0;
	if( !(s = getVal(frame, name)) ) {
	  if( (s = index(f+1, (int) ')')) )
	    f = s;
	  break;
	}
	strcpy(name,s);
	for( f = f+2; *f && (*f != '['); f++)
	  *p++ = *f;
	if( !*f )
	  break;
	sprintf(p, name);
	p += strlen(p);
	
	// This is for going to the first ' or end of line
	//
	for( ; *f && (*f != '\''); f++);
	
	for( ; *f && (*f != ')'); f++) *p++ = *f;
	break;
      }
      
      break;
    case '!':
      if( *val == '*' )
	break;
      strcpy(p, value);
      p += strlen(p);
      break;
    default:
      *p++ = *f;
    }// end switch
  }//end for
  
  *p = 0;
  return(p);
}


// add sql key constraint for each slot 
char *build_constraints(char *p, aFrame *frame) {
  aFrameDef	*fd;
  int		i;
  
  // find frame definition 
  if( !(fd = getFrameDef(frame->name)) )
    return( (char *)0 );
  
  // for each slot in frame
  for( i = 0; i <  fd->n_slot; i++) {
    // if value doesnt exist
    if( !*(frame->value+i) )                continue;

    // if not care
    if( !scmp("*", *(frame->value+i)) )     continue;

    // if this slot doesnt have sql
    if( !((fd->slot+i)->sql) )              continue;
    
    // see if constraint should be ommitted
    /*
      // Our code doesn't have anything to do with times
      if( backoff_cnt > 0 ) {
      if( !((fd->slot+i)->key) ) continue;
      if( !(scmp("[Travel_Time].[Depart_Time]",(fd->slot+i)->key)))
	continue;
      if( !(scmp("[Travel_Time].[Arrive_Time]",(fd->slot+i)->key)))
	continue;
    }
    */
    
    // add constraint for slot 
    p = build_str(p, *(frame->value+i), (fd->slot+i)->sql, frame);
  }
  
  return(p);
}


// build web query for frame
// returns 0 it no errors occured
// returns 1 if errors happened
//
// before changing the return parameters, check on the usage of this function
// inside the build_dbq() function above
//
int build_web_query(aFrame *frame) {

  // input checking
  //
  if (!frame) {
    printf("Error build_web_query(): parameter input 'frame' is null!\n");
    return 1;
  }
    
  char **wt;
  char *val;
  char web_query[10000];
  char merged_slot[10000];

  // Clear out all of the memory
  //
  memset(web_query, 0, sizeof(web_query));
  memset(merged_slot, 0, sizeof(merged_slot));
  
  // find web template for frame
  //
  if( !scmp( "Query_Address", frame->name ) )        wt = web_query_address;
  else if( !scmp( "Query_List", frame->name ) )      wt = web_query_list;
  else if( !scmp( "Query_Direction", frame->name ) ) wt = web_query_direction;
  else if( !scmp( "Query_Distance", frame->name ) )  wt = web_query_distance;
  else if( !scmp( "Query_Building", frame->name ) )  wt = web_query_address;
  //else if (!scmp( "Query_Intersect", frame->name )) wt = web_query_intersect;
  //else if( !scmp( "Query_Bus", frame->name ) )       wt = web_query_bus;
  else {
    printf("Error build_web_query(): web template for frame [%s] not found\n",
	   frame->name);
    fflush(stdout);
    return 1;
  }
  
  // Shouldn't ever happen but it is a quick check to verify we won't end up
  // with a null pointer
  if( !wt || !*wt ) {
    printf("Error build_web_query(): web template pointer is null. This "
	   "should never happen\n");
    return 1;
  }

  // For a quick explanation of what this is. The template above has all the
  // fields in it that the perl scripts require. The following attempts to
  // look inside the frame for data named whatever the template has written
  // there. If the data cannot be found, then a "*" is written. The final
  // string is the "parameters" to the perl script.
  
  // for each field in web template 
  for( ; *wt; wt++) {
    
    if( !(val = getVal(frame, *wt)) ) {
      strcat(web_query, ":*");
    }
    else {
      // This section use to check for a ' in the string. This has been
      // moved to the perl script itself (replace ' with \' for sql)
      // note: the 'f' below is the 'val' variable
      //      for(p = value; *f; p++, f++) {
      //	*p = *f;
      //      }
      //      *p = 0;
      
      strcat(web_query, ":");
      strcat(web_query, val);
      
      strcat(merged_slot, *wt);
      strcat(merged_slot, ":");

      strcat(merged_slot, val);
      strcat(merged_slot, ";");

      // The final result web_query looks like
      // :*:*:*:*:UNIVERSITY DRIVE:*:STARKVILLE:*:*:*:*:*:*: (cont)
      // *:WALMART:*:*:*:*:*:*:*"
      //
      // and the final result merged_slot looks like
      // [Depart_Loc].[Address]:UNIVERSITY DRIVE;[Depart_Loc].[City_Name]:
      // STARKVILLE;[Arrive_Loc].[Place_Name]:WALMART;"
      
      //printf("The web query couple:\n%s: %s\n\n", *wt, value);
      
    } //end else
  } //end wt[] for loop

  printf("exiting build_web_query()\n");
  printf("web_query------\n%s\nend web------\n", web_query);
  printf("merged_slot------\n%s\nend merge----------\n", merged_slot);
  fflush(stdout);
  
  // Now set the web query slots of these frames
  setKey(frame, "[web_query]", web_query);
  setKey(frame, "[merged_slot]", merged_slot);

  return 0;
}
