/*
 * Meta-API for communications with a UIUC CCO nameserver (qi).
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "qick.h"

static char rcsid[] = "$Id: query.c,v 2.2 2000/08/18 19:27:34 dgc Exp $";

/*
 * Various query routines.  qi_vquery() is the guts of them all.
 * qi_*_query()  routines query a specific server.
 * qi_*_lookup() routines query each server in succession until a match
 *	       is made.
 */
qir_t *
qi_vquery(qi_t *qi, char *query, va_list vp)
{
	qir_t	*qir = NULL, *saved_qir = NULL, *top_qir;
	qif_t	*qif = NULL, *saved_qif = NULL;
	qim_t	*qim = NULL, *saved_qim = NULL;
	int	 saved_subcode = 0;

	qi_vwrite(qi, query, vp);

	while ((qim = qi_read(qi, saved_qim = qim)) != NULL) {
		if (qim->qim_code == LR_OK) {
			/* End of replies. */
			break;
		} else if (qim->qim_code == -LR_OK) {
			/* Positive record match, one field per line. */

			/* Is this a continued field? */
			if (strcmp(qim->qim_field, "") == 0 && qif != NULL) {
				/* Continued line in field value. */
				qif->qif_value = realloc(qif->qif_value,
						strlen(qif->qif_value) +
						strlen(qim->qim_value) + 2);
				strcat(qif->qif_value, "\n");
				strcat(qif->qif_value, qim->qim_value);
				continue;
			}

			/* New field (possibly new record, too). */
			qif = qif_new();
			qif->qif_field = qim->qim_field;
			qif->qif_value = qim->qim_value;

			/* Is this a new record? */
			if (qim->qim_subcode != saved_subcode) {
				/* New: create new record and link to prev. */
				qir = qir_new();
				if (saved_qir != NULL) {
					saved_qir->qir_next = qir;
					qir->qir_index = saved_qir->qir_index + 1;
				} else {
					qir->qir_index = 1;
					top_qir = qir;
				}
				qir->qir_message = qif;
				saved_qir = qir;
				saved_subcode = qim->qim_subcode;
			} else {
				/* Not new: link to previous fields. */
				if (saved_qif != NULL) {
					saved_qif->qif_next = qif;
				}
			}

			/* Save field for future linkages. */
			saved_qif = qif;

		} else if (qim->qim_code > 0) {
			qi_error(qi, qi_get_reply_code(qim->qim_code));
			qir_free(qir);
			qim_free(qim);		    
			return NULL;     
		}
	}

	qim_free(saved_qim);
	return top_qir;
}

/*
 * Submit an arbitrary query (single server).
 */
qir_t *
qi_query(qi_t *qi, char *query, ...)
{
	va_list	 vp;
	qir_t	*qir;

	va_start(vp, query);
	qir = qi_vquery(qi, query, vp);
	va_end(vp);
	return qir;
}

/*
 * Submit an arbitrary query (all servers).
 */
qir_t *
qi_lookup(qick_t *qick, char *query, ...)
{
	int	 i;
	va_list	 vp;
	qir_t	*qir;
	list_t	*list;

	LIST_FOREACH(list, qick->q_servers) {
		if (LIST_DATUM(list) == NULL)
			continue;
		va_start(vp, query);
		qir = qi_vquery((qi_t *)LIST_DATUM(list), query, vp);
		va_end(vp);
		if (qir != NULL)
			return qir;
	}
	return NULL;
}

/*
 * Submit a field-specific query (single server).
 */
qir_t *
qi_query_field(qi_t *qi, char *field, char *value)
{
	return qi_query(qi, "query %s=\"%s\" return all", field, value);
}

/*
 * Submit a field-specific query (all servers).
 */
qir_t *
qi_lookup_field(qick_t *qick, char *field, char *value)
{
	return qi_lookup(qick, "query %s=\"%s\" return all", field, value);
}

/*
 * Submit a field-specific query, and return another field's *value*
 * (single server).
 */
char *
qi_query_simple(qi_t *qi, char *field, char *value, char *get)
{
	qir_t	*qir;
	char	*s;

	qir = qi_query(qi, "query %s=\"%s\" return %s", field, value, get);
	if (qir == NULL)
		return NULL;
	if (qir->qir_message == NULL)
		return NULL;
	if (qir->qir_message->qif_field == NULL)
		return NULL;
	if (strcasecmp(qir->qir_message->qif_field, get) != 0)
		return NULL;
	s = strdup(qir->qir_message->qif_value);
	qir_free(qir);
	return s;
}

/*
 * Submit a field-specific query, and return another field's *value*
 * (all servers).
 */
char *
qi_lookup_simple(qick_t *qick, char *field, char *value, char *get)
{
	qir_t	*qir;
	char	*s;

	qir = qi_lookup(qick, "query %s=\"%s\" return %s", field, value, get);
	if (qir == NULL)
		return NULL;
	if (qir->qir_message == NULL)
		return NULL;
	if (qir->qir_message->qif_field == NULL)
		return NULL;
	if (strcasecmp(qir->qir_message->qif_field, get) != 0)
		return NULL;
	s = strdup(qir->qir_message->qif_value);
	qir_free(qir);
	return s;
}


