/*
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 * 
 */

#include "xmlist.h"

/************************************************************
 *
 *  Stack code.
 *
 ************************************************************/

#define STACK_INC 25

/************************************************************
 *
 *  Stack Manipulation code.
 *
 ************************************************************/

/*	Function Name: _XmStackInit
 *	Description: Initializes the stack.
 *	Arguments: none
 *	Returns: the stack
 */

XmStack
_XmStackInit()
{
    return((XmStack) XtCalloc(sizeof(XmStackRec), (Cardinal) 1));
}

/*	Function Name: _XmStackFree
 *	Description: Frees all data associated with the stack.
 *	Arguments: stack - the stack
 *	Returns: none
 */

void
_XmStackFree(XmStack stack)
{
    XtFree((XtPointer) stack->elems);
    XtFree((XtPointer) stack);
}

/*	Function Name: _XmStackPush
 *	Description: Pushes an element onto the stack
 *	Arguments: stack - the stack
 *                 elem - element to push onto it.
 *	Returns: none
 */

void
_XmStackPush(XmStack stack, XtPointer elem)
{
    if ((++stack->top) >= stack->alloc) {
	stack->alloc += STACK_INC;
	stack->elems = 
	    (XtPointer *) XtRealloc((XtPointer) stack->elems,
				    sizeof(XtPointer) * stack->alloc);
    }

    stack->elems[stack->top] = elem;

#ifdef STACK_DEBUG
    printf("Pushing %d as elem %d\n", (int) elem, stack->top);    
#endif
}

/*	Function Name: _XmStackPop
 *	Description: Pops an element off of the stack.
 *	Arguments: stack - stack to pop off of.
 *	Returns: elem - the element that has been popped.
 */

XtPointer
_XmStackPop(XmStack stack)
{
    if (stack->top <= 0)
	return(NULL);		/* no elements on the stack. */

#ifdef STACK_DEBUG
    printf("Popping %d from elem %d\n", 
	   (int) stack->elems[stack->top], stack->top);
#endif

    return(stack->elems[(stack->top)--]);
}

/************************************************************
 *
 *  Queue code.
 *
 ************************************************************/

#ifdef QUEUE_DEBUG
#define QUEUE_INC 5
#else
#define QUEUE_INC 25
#endif

/************************************************************
 *
 *  Queue Manipulation code.
 *
 ************************************************************/

/*	Function Name: _XmQueueInit
 *	Description: Initializes the queue.
 *	Arguments: none
 *	Returns: the queue
 */

XmQueue
_XmQueueInit()
{
    return((XmQueue) XtCalloc(sizeof(XmQueueRec), (Cardinal) 1));
}

/*	Function Name: _XmQueueFree
 *	Description: Frees all data associated with the queue.
 *	Arguments: queue - the queue
 *	Returns: none
 */

void
_XmQueueFree(XmQueue queue)
{
    int count;
    _XmQElem *elem = queue->first;
    XmStack stack = _XmStackInit();

    for (count = 0; count < 2; count++) {
	while (elem != NULL) {
	    if (elem->alloced) 
		_XmStackPush(stack, (XtPointer) elem);
	    elem = elem->next;
	}
	
	elem = queue->free_elems;
    }

    while ((elem = (_XmQElem *) _XmStackPop(stack)) != NULL)
	XtFree((XtPointer) elem);

    _XmStackFree(stack);

    XtFree((XtPointer) queue);
}

/*	Function Name: QueuePush
 *	Description: Pushes an element onto the queue
 *	Arguments: queue - the queue
 *                 data_in - the data to push onto the queue.
 *	Returns: none
 */

/*
 * This is being removed because its name was not changed when compiled
 * -DBX.  This means that there is already a QueuePush() defined in the
 * BX 3.1 bx.o, so integrating the EPak into BX causes multiply defined
 * symbols.  Luckily, this function is not currently used in EPak.  We
 * can remove this only when we don't support EPak integration into
 * BX 3.0 or BX 3.1.
 */

#ifdef notdef
void
QueuePush(XmQueue queue, XtPointer data_in)
{
    _XmQElem *elem = _Xm_GetNewElement(queue);

    _Xm_AddQueue(queue, queue->last, elem);

    if (queue->first == NULL) 
	queue->first = elem;

    queue->last = elem;

    elem->data = data_in;

#ifdef QUEUE_DEBUG
    printf("Pushing %d\n", (int) data_in);
#endif
}
#endif

/*	Function Name: _XmQueuePop
 *	Description: Pops an element off of the queue.
 *	Arguments: queue - queue to pop off of.
 *	Returns: elem - the element that has been popped.
 */

XtPointer
_XmQueuePop(XmQueue queue)
{
    _XmQElem *first = _Xm_RemQueue(&queue->first);

    if (queue->first == NULL)
	queue->last = NULL;

    if (first == NULL)
	return(NULL);

    _Xm_AddQueue(NULL, queue->free_elems, first);

#ifdef QUEUE_DEBUG
    printf("Popping %d\n", (int) first->data);
#endif

    return(first->data);
}

/*	Function Name: _XmQueueCount
 *	Description: Returns the number of items in the queue
 *	Arguments: queue - queue to check.
 *	Returns: number of items in queue.
 */


int
_XmQueueCount(XmQueue queue)
{
    register int i;
    register _XmQElem *elem = queue->first;

    for (i = 0; elem != NULL; i++)
	elem = elem->next;

    return(i);
}

/*	Function Name: _XmQueuePrint
 *	Description: Prints out the real and free nodes of the queue.
 *	Arguments: queue - queue to print.
 *	Returns: none.
 */


#ifdef QUEUE_DEBUG
void
_XmQueuePrint(XmQueue queue)
{
    _XmQElem *elem;

    printf("Elements:\n");
    for (elem = queue->first; elem != NULL; elem = elem->next)
	printf("Element - %d\n", elem->data);

    printf("Reverse Elements:\n");
    for (elem = queue->last; elem != NULL; elem = elem->prev)
	printf("Element - %d\n", elem->data);

    printf("Free Elements:\n");
    for (elem = queue->free_elems; elem != NULL; elem = elem->next)
	printf("Free Element - %d\n", elem->data);
}
#endif

/************************************************************
 *
 *  Internal Used (Utils Lib) functions.
 *
 ************************************************************/

/*	Function Name: _Xm_AddQueue
 *	Description: Adds an element to the queue, after the element specified.
 *	Arguments: queue - Queue to add to, if NULL then we may be adding
 *                         to the free items list.
 *                 after - the element is added after this one.
 *                 elem - element to add.
 *	Returns: none.
 */

void
_Xm_AddQueue(XmQueue queue, _XmQElem *after, _XmQElem *elem)
{
    if (elem != NULL) {
	elem->prev = after;
	if (after == NULL) 
	    /*
	     * If queue is NULL then this is being added to the 
	     * free elements queue, and we make certain assumptions, like
	     * we are always adding to then end of the list, so that
	     * we never will try to add before the first element.
	     */
	    if (queue != NULL) {
		elem->next = queue->first;
		if (elem->next != NULL)
		    elem->next->prev = elem;
	    }
	    else
		elem->next = NULL;
	else
	    elem->next = after->next;
    }

    if (after != NULL) {
	if (after->next != NULL)
	    after->next->prev = elem;
	after->next = elem;
    }

}

/*	Function Name: _Xm_RemQueue
 *	Description: Removes the specified element form a queue, and sets
 *                   the queue to point to the next element.
 *	Arguments: queue - the queue to remove it from.
 *	Returns: the element removed.
 */

_XmQElem *
_Xm_RemQueue(_XmQElem ** queue)
{
    _XmQElem * elem = *queue;

    if (elem != NULL) {
	*queue = elem->next;

	if (elem->next != NULL)
	    elem->next->prev = elem->prev;

	if (elem->prev != NULL)
	    elem->prev->next = elem->next;
    }

    return(elem);
}

/*	Function Name: GetNewElement
 *	Description: Gets an element off the free_elems list, allocates
 *                   more elems if necessary.
 *	Arguments: queue - the queue to get the element from.
 *	Returns: an element.
 */

_XmQElem *
_Xm_GetNewElement(XmQueue queue)
{
    _XmQElem *elem;

    if ((elem = _Xm_RemQueue(&queue->free_elems)) == NULL) {
	register int i;

	/*
	 * We are out of free elements, alloc some more.
	 */

	queue->free_elems = (_XmQElem *) XtCalloc(sizeof(_XmQElem), QUEUE_INC);
	queue->free_elems->alloced = True;

	queue->free_elems->next = elem = queue->free_elems + 1;
	for (i = 1; i < (QUEUE_INC - 1); i++, elem++) {
	    elem->prev = elem - 1;
	    elem->next = elem + 1;
	}
	elem->prev = elem - 1;

	/*
	 * Pop an element off the free elements queue.
	 */

	elem = _Xm_RemQueue(&queue->free_elems);
    }

    return(elem);
}

/************************************************************
 *
 *  List code.
 *
 ************************************************************/

/*
 * This code is very dependant on the queue manipulation code, they
 * are sharing much functionality, several of these functions are just
 * wrappers around the queue maipulation code.
 */

/*	Function Name: _XmListInit
 *	Description: Initializes the list.
 *	Arguments: none
 *	Returns: the queue
 */

XmList
_XmListInit()
{
    return((XmList) _XmQueueInit());
}

/*	Function Name: _XmListFree
 *	Description: Frees all data associated with the list.
 *	Arguments: list - the list
 *	Returns: none
 */

void
_XmListFree(XmList list)
{
    _XmQueueFree((XmQueue) list);
}

/*	Function Name: _XmListAddAfter
 *	Description: Adds an element after the specified element.
 *	Arguments: list - the list
 *                 elem - The element to add this one after.
 *                        If NULL then add this element to the beginning of 
 *                        the list
 *                 data_in - the data to push onto the queue.
 *	Returns: The new added element.
 */

XmListElem *
_XmListAddAfter(XmList list, XmListElem *elem_in, XtPointer data_in)
{
    XmListElem *elem = (XmListElem *) _Xm_GetNewElement((XmQueue) list);

    _Xm_AddQueue((XmQueue) list, elem_in, elem);

    if (elem_in == NULL) 
	list->first = elem;

    if (elem_in == list->last)
	list->last = elem;

    elem->data = data_in;

    return(elem);
}

/*	Function Name: _XmListAddBefore
 *	Description: Adds an element before the specified element.
 *	Arguments: list - the list
 *                 elem - The element to add this one before.
 *                        If NULL then add this element to the end of 
 *                        the list.
 *                 data_in - the data to push onto the queue.
 *	Returns: The new added element.
 */

XmListElem *
_XmListAddBefore(XmList list, XmListElem *elem_in, XtPointer data_in)
{
    XmListElem *after;

    if (elem_in == NULL) 
	after = list->last;
    else
	after = elem_in->prev;

    return(_XmListAddAfter(list, after, data_in));
}

/*	Function Name: _XmListRemove
 *	Description: Removes an element from the list.
 *	Arguments: list - the list to remove the element from.
 *	Returns: none.
 */

void
_XmListRemove(XmList list, XmListElem *elem)
{
    XmListElem *dead = (XmListElem *) _Xm_RemQueue((_XmQElem **) &elem);    

    /*
     * if NULL is passed, then no action is taken.
     */

    if (dead == NULL) 
	return;

    if (list->first == dead)
	if ((list->first = dead->next) == NULL)
	    list->last = NULL;

    if (list->last == dead)
	if ((list->last = dead->prev) == NULL)
	    list->first = NULL;

    _Xm_AddQueue(NULL, list->free_elems, (_XmQElem *) dead);
}

/*	Function Name: _XmListCount
 *	Description: Returns the number of items in the list.
 *	Arguments: list - list to check.
 *	Returns: number of items in list.
 */


int
_XmListCount(XmList list)
{
    return(_XmQueueCount((XmQueue) list));
}

/*	Function Name: _XmListExec
 *	Description: Executes a function on all nodes in the list.
 *	Arguments: list - the list to use.
 *                 start - the element to start with.
 *                 end - the element to end with.
 *                 exec_func - the function to execute.
 *                 data - data to pass to the function.
 *	Returns: NULL is returned if all items from start to end 
 *               have been operated on.  Otherwise the node in which
 *               the operation stopped is returned.
 *
 * NOTES: If start is NULL the start is the first element in the list.
 *        If end is NULL then all elements after start will be searched.
 *        The list is always executed if forward order from start -> 
 *        	start->next, start->next->next...
 *        
 */

XmListElem *
_XmListExec(XmList list, XmListElem *start, XmListElem *end, 
	 XmListFunc func, XtPointer data)
{
    if (start == NULL)
	start = list->first;

    for ( ; (start != end) && (start != NULL); start = start->next) {
	if (!(*func)(start, data))
	    return(start);
    }
    
    return(NULL);
}
 
/*	Function Name: _XmListPrint
 *	Description: Prints out the real and free nodes of the list.
 *	Arguments: list - list to print.
 *	Returns: none.
 */


#ifdef QUEUE_DEBUG
void
_XmListPrint(XmList list)
{
    _XmQueuePrint((XmQueue) list);
}
#endif
   
