/*--------
 * Module:			tuplelist.c
 *
 * Description:		This module contains functions for creating a manual
 *					result set (the TupleList) and retrieving data from
 *					it for a specific row/column.
 *
 * Classes:			TupleListClass (Functions prefix: "TL_")
 *
 * API functions:	none
 *
 * Comments:		See "notice.txt" for copyright and license information.
 *--------
 */

#include "tuplelist.h"

#include <stdlib.h>
#include "tuple.h"


TupleListClass *
TL_Constructor(UInt4 fieldcnt)
{
	TupleListClass *rv;

	mylog("in TL_Constructor\n");

	rv = (TupleListClass *) malloc(sizeof(TupleListClass));
	if (rv)
	{
		rv->num_fields = fieldcnt;
		rv->num_tuples = 0;
		rv->list_start = NULL;
		rv->list_end = NULL;
		rv->lastref = NULL;
		rv->last_indexed = -1;
	}

	mylog("exit TL_Constructor\n");

	return rv;
}


void
TL_Destructor(TupleListClass *self)
{
	int			lf;
	TupleNode  *node,
			   *tp;

	mylog("TupleList: in DESTRUCTOR\n");

	node = self->list_start;
	while (node != NULL)
	{
		for (lf = 0; lf < self->num_fields; lf++)
			if (node->tuple[lf].value != NULL)
				free(node->tuple[lf].value);
		tp = node->next;
		free(node);
		node = tp;
	}

	free(self);

	mylog("TupleList: exit DESTRUCTOR\n");
}


void *
TL_get_fieldval(TupleListClass *self, Int4 tupleno, Int2 fieldno)
{
	Int4		lf;
	Int4		delta,
				from_end;
	char		end_is_closer,
				start_is_closer;
	TupleNode  *rv;

	if (self->last_indexed == -1)
		/* we have an empty tuple list */
		return NULL;

	/* some more sanity checks */
	if ((tupleno >= self->num_tuples) || (tupleno < 0))
		/* illegal tuple number range */
		return NULL;

	if ((fieldno >= self->num_fields) || (fieldno < 0))
		/* illegel field number range */
		return NULL;

	/*
	 * check if we are accessing the same tuple that was used in the last
	 * fetch (e.g: for fetching all the fields one after another. Do this
	 * to speed things up
	 */
	if (tupleno == self->last_indexed)
		return self->lastref->tuple[fieldno].value;

	/* now for the tricky part... */

	/*
	 * Since random access is quite inefficient for linked lists we use
	 * the lastref pointer that points to the last element referenced by a
	 * get_fieldval() call in conjunction with the its index number that
	 * is stored in last_indexed. (So we use some locality of reference
	 * principle to speed things up)
	 */

	delta = tupleno - self->last_indexed;
	/* if delta is positive, we have to go forward */

	/*
	 * now check if we are closer to the start or the end of the list than
	 * to our last_indexed pointer
	 */
	from_end = (self->num_tuples - 1) - tupleno;

	start_is_closer = labs(delta) > tupleno;

	/*
	 * true if we are closer to the start of the list than to the
	 * last_indexed pointer
	 */

	end_is_closer = labs(delta) > from_end;
	/* true if we are closer at the end of the list */

	if (end_is_closer)
	{
		/* scanning from the end is the shortest way. so we do that... */
		rv = self->list_end;
		for (lf = 0; lf < from_end; lf++)
			rv = rv->prev;
	}
	else if (start_is_closer)
	{
		/*
		 * the shortest way is to start the search from the head of the
		 * list
		 */
		rv = self->list_start;
		for (lf = 0; lf < tupleno; lf++)
			rv = rv->next;
	}
	else
	{
		/* the closest way is starting from our lastref - pointer */
		rv = self->lastref;

		/*
		 * at first determine whether we have to search forward or
		 * backwards
		 */
		if (delta < 0)
		{
			/* we have to search backwards */
			for (lf = 0; lf < (-1) * delta; lf++)
				rv = rv->prev;
		}
		else
		{
			/* ok, we have to search forward... */
			for (lf = 0; lf < delta; lf++)
				rv = rv->next;
		}
	}

	/*
	 * now we have got our return pointer, so update the lastref and the
	 * last_indexed values
	 */
	self->lastref = rv;
	self->last_indexed = tupleno;

	return rv->tuple[fieldno].value;
}


char
TL_add_tuple(TupleListClass *self, TupleNode *new_field)
{
	/*
	 * we append the tuple at the end of the doubly linked list of the
	 * tuples we have already read in
	 */

	new_field->prev = NULL;
	new_field->next = NULL;

	if (self->list_start == NULL)
	{
		/* the list is empty, we have to add the first tuple */
		self->list_start = new_field;
		self->list_end = new_field;
		self->lastref = new_field;
		self->last_indexed = 0;
	}
	else
	{
		/*
		 * there is already an element in the list, so add the new one at
		 * the end of the list
		 */
		self->list_end->next = new_field;
		new_field->prev = self->list_end;
		self->list_end = new_field;
	}
	self->num_tuples++;

	/* this method of building a list cannot fail, so we return 1 */
	return 1;
}
