/*
 * pgeasy.c
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "libpq-fe.h"
#include "halt.h"
#include "libpgeasy.h"

#ifndef NUL
#define NUL '\0'
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

/* GLOBAL VARIABLES */
static PGconn *conn;
static PGresult *res = NULL;

static int	tuple;		/* stores fetch location */

#define ON_ERROR_STOP	0
#define ON_ERROR_CONTINUE		1

static int	on_error_state = ON_ERROR_STOP;	/* halt on errors? */

static int	in_result_block = FALSE;		
static int	was_get_unset_result = FALSE;

/*
 *
 *	connectdb - returns PGconn structure
 *
 */
PGconn *
connectdb(char *options)
{
	/* make a connection to the database */
	conn = PQconnectdb(options);
	if (PQstatus(conn) == CONNECTION_BAD)
		halt("Connection to database using '%s' failed.\n%s\n", options,
			 PQerrorMessage(conn));
	return conn;
}

/*
 *
 *		disconnectdb
 *
 */
void
disconnectdb()
{
	if (res != NULL &&
		in_result_block == FALSE &&
		was_get_unset_result == FALSE)
	{
		PQclear(res);
		res = NULL;
	}

	PQfinish(conn);
}

/*
 *
 *	doquery - returns PGresult structure
 *
 */
PGresult *
doquery(char *query)
{
	if (res != NULL &&
		in_result_block == FALSE &&
		was_get_unset_result == FALSE)
		PQclear(res);

	was_get_unset_result = FALSE;
	res = PQexec(conn, query);

	if (on_error_state == ON_ERROR_STOP &&
		(res == NULL ||
		 PQresultStatus(res) == PGRES_BAD_RESPONSE ||
		 PQresultStatus(res) == PGRES_NONFATAL_ERROR ||
		 PQresultStatus(res) == PGRES_FATAL_ERROR))
	{
		if (res != NULL)
			fprintf(stderr, "query error:  %s\n", PQcmdStatus(res));
		else
			fprintf(stderr, "connection error:  %s\n", PQerrorMessage(conn));
		PQfinish(conn);
		halt("failed request:  %s\n", query);
	}
	tuple = 0;
	return res;
}

/*
 *
 *	fetch - returns tuple number (starts at 0), or the value END_OF_TUPLES
 *			NULL pointers are skipped
 *
 */
int
fetch(void *param,...)
{
	va_list		ap;
	int			arg,
				num_fields;

	num_fields = PQnfields(res);

	if (tuple >= PQntuples(res))
		return END_OF_TUPLES;

	va_start(ap, param);
	for (arg = 0; arg < num_fields; arg++)
	{
		if (param != NULL)
		{
			if (PQfsize(res, arg) == -1)
			{
				memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
				((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
			}
			else
				memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
		}
		param = va_arg(ap, char *);
	}
	va_end(ap);
	return tuple++;
}

/*
 *
 *		fetchwithnulls - returns tuple number (starts at 0),
 *																						or the value END_OF_TUPLES
 *		Returns TRUE or FALSE into null indicator variables
 *		NULL pointers are skipped
 */
int
fetchwithnulls(void *param,...)
{
	va_list		ap;
	int			arg,
				num_fields;

	num_fields = PQnfields(res);

	if (tuple >= PQntuples(res))
		return END_OF_TUPLES;

	va_start(ap, param);
	for (arg = 0; arg < num_fields; arg++)
	{
		if (param != NULL)
		{
			if (PQfsize(res, arg) == -1)
			{
				memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
				((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
			}
			else
				memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
		}
		param = va_arg(ap, char *);
		if (PQgetisnull(res, tuple, arg) != 0)
			*(int *) param = 1;
		else
			*(int *) param = 0;
		param = va_arg(ap, char *);
	}
	va_end(ap);
	return tuple++;
}

/*
 *
 *	on_error_stop
 *
 */
void
on_error_stop()
{
	on_error_state = ON_ERROR_STOP;
}

/*
 *
 *	on_error_continue
 *
 */
void
on_error_continue()
{
	on_error_state = ON_ERROR_CONTINUE;
}


/*
 *
 *	get_result
 *
 */
PGresult *
get_result()
{
	char	   *cmdstatus = PQcmdStatus(res);

	was_get_unset_result = TRUE;

	/* we have to store the fetch location somewhere */
	/* XXX THIS IS A NO-NO */
	cmdstatus[0] = NUL;
	memcpy(&cmdstatus[1], &tuple, sizeof(tuple));

	return res;
}

/*
 *
 *	set_result
 *
 */
void
set_result(PGresult *newres)
{

	char	   *cmdstatus = PQcmdStatus(res);

	if (newres == NULL)
		halt("set_result called with null result pointer\n");

	if (res != NULL && was_get_unset_result == FALSE)
	{
		if (in_result_block == FALSE)
			PQclear(res);
		else
		{
			/* XXX THIS IS A NO-NO */
			cmdstatus[0] = NUL;
			memcpy(&cmdstatus[1], &tuple, sizeof(tuple));
		}
	}

	in_result_block = TRUE;
	was_get_unset_result = FALSE;

	cmdstatus = PQcmdStatus(newres);
	memcpy(&tuple, &cmdstatus[1], sizeof(tuple));

	res = newres;
}


/*
 *
 *	unset_result
 *
 */
void
unset_result(PGresult *oldres)
{
	char	   *cmdstatus = PQcmdStatus(oldres);

	if (oldres == NULL)
		halt("unset_result called with null result pointer\n");

	if (in_result_block == FALSE)
		halt("Unset of result without being set.\n");

	was_get_unset_result = TRUE;

	/* XXX THIS IS A NO-NO */
	cmdstatus[0] = NUL;
	memcpy(&cmdstatus[1], &tuple, sizeof(tuple));
	in_result_block = FALSE;
}

/*
 *
 *	reset_fetch
 *
 */
void
reset_fetch()
{
	tuple = 0;
}
