/************************************************************************/
/* Copyright 2002 MBARI													*/
/************************************************************************/
/* Summary	: Linked List library for OASIS3							*/
/* Filename : olist.c													*/
/* Author	: Robert Herlien (rah)										*/
/* Project	: OASIS Mooring Replacement (OASIS3)						*/
/* Revision: 1.0														*/
/* Created  : 07/06/2016 from oasis3/4 task.h							*/
/*																			*/
/* MBARI provides this documentation and code "as is", with no warranty,	*/
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium	*/
/* Research Institute to assist in its use, correction, modification, or	*/
/* enhancement. This information should not be published or distributed to	*/
/* third parties without specific written permission from MBARI.			*/
/*																			*/
/************************************************************************/
/* Modification History:												*/
/* 10oct2002 rah - OASIS3 list.c created from OASIS list.s				*/
/* 7jul20016 rah - OASIS5 olist.c created from OASIS3 list.c			*/
/* $Log$
 */
/************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <olist.h>						/* OASIS linked list definitions	*/


/************************************************************************/
/* Note that the mutual exclusion for list handling, implemented here,	*/
/* MAY BE INSUFFICIENT for list integrity, depending on your usage of	*/
/* the list routines.  E.g., the typical list_first() list_next()		*/
/* pattern is vulnerable to having someone else manipulate the list		*/
/* between calls.  The user is responsible for locking the list around	*/
/* the entire usage pattern of the list, using the macros lockList()	*/
/* and unlockList().  That's why the mutex semaphore in the LstHead is	*/
/* created as a RecursiveMutex - to allow you to lock it, while allowing*/
/* these routines to also recursively lock it.							*/
/*																		*/
/* Also note that the macros evaluate to TRUE if configUSE_PREEMPTION	*/
/* is zero (not using preemption).  It is assumed that you won't		*/
/* explicitly (or implicitly) relinquish the CPU when in the middle of	*/
/* list manipulation.													*/
/************************************************************************/


/************************************************************************/
/* Function	   : list_init												*/
/* Purpose	   : Initialize Linked List									*/
/* Inputs	   : Ptr to LstHead											*/
/* Outputs	   : None													*/
/************************************************************************/
Void	list_init(LstHead *lp)
{
	lp->lst_head = NULLNODE;
	lp->lst_tail = NULLNODE;
#if (configUSE_PREEMPTION != 0)
	lp->lstSem = xSemaphoreCreateRecursiveMutexStatic(&lp->lstSemBuf);
#endif

} /* list_init() */


/************************************************************************/
/* Function	   : list_head												*/
/* Purpose	   : Delete and return head of linked list					*/
/* Inputs	   : Ptr to LstHead											*/
/* Outputs	   : None													*/
/************************************************************************/
Node	*list_head(LstHead *lp)
{
	Reg Node		*np;

	if (lp->lst_head == NULLNODE)
		return(NULLNODE);

	lockList(lp);
	np = lp->lst_head;
	lp->lst_head = np->lst_next;

	if (np->lst_next == NULLNODE)
		lp->lst_tail = NULLNODE;
	else
		np->lst_next->lst_prev = NULL;

	unlockList(lp);
	return(np);

} /* list_head() */


/************************************************************************/
/* Function	   : list_add												*/
/* Purpose	   : Add a node to the end of a linked list					*/
/* Inputs	   : List to add to, node to add							*/
/* Outputs	   : None													*/
/************************************************************************/
Void	 list_add(LstHead *lp, Node *np)
{
	lockList(lp);
	np->lst_next = NULLNODE;
	np->lst_prev = lp->lst_tail;

	if (lp->lst_tail == NULLNODE)
		lp->lst_head = np;
	else
		lp->lst_tail->lst_next = np;

	lp->lst_tail = np;
	unlockList(lp);

} /* list_add() */


/************************************************************************/
/* Function	   : listIsOnList											*/
/* Purpose	   : Determine whether given node is on a given list		*/
/* Inputs	   : List, node to check									*/
/* Outputs	   : TRUE if on list, else FALSE							*/
/************************************************************************/
static MBool  listIsOnList(LstHead *lp, Node *target)
{
	Node	*np;

	for (np = list_first(lp); np != NULLNODE; np = list_next(np))
		if (np == target)
			return(TRUE);

	return(FALSE);

} /* listIsOnList() */


/************************************************************************/
/* Function	   : list_get												*/
/* Purpose	   : Delete and return a given node from linked list		*/
/* Inputs	   : List to get from, node to delete						*/
/* Outputs	   : Node passed in, or NODE_NULL if not on list			*/
/************************************************************************/
Node	 *list_get(LstHead *lp, Node *np)
{
	lockList(lp);
	if (!listIsOnList(lp,np))
	{
		unlockList(lp);
		return(NULLNODE);
	}

	if (np->lst_prev == NULLNODE)
		lp->lst_head = np->lst_next;
	else
		np->lst_prev->lst_next = np->lst_next;

	if (np->lst_next == NULLNODE)
		lp->lst_tail = np->lst_prev;
	else
		np->lst_next->lst_prev = np->lst_prev;

	unlockList(lp);
	return(np);

} /* list_get() */


/************************************************************************/
/* Function	   : list_first												*/
/* Purpose	   : Return first entry of linked list						*/
/* Inputs	   : Ptr to LstHead											*/
/* Outputs	   : None													*/
/************************************************************************/
Node	 *list_first(LstHead *lp)
{
	return(lp->lst_head);

} /* list_first() */


/************************************************************************/
/* Function	   : list_next												*/
/* Purpose	   : Return next entry of linked list						*/
/* Inputs	   : Ptr to LstHead											*/
/* Outputs	   : None													*/
/************************************************************************/
Node	 *list_next(Node *np)
{
	return(np->lst_next);

} /* list_next() */


/************************************************************************/
/* Function	   : list_count												*/
/* Purpose	   : Return number of nodes on linked list					*/
/* Inputs	   : Ptr to LstHead											*/
/* Outputs	   : Number of Nodes										*/
/************************************************************************/
Nat32	list_count(LstHead *lp)
{
	Reg Node		*np;
	Reg Nat32		cnt=0;

	lockList(lp);
	for (np = list_first(lp); np != NULLNODE; np = list_next(np))
		cnt++;

	unlockList(lp);
	return(cnt);

} /* list_count() */
