/**************************************************************************
*** COPYRIGHT (c) 2002 by TransNexus, Inc.                              ***
***                                                                     ***
*** This software is property of TransNexus, Inc.                       ***
*** This software is freely available under license from TransNexus.    ***
*** The license terms and conditions for free use of this software by   ***
*** third parties are defined in the OSP Toolkit Software License       ***
*** Agreement (LICENSE.txt).  Any use of this software by third         ***
*** parties, which does not comply with the terms and conditions of the ***
*** OSP Toolkit Software License Agreement is prohibited without        ***
*** the prior, express, written consent of TransNexus, Inc.             ***
***                                                                     ***
*** Thank you for using the OSP ToolKit(TM).  Please report any bugs,   ***
*** suggestions or feedback to support@transnexus.com                   ***
***                                                                     ***
**************************************************************************/








/*
 * osplist.c - Utility functions for linked lists.
 */

#include "osp/osp.h"
#include "osp/osplist.h"

/*
 * The following functions implement a fairly straightforward
 * linked list, though there is a small twist. Specifically,
 * a la Knuth, the List structure actually points to the last
 * item on the list, which is then circularly linked to the
 * first item. This approach minimizes the time needed to
 * find either the front (2 memory references) or back (1
 * memory reference) of the list.
 *
 *    List Head: ---------------------------------+
 *                                                |
 *                                                v
 *         +-------+    +-------+             +-------+
 *    +--->| Item1 |--->| Item2 |---> ... --->| ItemN |---+
 *    |    +-------+    +-------+             +-------+   |
 *    +---------------------------------------------------+
 *
 */

/*
 * Many of the following functions are also implemented as
 * macros for speed. If so implemented, the macro implementation
 * takes precedence. To force the use of the function
 * implementation (e.g. for debugging), simply comment out
 * the macro definition in osplist.h.
 */

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListLinkNew() - initialize a list link                            */
/*-----------------------------------------------------------------------*/
void                               /* no return value */
OSPPListLinkNew(
    OSPTLISTLINK *ospvLink         /* address of link to initialize */
)
{
    if (ospvLink != OSPC_OSNULL) 
    {
        /* just NULL out the link pointer */
        ospvLink->ospmLinkNext = OSPC_OSNULL;
    }
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListNew() - initialize a list                                     */
/*-----------------------------------------------------------------------*/
void                               /* no return value */
OSPPListNew(
    OSPTLIST *ospvList             /* address of list to initialize */
)
{
    if (ospvList != OSPC_OSNULL) 
    {
        /* just NULL out the list pointer */
        *ospvList = OSPC_OSNULL;
    }
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListDelete() - clean up for a list that's not needed              */
/*-----------------------------------------------------------------------*/
void                              /* no return value */
OSPPListDelete(
    OSPTLIST *ospvList            /* address of list to destroy */
)
{
    if(ospvList != OSPC_OSNULL) 
    {
        /* check to see that the list is empty */
        if (*ospvList == OSPC_OSNULL) 
        {
            /* NULL out the list pointer */
            *ospvList = OSPC_OSNULL;
        }
    }
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListEmpty() - is a list empty?                                    */
/*-----------------------------------------------------------------------*/
unsigned                            /* non-zero if empty, zero otherwise */
OSPPListEmpty(
    OSPTLIST *ospvList              /* list to check */
)
{
    unsigned ospvEmpty = OSPC_TRUE;

    if (ospvList != OSPC_OSNULL) 
    {
        ospvEmpty = (*ospvList == OSPC_OSNULL);
    }
    return(ospvEmpty);
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListLast() - returns last item on a list                          */
/*-----------------------------------------------------------------------*/
void *                     /* returns pointer to item or NULL */
OSPPListLast(
    OSPTLIST *ospvList     /* list to reference */
)
{
    /* see introductory comment on list structure at beginning of file */

    OSPTLISTLINK *ospvLast = OSPC_OSNULL;

    if (ospvList != OSPC_OSNULL) 
    {
        ospvLast = *ospvList;
    }
    return(ospvLast);
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListFirst() - returns first item on list                          */
/*-----------------------------------------------------------------------*/
void *                        /* returns pointer to item or null */
OSPPListFirst(
    OSPTLIST *ospvList        /* list to reference */
)
{
    /* see introductory comment on list structure at beginning of file */

    OSPTLISTLINK *ospvFirst = OSPC_OSNULL;  /* item to return */

    if (ospvList != OSPC_OSNULL) 
    {
        if (OSPPListEmpty(ospvList))
        {
            ospvFirst = OSPC_OSNULL;
        }
        else
        {
            ospvFirst = ((OSPTLISTLINK *)(*ospvList))->ospmLinkNext;
        }
    }
    return(ospvFirst);
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListNext() - returns next item on list without removing any item  */
/*-----------------------------------------------------------------------*/
void *                        /* returns pointer to item or null */
OSPPListNext(
    OSPTLIST *ospvList,       /* list to reference */
    void     *ospvItem        /* current item */
)
{
    /* see introductory comment on list structure at beginning of file */

    OSPTLISTLINK *ospvNext = OSPC_OSNULL;   /* item to return */

    if(ospvList != OSPC_OSNULL) 
    {
        if (ospvItem != OSPC_OSNULL) 
        {
            if (OSPPListLast(ospvList) == ospvItem)
            {
                ospvNext = OSPC_OSNULL;
            }
            else
            {
                ospvNext = ((OSPTLISTLINK *)ospvItem)->ospmLinkNext;
            }
        }
    }
    return(ospvNext);
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListMove() - move list contents from one list to another          */
/*-----------------------------------------------------------------------*/
void                          /* no return */
OSPPListMove(
    OSPTLIST *ospvDst,       /* destination list */
    OSPTLIST *ospvSrc        /* source list */
)
{
    if (ospvDst != OSPC_OSNULL) 
    {
        if (ospvSrc != OSPC_OSNULL) 
        {
            *ospvDst = *ospvSrc;
            OSPPListNew(ospvSrc);
        }
    }
}


/**/
/*-----------------------------------------------------------------------*/
/* OSPPListAppend() - add item to end of list                            */
/*-----------------------------------------------------------------------*/
void                         /* no return value */
OSPPListAppend(
    OSPTLIST *ospvList,      /* list to which item is to be added */
    void     *ospvItem       /* item to add */
)
{
    /* see introductory comment on list structure at beginning of file */

    if (ospvList != OSPC_OSNULL) 
    {
        if (ospvItem != OSPC_OSNULL) 
        {
            if (OSPPListEmpty(ospvList)) 
            {
                ((OSPTLISTLINK *)ospvItem)->ospmLinkNext = (OSPTLISTLINK *)ospvItem;
            }
            else
            {
                ((OSPTLISTLINK *)ospvItem)->ospmLinkNext =
                    ((OSPTLISTLINK *)(*ospvList))->ospmLinkNext;
                ((OSPTLISTLINK *)(*ospvList))->ospmLinkNext = (OSPTLISTLINK *)ospvItem;
            }
            *ospvList = (OSPTLISTLINK *)ospvItem;
        }
    }
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListRemove()                                                      */
/*-----------------------------------------------------------------------*/

void *                       /* returns item removed or NULL */
OSPPListRemove(
    OSPTLIST *ospvList
)
{
    /* see introductory comment on list structure at beginning of file */

    OSPTLISTLINK *ospvItem = OSPC_OSNULL;   /* item to return */

    if (ospvList != OSPC_OSNULL) 
    {
        if (OSPPListEmpty(ospvList))
        {
            ospvItem = OSPC_OSNULL;
        }
        else if (OSPPListFirst(ospvList) == OSPPListLast(ospvList))
        {
            ospvItem = *ospvList;
            *ospvList = OSPC_OSNULL;
            ospvItem->ospmLinkNext = OSPC_OSNULL;
        }
        else
        {
            ospvItem = ((OSPTLISTLINK *)(*ospvList))->ospmLinkNext;
            ((OSPTLISTLINK *)(*ospvList))->ospmLinkNext =
                ospvItem->ospmLinkNext;
            ospvItem->ospmLinkNext = OSPC_OSNULL;
        }
    }
    return(ospvItem);
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListNextToLast() - returns next to last item                      */
/*-----------------------------------------------------------------------*/
void *                       /* returns item or NULL */
OSPPListNextToLast(
    OSPTLIST *ospvList
)
{
    void *lastitem  = OSPC_OSNULL,
         *curritem  = OSPC_OSNULL,
         *nextolast = OSPC_OSNULL;

    lastitem = OSPPListLast(ospvList);
    curritem = OSPPListFirst(ospvList);

    while (curritem != OSPC_OSNULL &&
        ((OSPTLISTLINK *)curritem)->ospmLinkNext != lastitem)
        curritem = OSPPListNext(ospvList, curritem);

    nextolast = curritem;

    return nextolast;
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListRemoveSpecificItem() - removes a specific item                */
/*-----------------------------------------------------------------------*/
void *                       /* returns item removed or NULL */
OSPPListRemoveSpecificItem(
    OSPTLIST *ospvList,
    void     *ospvItem
)
{
    /* see introductory comment on list structure at beginning of file */

    void     *curritem = OSPC_OSNULL,
             *previtem = OSPC_OSNULL;

    if (ospvList != OSPC_OSNULL) 
    {

        if (OSPPListEmpty(ospvList))
        {
            ospvItem = OSPC_OSNULL;
        }
        else 
        {
            previtem = curritem = OSPPListFirst(ospvList); 
            if (ospvItem == curritem && curritem == OSPPListLast(ospvList))
            {
                ospvItem = (void *)*ospvList;
                *ospvList = OSPC_OSNULL;
                ((OSPTLISTLINK *)ospvItem)->ospmLinkNext = OSPC_OSNULL;
            }
            else
            {
                for (; curritem != OSPC_OSNULL; 
                    curritem = OSPPListNext(ospvList,curritem))
                {
                    if (curritem == ospvItem)
                    {
                        /* if first item in list */
                        if (curritem == previtem)
                        {
                            ospvItem = ((OSPTLISTLINK *)(*ospvList))->ospmLinkNext;
                            ((OSPTLISTLINK *)(*ospvList))->ospmLinkNext =
                                ((OSPTLISTLINK *)ospvItem)->ospmLinkNext;
                            ((OSPTLISTLINK *)ospvItem)->ospmLinkNext = OSPC_OSNULL;
                        }
                        else
                        {
                            /* if last item in list */
                            if (curritem == OSPPListLast(ospvList))
                            {
                                ospvItem = (void *)*ospvList;
                                previtem = OSPPListNextToLast(ospvList);
                                *ospvList = (OSPTLIST)previtem;
                            }
                            ((OSPTLISTLINK *)previtem)->ospmLinkNext =  
                                ((OSPTLISTLINK *)ospvItem)->ospmLinkNext;
                        }
                        break;
                    }
                    previtem = curritem;
                }
            }
        }
    }
    return(ospvItem);
}

/**/
/*-----------------------------------------------------------------------*/
/* OSPPListCount() - returns list count                                  */
/*-----------------------------------------------------------------------*/
unsigned                        /* returns list count */
OSPPListCount(
    OSPTLIST *ospvList)
{
    unsigned  count = 0;
    void     *item  = OSPC_OSNULL;

    for (item = OSPPListFirst(ospvList); 
        item != OSPC_OSNULL; 
        item = OSPPListNext(ospvList, item))
    {
        count++;
    }
    return count;
}
