/* 
 * tixList.c --
 *
 *	Implements easy-to-use link lists.
 *
 * Copyright (c) 1996, Expert Interface Technologies
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 */
#include <tixPort.h>
#include <tixInt.h>

#define NEXT(info,ptr) (char*)(*(char**)((ptr+(info->nextOffset))))
#define PREV(info,ptr) (char*)(*(char**)((ptr+(info->prevOffset))))

static void 		SetNext _ANSI_ARGS_((Tix_ListInfo * info,
			    char * ptr, char * next));

static void SetNext(info, ptr, next)
    Tix_ListInfo * info;
    char * ptr;
    char * next;
{
    char ** next_ptr = (char**)((ptr+(info->nextOffset)));
    * next_ptr = next;
}

void Tix_LinkListInit(lPtr)
    Tix_LinkList * lPtr;
{
    lPtr->numItems = 0;
    lPtr->head   = (char*)NULL;
    lPtr->tail   = (char*)NULL;
}


void
Tix_LinkListAppend(infoPtr, lPtr, itemPtr, flags)
    Tix_ListInfo * infoPtr;
    Tix_LinkList * lPtr;
    char * itemPtr;
    int flags;
{
    char * ptr;

    if (flags | TIX_UNIQUE) {
	/* Check for uniqueness */
	for (ptr=lPtr->head;
	     ptr!=NULL;
	     ptr=NEXT(infoPtr,ptr)) {
	    if (ptr == itemPtr) {
		return;
	    }
	}
    }
    if (lPtr->head == NULL) {
	lPtr->head = lPtr->tail = itemPtr;
    } else {
	SetNext(infoPtr, lPtr->tail, itemPtr);
	lPtr->tail = itemPtr;
    }

    SetNext(infoPtr, itemPtr, NULL);
    ++ lPtr->numItems;
}

void Tix_LinkListIteratorInit(liPtr)
    Tix_ListIterator * liPtr;
{
    liPtr->started = 0;
}

void Tix_LinkListStart(infoPtr, lPtr, liPtr)
    Tix_ListInfo * infoPtr;
    Tix_LinkList * lPtr;
    Tix_ListIterator * liPtr;
{
    if (lPtr->head == NULL) {
	liPtr->last = NULL;
	liPtr->curr = NULL;
    } else {
	liPtr->last = liPtr->curr = lPtr->head;
    }
    liPtr->deleted = 0;
    liPtr->started = 1;
}

void Tix_LinkListNext(infoPtr, lPtr, liPtr)
    Tix_ListInfo * infoPtr;
    Tix_LinkList * lPtr;
    Tix_ListIterator * liPtr;
{
    if (liPtr->curr == NULL) {
	return;
    }
    if (liPtr->deleted == 1) {
	/* the curr pointer has already been adjusted */
	liPtr->deleted = 0;
	return;
    }

    liPtr->last = liPtr->curr;
    liPtr->curr = NEXT(infoPtr, liPtr->curr);
}

/*
 *----------------------------------------------------------------------
 * Tix_LinkListDelete --
 *
 *	Deletes an element from the linklist. The proper step of deleting
 *	an element is:
 *
 *	for (Tix_SimpleListStart(&list, &li); !Tix_SimpleListDone(&li);
 *	         Tix_SimpleListNext (&list, &li)) {
 *	    MyData * p = (MyData*)li.curr;
 *	    if (someCondition) {
 *		Tix_SimpleListDelete(&cPtr->subWDefs, &li);
 *		ckfree((char*)p);
 *          }
 *	}
 *
 *	i.e., The pointer can be freed only after Tix_SimpleListDelete().
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The pointers in the list are adjusted  and the liPtr is advanced
 *	to the next element.
 *----------------------------------------------------------------------
 */
void
Tix_LinkListDelete(infoPtr, lPtr, liPtr)
    Tix_ListInfo * infoPtr;
    Tix_LinkList * lPtr;
    Tix_ListIterator * liPtr;
{
    if (liPtr->curr == NULL) {
	/* %% probably is a mistake */
	return;
    }
    if (liPtr->deleted == 1) {
	/* %% probably is a mistake */
	return;
    }
    if (lPtr->head == lPtr->tail) {
	lPtr->head  = lPtr->tail = NULL;
	liPtr->curr = NULL;
    }
    else if (lPtr->head == liPtr->curr) {
	lPtr->head  = NEXT(infoPtr, liPtr->curr);
	liPtr->curr = lPtr->head;
	liPtr->last = lPtr->head;
    }
    else if (lPtr->tail == liPtr->curr) {
	lPtr->tail = liPtr->last;
	SetNext(infoPtr, lPtr->tail, NULL);
	liPtr->curr = NULL;
    }
    else {
	SetNext(infoPtr, liPtr->last, NEXT(infoPtr, liPtr->curr));
	liPtr->curr = NEXT(infoPtr, liPtr->last);
    }
    -- lPtr->numItems;

    liPtr->deleted = 1;
}

/*----------------------------------------------------------------------
 *  Tix_LinkListInsert --
 *
 *	Insert the item at the position indicated by liPtr
 *----------------------------------------------------------------------
 */
void Tix_LinkListInsert(infoPtr, lPtr, itemPtr, liPtr)
    Tix_ListInfo * infoPtr;
    Tix_LinkList * lPtr;
    char * itemPtr;
    Tix_ListIterator * liPtr;
{
    if (lPtr->numItems == 0) {
	/* Just do an append
	 */
	Tix_LinkListAppend(infoPtr, lPtr, itemPtr, 0);

	/* Fix the iterator (%% I am not sure if this is necessary)
	 */
	liPtr->curr = liPtr->last = lPtr->head;
	return;
    }

    if (liPtr->curr == NULL) {
	/* %% probably is a mistake */
	return;
    }
    if (lPtr->head == lPtr->tail) {
	lPtr->head = itemPtr;
	SetNext(infoPtr, lPtr->head, lPtr->tail);
	liPtr->last = itemPtr;
	liPtr->curr = itemPtr;
    }
    else if (liPtr->curr == lPtr->head) {
	lPtr->head = itemPtr;
	SetNext(infoPtr, lPtr->head, liPtr->curr);
	liPtr->last = itemPtr;
	liPtr->curr = itemPtr;
    }
    else {
	SetNext(infoPtr, liPtr->last, itemPtr);
	SetNext(infoPtr, itemPtr,     liPtr->curr);
	liPtr->last = itemPtr;
    }
    ++ lPtr->numItems;
}

/*----------------------------------------------------------------------
 * Tix_LinkListFindAndDelete --
 *
 *	Find an element and delete it.
 *
 * liPtr:
 *	Can be NULL.
 *	If non-NULL, the search will start from the current entry indexed
 *	by liPtr;
 *
 * Return value:
 *	1 if element is found and deleted
 *	0 if element is not found
 *----------------------------------------------------------------------
 */
int Tix_LinkListFindAndDelete(infoPtr, lPtr, itemPtr, liPtr)
    Tix_ListInfo * infoPtr;
    Tix_LinkList * lPtr;
    char * itemPtr;
    Tix_ListIterator * liPtr;
{
    Tix_ListIterator defIterator;

    if (liPtr == NULL) {
	Tix_LinkListIteratorInit(&defIterator);
	liPtr = &defIterator;
    }

    if (!liPtr->started) {
	Tix_LinkListStart(infoPtr, lPtr, liPtr);
    }

    if (Tix_LinkListFind(infoPtr, lPtr, itemPtr, liPtr)) {
	Tix_LinkListDelete(infoPtr, lPtr, liPtr);
	return 1;
    } else {
	return 0;
    }
}

int Tix_LinkListDeleteRange(infoPtr, lPtr, fromPtr, toPtr, liPtr)
    Tix_ListInfo * infoPtr;
    Tix_LinkList * lPtr;
    char * fromPtr;
    char * toPtr;
    Tix_ListIterator * liPtr;
{
    Tix_ListIterator defIterator;
    int start = 0;
    int deleted = 0;

    if (liPtr == NULL) {
	Tix_LinkListIteratorInit(&defIterator);
	liPtr = &defIterator;
    }
    if (!liPtr->started) {
	Tix_LinkListStart(infoPtr, lPtr, liPtr);
    }

    for (;
	 !Tix_LinkListDone(liPtr);
	 Tix_LinkListNext (infoPtr, lPtr, liPtr)) {

	if (liPtr->curr == fromPtr) {
	    start = 1;
	}
	if (start) {
	    Tix_LinkListDelete(infoPtr, lPtr, liPtr);
	    ++ deleted;
	}
	if (liPtr->curr == toPtr) {
	    break;
	}
    }

    return deleted;
}

int Tix_LinkListFind(infoPtr, lPtr, itemPtr, liPtr)
    Tix_ListInfo * infoPtr;
    Tix_LinkList * lPtr;
    char * itemPtr;
    Tix_ListIterator * liPtr;
{
    if (!liPtr->started) {
	Tix_LinkListStart(infoPtr, lPtr, liPtr);
    }

    for (Tix_LinkListStart(infoPtr, lPtr, liPtr);
	 !Tix_LinkListDone(liPtr);
	 Tix_LinkListNext (infoPtr, lPtr, liPtr)) {

	if (liPtr->curr == itemPtr) {
	    return 1;				/* found! */
	}
    }

    return 0;					/* Can't find */
}
