/*
 *
 * VRML to RW converter plug-in
 */
/****************************************************************************
 *
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1997 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : LLinkList.c
 *                                                                         
 * Purpose : Polymorphic doubly linked list + methods
 *                                                                         
 ****************************************************************************/

/****************************************************************************
 Includes
 */

#include <stdlib.h>

#include "rpplugin.h"
#include <rpdbgerr.h>
#include "rpvrmlanim.h"
#include "rpvrml.h"
#include "llinklist.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: llinklist.c,v 1.37 2001/02/05 11:44:21 johns Exp $";

/****************************************************************************
 Static locals
 */

static RwFreeList  *llLinkFreeList = (RwFreeList *)NULL;

#if (0)
FieldBlock         *linkBlock;

#endif /* (0) */

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Initialization/Termination

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

VRMLBool
LLinkListOpen(void)
{
    RWFUNCTION(RWSTRING("LLinkListOpen"));

    /* Large block size because we'll be alloctaing vast numbers of these */
    llLinkFreeList = RwFreeListCreate(sizeof(LLLink), 100, 0);

    if (!llLinkFreeList)
    {
        RWRETURN(FALSE);
    }

#if 0
    linkBlock = FieldBlock_Create(sizeof(LLLink), 100);
    if (!linkBlock)
    {
        RWRETURN((FALSE));
    }
#endif /* 0 */

    RWRETURN(TRUE);
}

void
LLinkListClose(void)
{
    RWFUNCTION(RWSTRING("LLinkListClose"));

    if (llLinkFreeList)
    {
        RwFreeListDestroy(llLinkFreeList);
        llLinkFreeList = (RwFreeList *)NULL;
    }

#if 0
    if (linkBlock)
    {
        FieldBlock_Destroy(linkBlock);
    }
#endif /* 0 */

    RWRETURNVOID();

}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Link methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

void
LLLink_Init(LLLink * link)
{
    RWFUNCTION(RWSTRING("LLLink_Init"));
    RWASSERT(link);
    link->Next = (LLLink *)NULL;
    link->Prev = (LLLink *)NULL;
    link->Data = NULL;
    RWRETURNVOID();
}

LLLink             *
LLLink_Create(void *Data)
{
    LLLink             *Link;

    RWFUNCTION(RWSTRING("LLLink_Create"));

    /* NULL data is valid */
    Link = (LLLink *) RwFreeListAlloc(llLinkFreeList);
#if (0)
    Link = FieldBlock_GetElement(linkBlock);
#endif /* (0) */
    if (Link)
    {
        LLLink_Init(Link);
        Link->Data = Data;
        RWRETURN((Link));
    }
    RWRETURN(((LLLink *)NULL));
}

void
LLLink_Destroy(LLLink * link)
{
    static RwInt32      timeToPurge = 0;

    RWFUNCTION(RWSTRING("LLLink_Destroy"));
    RWASSERT(link);
    RwFreeListFree(llLinkFreeList, link);

    /* This makes things go faster under a debug build by shrinking the free list */
    timeToPurge++;
    timeToPurge &= 1023;
#ifdef RWDEBUG
    if (timeToPurge == 0)
    {
        RwFreeListPurge(llLinkFreeList);
    }
#endif /* RWDEBUG */
    RWRETURNVOID();
}

LLLink             *
LLLink_GetNext(LLLink * link)
{
    RWFUNCTION(RWSTRING("LLLink_GetNext"));
    RWASSERT(link);
    RWRETURN((link)->Next);
}

LLLink             *
LLLink_GetPrev(LLLink * link)
{
    RWFUNCTION(RWSTRING("LLLink_GetPrev"));
    RWASSERT(link);
    RWRETURN((link)->Prev);
}

void               *
LLLink_GetData(LLLink * link)
{
    RWFUNCTION(RWSTRING("LLLink_GetData"));
    RWASSERT(link);
    RWRETURN(link->Data);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   LinkList methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

void
LLinkList_Init(LLinkList * llist)
{
    RWFUNCTION(RWSTRING("LLinkList_Init"));
    RWASSERT(llist);
    llist->Items = 0;
    llist->Link.Next = (LLLink *) llist;
    llist->Link.Prev = (LLLink *) llist;
    llist->Link.Data = NULL;
    llist->Iterator = (LLLink *) llist;
    RWRETURNVOID();
}

void
LLinkList_Reset(LLinkList * llist)
{
    RWFUNCTION(RWSTRING("LLinkList_Reset"));
    RWASSERT(llist);
    llist->Items = 0;
    llist->Link.Prev->Next = (LLLink *) llist;
    llist->Link.Next->Prev = (LLLink *) llist;
    RWRETURNVOID();
}

VRMLBool
LLinkList_IsEmpty(LLinkList * llist)
{
    RWFUNCTION(RWSTRING("LLinkList_IsEmpty"));
    RWASSERT(llist);
    RWRETURN(llist->Link.Next == &(llist->Link));
}

void
LLinkList_AddLink(LLinkList * llist, LLLink * link)
{
    RWFUNCTION(RWSTRING("LLinkList_AddLink"));
    RWASSERT(llist);
    RWASSERT(link);
    llist->Link.Next->Prev = link;
    link->Next = llist->Link.Next;
    llist->Link.Next = link;
    link->Prev = &llist->Link;
    llist->Items++;
    RWRETURNVOID();
}

VRMLBool
LLinkList_AddData(LLinkList * llist, void *data)
{
    LLLink             *dataLink;

    RWFUNCTION(RWSTRING("LLinkList_AddData"));
    RWASSERT(llist);
    if ((dataLink = LLLink_Create(data)))
    {
        LLinkList_AddLink(llist, dataLink);
        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

void
LLinkList_RemoveLink(LLinkList * llist, LLLink * link)
{
    RWFUNCTION(RWSTRING("LLinkList_RemoveLink"));
    RWASSERT(llist);
    RWASSERT(link);

    link->Prev->Next = link->Next;
    link->Next->Prev = link->Prev;
    llist->Items--;
    RWRETURNVOID();
}

LLLink             *
LLinkList_GetFirst(LLinkList * llist)
{
    RWFUNCTION(RWSTRING("LLinkList_GetFirst"));

    RWRETURN((llist->Link.Next));
}

LLLink             *
LLinkList_GetLast(LLinkList * llist)
{
    RWFUNCTION(RWSTRING("LLinkList_GetLast"));

    RWRETURN((llist->Link.Prev));
}

VRMLBool
LLinkList_IsTerminator(LLinkList * llist, LLLink * link)
{
    RWFUNCTION(RWSTRING("LLinkList_IsTerminator"));

    RWRETURN((link == (LLLink *) llist));
}

VRMLBool
LLinkList_Destroy(LLinkList * list, linkCallback destroy)
{
    LLLink             *currentLink, *nextLink;
    int                 currentItem = 0;

    RWFUNCTION(RWSTRING("LLinkList_Destroy"));

    /* destroy list */
    currentLink = LLinkList_GetFirst(list);
    while (currentLink != (LLLink *) list)
    {
        void               *data;

        data = currentLink->Data;
        nextLink = LLLink_GetNext(currentLink);
        if (destroy && data)
        {
            destroy(data);
        }
        LLinkList_RemoveLink(list, currentLink);
        LLLink_Destroy(currentLink);
        currentLink = nextLink;
        currentItem++;
    }
    RWRETURN((TRUE));
}

int
LLinkList_NumItems(LLinkList * list)
{
#if (0)
    LLLink             *currentLink;
#endif /* (0) */
    int                 Items = 0;

    RWFUNCTION(RWSTRING("LLinkList_NumItems"));

    Items = list->Items;
#if 0
    currentLink = LLinkList_GetFirst(list);
    while (currentLink != (LLLink *) list)
    {
        Items++;
        currentLink = LLLink_GetNext(currentLink);
    }
#endif /* 0 */
    RWRETURN((Items));
}

VRMLBool
LLinkList_ForAllLinks(LLinkList * list, linkCallback callback)
{
    LLLink             *currentLink, *nextLink;

    RWFUNCTION(RWSTRING("LLinkList_ForAllLinks"));
    RWASSERT(list);
    RWASSERT(callback);

    currentLink = LLinkList_GetLast(list);
    while (currentLink != (LLLink *) list)
    {
        void               *data;

        data = currentLink->Data;
        nextLink = LLLink_GetPrev(currentLink);
        if (data != NULL)
        {
            callback(data);
        }
        currentLink = nextLink;
    }
    RWRETURN(TRUE);
}

RwBool
LLinkList_ForAllLinkData(LLinkList * list, 
                         linkCallbackIII
                         callback, 
                         void *inout)
{
    RWFUNCTION(RWSTRING("LLinkList_ForAllLinkData"));
    RWASSERT(list);
    RWASSERT(callback);

    if (list && callback)
    {
        LLLink             *currentLink, *nextLink;

        currentLink = LLinkList_GetFirst(list);
        while (currentLink != (LLLink *) list)
        {
            void               *linkData;

            linkData = currentLink->Data;
            nextLink = LLLink_GetNext(currentLink);
            if (!callback(linkData, inout))
            {
                /* early out */
                RWRETURN(TRUE);
            }
            currentLink = nextLink;
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
LLinkList_ForAllLinksII(LLinkList * list, linkCallbackII callback)
{
    RWFUNCTION(RWSTRING("LLinkList_ForAllLinksII"));
    RWASSERT(list);
    RWASSERT(callback);

    if (list && callback)
    {
        LLLink             *currentLink, *nextLink;

        currentLink = LLinkList_GetFirst(list);
        while (currentLink != (LLLink *) list)
        {
#if (0)
            void               *data;

#endif /* (0) */

#if (0)
            data = currentLink->Data;
#endif /* (0) */
            nextLink = LLLink_GetNext(currentLink);
            if (!callback(list, currentLink))
            {
                /* early out */
                RWRETURN(TRUE);
            }
            currentLink = nextLink;
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

LLLink             *
LLinkList_IteratorReset(LLinkList * list)
{
    LLLink             *link;

    RWFUNCTION(RWSTRING("LLinkList_IteratorReset"));
    RWASSERT(list);

    link = list->Iterator;
    list->Iterator = (LLLink *) list;

    RWRETURN(link);
}

void
LLinkList_IteratorRestore(LLinkList * list, LLLink * link)
{
    RWFUNCTION(RWSTRING("LLinkList_IteratorRestore"));
    RWASSERT(list);
    RWASSERT(link);

    list->Iterator = link;

    RWRETURNVOID();
}

void               *
LLinkList_IteratorNext(LLinkList * list)
{
    RWFUNCTION(RWSTRING("LLinkList_IteratorNext"));
    RWASSERT(list);

    list->Iterator = list->Iterator->Next;
    if (list->Iterator != (LLLink *) list)
    {
        RWRETURN(list->Iterator->Data);
    }
    else
    {
        RWRETURN(NULL);
    }
}

RwBool
LLinkList_IteratorNextII(LLinkList * list, void *data)
{
    RWFUNCTION(RWSTRING("LLinkList_IteratorNextII"));
    RWASSERT(list);

    if (list)
    {
        list->Iterator = list->Iterator->Next;
        if (list->Iterator != (LLLink *) list)
        {
            data = list->Iterator->Data;

            RWRETURN(TRUE);
        }

        /* end of list */
        RWRETURN(FALSE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

void               *
LLinkList_IteratorPrev(LLinkList * list)
{
    RWFUNCTION(RWSTRING("LLinkList_IteratorPrev"));
    RWASSERT(list);

    list->Iterator = list->Iterator->Prev;
    if (list->Iterator != (LLLink *) list)
    {
        RWRETURN(list->Iterator->Data);
    }
    else
    {
        RWRETURN(NULL);
    }
}

void               *
LLinkList_GetItem(LLinkList * list, int Item)
{
    int                 itemCnt = 0;
    LLLink             *currentLink;
    VRMLBool            itemFound = FALSE;

    RWFUNCTION(RWSTRING("LLinkList_GetItem"));
    RWASSERT(list);
    currentLink = LLinkList_GetLast(list);
    while (currentLink != (LLLink *) list)
    {
        if (Item == itemCnt++)
        {
            itemFound = TRUE;
            break;
        }
        currentLink = LLLink_GetPrev(currentLink);
    }

    if (itemFound)
    {
        RWRETURN(currentLink->Data);
    }

    RWRETURN(NULL);
}
