/* WIDE AREA INFORMATION SERVER SOFTWARE:
   No guarantees or restrictions.  See the readme file for the full standard
   disclaimer.

   This is part of the X user-interface for the WAIS software.  Do with it
   as you please.

   Version 0.82
   Wed Apr 24 1991

   jonathan@Think.COM

*/

#define _C_QUESTION

#include "xwais.h"

void showDiags(d, w)
diagnosticRecord **d;
Widget w;
{
  int i;
  char msg[256];

  for (i = 0; d[i] != NULL; i++) {
    if (d[i]->ADDINFO != NULL) {
      sprintf(msg, "\nCode: %s, %s", d[i]->DIAG, d[i] ->ADDINFO);
      PrintStatus(msg, w);
      sleep(2);
    }
  }
}

void
write_text_record_completely(fp, record, quote_string_quotes)
FILE *fp;
WAISDocumentText *record;
boolean quote_string_quotes;
{
  long count;
  /* fprintf(fp," Text\n");
  print_any("     DocumentID:  ", record->DocumentID);
  fprintf(fp,"     VersionNumber:  %d\n", record->VersionNumber);
  */
  for(count = 0; count < record->DocumentText->size; count++){
    int ch = record->DocumentText->bytes[count];
    if(27 == ch){
      /* then we have an escape code */
      /* if the next letter is '(' or ')', then ignore two letters */
      if('(' == record->DocumentText->bytes[count + 1] ||
      ')' == record->DocumentText->bytes[count + 1])
	count += 1;             /* it is a term marker */
      else count += 4;         /* it is a paragraph marker */
    }
    else if (isprint(ch)){
      if(quote_string_quotes && ch == '"')
	putc('\\', fp);
      putc(ch, fp);
    } 
    else if (ch == '\n' || ch == '\r')
      fprintf(fp, "\n");
  }
}


/* for making searches */

DocList
build_response_list(response, source)
SearchResponseAPDU *response;
SourceID source;
{
  long continue_viewing;
  long i, k;
  WAISSearchResponse  *info;
  DocList last = NULL, doc, result = NULL;
  DocumentID docID;

  k = response->NumberOfRecordsReturned;

  if ( response->DatabaseDiagnosticRecords != 0 ) {
    info = (WAISSearchResponse*)response->DatabaseDiagnosticRecords;
    if ( info->DocHeaders != NULL ) {
      for(i = 0; i < k; i++) {
	if(result == NULL) {
	  doc = result = makeDocList(NULL, NULL);
	}
	else
	  doc = makeDocList(NULL, NULL);

	if(info->DocHeaders[i] != NULL ) {
	  docID = fillDocumentID(info, source, i);
	  doc->thisDoc = docID;
	}
	if(last != NULL) {
	  last->nextDoc = doc;
	}
	last = doc;
      }
    }
  }
  return result;
}

/* right now this hacks out the ^Q/S too.  I'll do better later. */

void replacecontrolM(buffer, length)
char *buffer;
long *length;
{
  char *here, *there, c;
  long i, newlength;

  here = there = buffer;
  for(newlength = 0, i = 0; i < *length; i++) {
    c = *here;
    switch (c) {
    case 0:
      *there = 0;
      *length = newlength;
      return;
    case '\r':
      *there = '\n';
      newlength++;
      here++; there++;
      break;
    case 19:
    case 17:
      here++;
      break;
    default:
      *there = *here;
      newlength++;
      here++; there++;
    }
  }
  *length = newlength;
}

#define MAXDOCS 40

void SearchWais(q)
Question q;
{
  Source source;
  static long request_buffer_length;
  static int document_number;
  DocObj **Doc;
  int i;
  float top, shown;
  char *request_message, *response_message;
  char message[255];
  DocList last;
  diagnosticRecord **diag;
  char *database;
  long numdocs;

  request_message = (char*)q->request_message;
  response_message = (char*)q->response_message;

  /* clear the results */

  q->ResultDocuments = NULL;

  /* build DocObjs */

  Doc = (DocObj**) s_malloc((q->numdocs+1) * sizeof(char*));

  q->numsources = listlength(q->Sources);

  if (q->numsources != 0)
    numdocs = MAXDOCS/q->numsources;
  else numdocs = 0;
  {
    DocList dl;
    for(i=0, dl = q->RelevantDocuments;
	dl != NULL;
	dl = dl->nextDoc, i++)
      if(dl->thisDoc->doc != NULL)
	if(dl->thisDoc->doc->id != NULL)
	  if(dl->thisDoc->doc->id->originalLocalID != NULL)
	    if(dl->thisDoc->start >= 0)
	      Doc[i] =
		makeDocObjUsingLines(anyFromDocID(dl->thisDoc->doc->id),
				     (dl->thisDoc->doc->type) ?
				     dl->thisDoc->doc->type : "TEXT",
				     dl->thisDoc->start, dl->thisDoc->end);
	    else
	      Doc[i] =
		makeDocObjUsingWholeDocument(anyFromDocID(dl->thisDoc->doc->id),
					     (dl->thisDoc->doc->type) ?
					     dl->thisDoc->doc->type : "TEXT");

    Doc[i] = NULL;
  }

  /* check to see if the question has a source */
  source = NULL;

  if(q->Sources != NULL) {
    SourceList slist;

    for(slist = q->Sources;
	slist != NULL;
	slist = slist->nextSource) {

      source = findsource(slist->thisSource->filename);

      if(source == NULL) {
	sprintf(message, "\nCan't find source %s.", slist->thisSource->filename);
	PrintStatus(message, q->window->StatusWindow);
	sleep(2);
      }
      else {
	sprintf(message, "\nSearching %s.", source->name);
	PrintStatus(message, q->window->StatusWindow);
      
	request_buffer_length = MAX_MESSAGE_LEN; /* how much of the request is left */

	if(source->initp != TRUE)
	  init_for_source(source, request_message,
			  request_buffer_length,
			  response_message);

	request_buffer_length = source->buffer_length;

	if(source->database[0] == 0) database = NULL;
	else database = source->database;

	if(source->initp == TRUE) {
	  if(NULL ==
	     generate_search_apdu(request_message + HEADER_LENGTH, 
				  &request_buffer_length, 
				  q->keywords, database, Doc, numdocs)) {
	    PrintStatus("Buffer overflow: request too large", q->window->StatusWindow);
	    sleep(2);
	  }
	  else if(0 ==
		  interpret_message(request_message, 
				    MAX_MESSAGE_LEN - request_buffer_length, 
				    response_message,
				    MAX_MESSAGE_LEN,
				    source->connection, 
				    false /* true verbose */
				    )) {
	    PrintStatus("Warning: no information returned.  Possibly a bad connection"
			, q->window->StatusWindow);
	    sleep(2);
	  }
	  else {

	    readSearchResponseAPDU(&q->query_response,
				   response_message + HEADER_LENGTH);

      
	    if (q->query_response != NULL)
	      if ((WAISSearchResponse *)q->query_response->DatabaseDiagnosticRecords != NULL) 
		if ((diag =
		     ((WAISSearchResponse *)q->query_response->DatabaseDiagnosticRecords)->Diagnostics) != NULL)
		  showDiags(diag, q->window->StatusWindow);
      
	    if (q->ResultDocuments != NULL) {
	      last = findLast(q->ResultDocuments);
	      last->nextDoc = build_response_list(q->query_response,
						  slist->thisSource);
	    }
	    else
	      q->ResultDocuments =
		build_response_list(q->query_response,
				    slist->thisSource);
	    /* ok, now we've got all the documents, let's sort them */
	  }
	}
	else {
	  sprintf(message, "\nError connecting to %s.", slist->thisSource->filename);
	  PrintStatus(message, q->window->StatusWindow);
	  sleep(2);
	}

      }
    }
    sort_document_list(q->ResultDocuments);
    if(q->Result_Items != NULL) freeItemList(q->Result_Items);
    q->Result_Items = buildDocumentItemList(q->ResultDocuments, TRUE);
    q->window->ResultDocuments->offset = 0;
    q->numresdocs =  charlistlength(q->Result_Items);
    sprintf(message, "\nFound %d documents.", q->numresdocs);
    PrintStatus(message, q->window->StatusWindow);
  }
  else {
    PrintStatus("\nThis Question has no sources to search.  Please add one.",
		q->window->StatusWindow);
    q->ResultDocuments = NULL;

    if(q->Result_Items != NULL) freeItemList(q->Result_Items);
    q->Result_Items = buildDocumentItemList(q->ResultDocuments, TRUE);
    q->window->ResultDocuments->offset = 0;
    q->numresdocs =  charlistlength(q->Result_Items);
  }
}

Boolean
 ViewWaisDocument(q, source, doc, t)
Question q;
Source source;
DocumentID doc;
Textbuff t;
{
  static long request_length, chars_per_page;
  static long lines, size, count, chars, numChars;
  any* docany;
  WAISDocumentText *text;
  char *viewtext, message[255];
  int i;
  char *viewbuffer;
  Widget textwindow;
  Arg args[5];
  Cardinal num_args;
  diagnosticRecord **diag;
  char *database;

  if(source->database[0] == 0) database = NULL;
  else database = source->database;

  docany = anyFromDocID(doc->doc->id);

  sprintf(message, "\nGetting document from server...", size);
  PrintStatus(message, q->window->StatusWindow);

  size = 0;

  if(t->type != NULL) s_free(t->type);
  t->type = s_strdup(doc->doc->type);

  lines = doc->doc->numLines;
  chars = doc->doc->numChars;
  numChars = chars+1000; /* for slop? */

  if((t->text = (char*)s_malloc(numChars)) == NULL) {
    PrintStatus("\nUnable to allocate message space.  Something is wrong.",
		q->window->StatusWindow);
    return FALSE;
  }

  viewbuffer = t->text;

  viewtext = viewbuffer;
  memset(viewtext, 0, numChars);

  docany = anyFromDocID(doc->doc->id);

  request_length = MAX_MESSAGE_LEN;

  if(source->initp == FALSE)
    init_for_source(source,
		    q->request_message, 
		    request_length,
		    q->response_message);

  chars_per_page = source->buffer_length - HEADER_LENGTH - 1000; /* ? */

  if (chars <= 0) {
    PrintStatus("\nEmpty Document, no text was returned",
	   q->window->StatusWindow);
    return FALSE;
  }

  for(count = 0; 
      count * chars_per_page < chars;
      count++) {
    /* show as we go... */

    request_length = source->buffer_length;

    if(0 ==
       generate_retrieval_apdu(q->request_message + HEADER_LENGTH,
			       &request_length, 
			       docany,
			       CT_byte,
			       count * chars_per_page,
			       MIN((count + 1) * chars_per_page, chars),
			       (doc->doc->type) ?
			       doc->doc->type : "TEXT",
			       database)) {
      PrintStatus("\nWarning: no information returned.  Possibly a bad connection"
		  , q->window->StatusWindow);
      return FALSE;
      }
	     
    if(0 ==
       interpret_message(q->request_message, 
			 MAX_MESSAGE_LEN - request_length, 
			 q->response_message,
			 MAX_MESSAGE_LEN,
			 source->connection,
			 false	/* true verbose */	
			 )) {
      PrintStatus("\nWarning: no information returned.  Possibly a bad connection"
		  , q->window->StatusWindow);
      return FALSE;
    }

    readSearchResponseAPDU(&q->retrieval_response, 
			   q->response_message + HEADER_LENGTH);

    if (q->retrieval_response != NULL)
      if ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords != NULL) 
	if ((diag =
	     ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Diagnostics) != NULL)
	  showDiags(diag, q->window->StatusWindow);
    

    if(NULL == ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Text) {
      return FALSE;
    }

    text = ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Text[0];
    if (!strcmp(doc->doc->type, "TEXT") &&
	!(strcmp(app_resources.removeSeekerCodes, "On"))) {
      /* get rid of wierd stuff in text buffers only */
      long length;
    
      length = text->DocumentText->size;
      delete_seeker_codes(text->DocumentText->bytes, &length);
      text->DocumentText->size = length;
      replacecontrolM(text->DocumentText->bytes, &length);
      text->DocumentText->size = length;
    }

    size+=text->DocumentText->size;
    if (size <= numChars) {
      memcpy(viewtext, text->DocumentText->bytes, text->DocumentText->size);
      viewtext+=text->DocumentText->size;
      sprintf(message, "\nReceived %d bytes from %s server",
	      size, source->name);
      PrintStatus(message, q->window->StatusWindow);
    }
    else {
      PrintStatus("\nBuffer overflow!", q->window->StatusWindow);
      Feep();
      break;
    }

    
  }

  /* display_search_response(q->retrieval_response); the general thing */
  if(NULL == ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Text){
    PrintStatus("\nNo text was returned",
	   q->window->StatusWindow);
  }
  t->size = chars;

  return TRUE;
}

Boolean init_for_source(source, request, length, response)
Source source;
char *request;
long length;
char *response;
{
  if(source->initp == FALSE) {
    source->buffer_length = MAX_MESSAGE_LEN;
    if(source->server[0] == 0)
      source->connection = NULL;
    else
      if ((source->connection = connect_to_server(source->server,
						  atoi(source->service))) == NULL) {
	PrintStatus("Bad Connection to server.\n", the_Question->window->StatusWindow);
	source->initp = FALSE;
	return source->initp;
      }
    
    source->buffer_length = 
      init_connection(request, response,
		      length,
		      source->connection);

    if (source->buffer_length < 0) {
      PrintStatus("Bad Connection to server.\n", the_Question->window->StatusWindow);
      source->initp = FALSE;
    }
    else {
      SList s;

      source->initp = TRUE;
      /* set the init and connection for other sources with the same
	 host and port. */
      for (s = Sources; s != NULL; s = s->nextSource) {
      	if (s->thisSource != source) {
	  if (strcmp(s->thisSource->server, source->server) == 0 &&
	      strcmp(s->thisSource->service, source->service) == 0) {
	    s->thisSource->connection = source->connection;
	    s->thisSource->buffer_length = source->buffer_length;
	    s->thisSource->initp = TRUE;
	  }
	}
      }
    }
    return source->initp;
  }
}
