/*
 *
 * VRML to RW converter plug-in
 */

/****************************************************************************
 *
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1997 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : VrmlNodeType.c
 *                                                                         
 * Purpose : The VrmlNodeType class is responsible for storing the field type 
 *           & value information of standard & user defined prototypes,
 *
 ****************************************************************************/

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

#include <stdio.h>
#include <assert.h>
#include <string.h>

#include "rpplugin.h"
#include "vrmlnodetype.h"
#include "utilities.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: vrmlnodetype.c,v 1.30 2001/03/06 08:50:01 Martins Exp $";

/****************************************************************************
 Local (Static) Prototypes
 */

static RwBool       VrmlNodeType_add(VrmlNodeType *, LLinkList *,
                                     const char *, int);
static RwInt32      VrmlNodeType_has(VrmlNodeType *, LLinkList *,
                                     const char *);

static NameTypeRec *NameTypeRec_Create(const char *name, int type);
static RwBool       NameTypeRec_Destroy(NameTypeRec * nr);

/****************************************************************************
 Local (static) Globals
 */

/* list of standard & user defined VrmlNodeTypes */
static LLinkList    typeList;
static RwFreeList  *vrmlNodeTypeFreeList = (RwFreeList *)NULL;

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

   Initialisation/Termination

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

RwBool
VrmlNodeTypeOpen(void)
{
    RWFUNCTION(RWSTRING("VrmlNodeTypeOpen"));

    vrmlNodeTypeFreeList =
        RwFreeListCreate(sizeof(VrmlNodeType), 20, 0);
    if (!vrmlNodeTypeFreeList)
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

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

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

    RWRETURNVOID();
}

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

   VrmlNodeType methods

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

VrmlNodeType       *
VrmlNodeType_Create(const char *nm)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_Create"));
    RWASSERT(nm);

    if (nm)
    {
        VrmlNodeType       *t;

        t = (VrmlNodeType *) RwFreeListAlloc(vrmlNodeTypeFreeList);
        if (!t)
        {
            RWRETURN((VrmlNodeType *)NULL);
        }

        rwstrdup(t->name, nm);
        if (!t->name)
        {
            VrmlNodeType_Destroy(t);

            RWRETURN((VrmlNodeType *)NULL);
        }

        t->afUrl = (AbstractField *)NULL;
        LLinkList_Init(&t->eventIns);
        LLinkList_Init(&t->eventOuts);
        LLinkList_Init(&t->fields);
        LLinkList_Init(&t->aNodeDefs);

        RWRETURN(t);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((VrmlNodeType *)NULL);
}

RwBool
VrmlNodeType_Destroy(VrmlNodeType * t)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_Destroy"));
    RWASSERT(t);

    if (t)
    {
        /* free the name */
        if (t->name)
        {
            RwFree(t->name);
        }

        if (t->afUrl)
        {
            AbstractField_Destroy(t->afUrl);
        }

        /* destroy eventIns list */
        LLinkList_Destroy(&t->eventIns,
                          DESTROYCALLBACK(NameTypeRec_Destroy));

        /* destroy eventOuts list */
        LLinkList_Destroy(&t->eventOuts,
                          DESTROYCALLBACK(NameTypeRec_Destroy));

        /* destroy fields list */
        LLinkList_Destroy(&t->fields,
                          DESTROYCALLBACK(NameTypeRec_Destroy));

        /* destroy AbstractNode definition list */
        LLinkList_Destroy(&t->aNodeDefs,
                          DESTROYCALLBACK(AbstractNode_Destroy));

        RwFreeListFree(vrmlNodeTypeFreeList, t);

        RWRETURN(TRUE);
    }

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

RwBool
VrmlNodeType_addEventIn(VrmlNodeType * t, const char *name, int type)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_addEventIn"));
    RWASSERT(t);
    RWASSERT(name);

    if (t && name)
    {
        if (!VrmlNodeType_add(t, &t->eventIns, name, type))
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

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

RwBool
VrmlNodeType_addEventOut(VrmlNodeType * t, const char *name, int type)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_addEventOut"));
    RWASSERT(t);
    RWASSERT(name);

    if (t && name)
    {
        if (!VrmlNodeType_add(t, &t->eventOuts, name, type))
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

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

RwBool
VrmlNodeType_addField(VrmlNodeType * t, const char *name, int type)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_addField"));
    RWASSERT(t);
    RWASSERT(name);

    if (t && name)
    {
        if (!VrmlNodeType_add(t, &t->fields, name, type))
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

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

RwBool
VrmlNodeType_addExposedField(VrmlNodeType * t, const char *name,
                             int type)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_addExposedField"));
    RWASSERT(t);
    RWASSERT(name);

    if (t && name)
    {
        char                buffer[256];

        if (!VrmlNodeType_add(t, &t->fields, name, type))
        {
            RWRETURN(FALSE);
        }

        sprintf(buffer, "set_%s", name);
        if (!VrmlNodeType_add(t, &t->eventIns, buffer, type))
        {
            RWRETURN(FALSE);
        }

        sprintf(buffer, "%s_changed", name);
        if (!VrmlNodeType_add(t, &t->eventOuts, buffer, type))
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
VrmlNodeType_add(VrmlNodeType * t, LLinkList * recs, const char *name,
                 int type)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_add"));
    RWASSERT(t);
    RWASSERT(recs);
    RWASSERT(name);

    if (t && recs && name)
    {
        NameTypeRec        *nr = (AbstractField *)NULL;

        /* used to store PROTO's default field vale */
        nr = NameTypeRec_Create(name, type);
        if (!nr)
        {
            RWRETURN(FALSE);
        }

        if (!LLinkList_AddData(recs, nr))
        {
            NameTypeRec_Destroy(nr);

            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

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

RwInt32
VrmlNodeType_hasEventIn(VrmlNodeType * t, const char *name)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_hasEventIn"));
    RWASSERT(t);
    RWASSERT(name);

    if (t && name)
    {
        RWRETURN(VrmlNodeType_has(t, &t->eventIns, name));
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

RwInt32
VrmlNodeType_hasEventOut(VrmlNodeType * t, const char *name)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_hasEventOut"));
    RWASSERT(t);
    RWASSERT(name);

    if (t && name)
    {
        RWRETURN(VrmlNodeType_has(t, &t->eventOuts, name));
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

RwInt32
VrmlNodeType_hasField(VrmlNodeType * t, const char *name)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_hasField"));
    RWASSERT(t);
    RWASSERT(name);

    if (t && name)
    {
        RWRETURN(VrmlNodeType_has(t, &t->fields, name));
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

RwInt32
VrmlNodeType_hasExposedField(VrmlNodeType * t, const char *name)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_hasExposedField"));
    RWASSERT(t);
    RWASSERT(name);

    if (t && name)
    {
        char                tmp[1000];
        int                 typeField, typeEventIn, typeEventOut;

        /*
         * Must have field "name", eventIn "set_name", and eventOut
         * "name_changed", all with same type:
         */

        typeField = VrmlNodeType_has(t, &t->fields, name);
        if (!typeField)
        {
            RWRETURN(0);
        }

        sprintf(tmp, "set_%s\n", name);
        typeEventIn =
            VrmlNodeType_has(t, &t->eventIns, name /* OR tmp? */ );
        if (!typeEventIn)
        {
            RWRETURN(0);
        }

        sprintf(tmp, "%s_changed", name);
        typeEventOut =
            VrmlNodeType_has(t, &t->eventOuts, name /* OR tmp? */ );
        if (typeEventOut)
        {
            RWRETURN(0);
        }

        if ((typeField != typeEventIn) ||
            (typeField != typeEventOut)
            || (typeEventIn != typeEventOut))
        {
            RWRETURN(0);
        }

        RWRETURN(typeField);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

static              RwInt32
VrmlNodeType_has(VrmlNodeType * t, LLinkList * recs, const char *name)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_has"));
    RWASSERT(t);
    RWASSERT(recs);
    RWASSERT(name);

    if (t && recs && name)
    {
        LLLink             *currentLink;

        currentLink = LLinkList_GetFirst(recs);
        if (!currentLink)
        {
            RWRETURN(0);
        }

        while (!LLinkList_IsTerminator(recs, currentLink))
        {
            NameTypeRec        *rt;

            rt = (NameTypeRec *) LLLink_GetData(currentLink);
            if (!rt)
            {
                RWRETURN(0);
            }

            if ((rwstrcmp(rt->name, name) == 0))
            {
                RWRETURN(rt->type);
            }

            currentLink = LLLink_GetNext(currentLink);
        }

        RWRETURN(0);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

AbstractNode       *
VrmlNodeType_GetBaseAbstractNode(VrmlNodeType * t)
{
    RWFUNCTION(RWSTRING("VrmlNodeType_GetBaseAbstractNode"));
    RWASSERT(t);

    if (t)
    {
        if (t->userProto)
        {
            AbstractNode       *an;

            an = (AbstractNode *) LLinkList_GetItem(&t->aNodeDefs, 0);

            RWRETURN(an);
        }

        RWRETURN((AbstractNode *)NULL);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

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

   TypeList methods

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

/*
 *  Namespace management functions.  PROTO definitions add node types
 *  to the namespace.  PROTO implementations are a separate node
 *  namespace, and require that any nested PROTOs NOT be available
 *  outside the PROTO implementation.
 *  addToNameSpace will print an error to stderr if the given type
 *  is already defined.
*/

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

    LLinkList_Init(&typeList);

    RWRETURNVOID();
}

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

    while (!LLinkList_IsEmpty(&typeList))
    {
        TypeList_popNameSpace();
    }

    RWRETURNVOID();
}

RwBool
TypeList_addToNameSpace(VrmlNodeType * t)
{
    RWFUNCTION(RWSTRING("TypeList_addToNameSpace"));
    RWASSERT(t);

    if (t)
    {
        if (!LLinkList_AddData(&typeList, t))
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

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

/*
 One list is used to store all the node types.  Nested namespaces are
 separated by NULL elements.
 This isn't terribly efficient, but it is nice and simple.
*/
RwBool
TypeList_pushNameSpace(void)
{
    RWFUNCTION(RWSTRING("TypeList_pushNameSpace"));

    if (!LLinkList_AddData(&typeList, NULL))
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

static              RwBool
_popNameSpaceCallBack(LLinkList * list, LLLink * link)
{
    RWFUNCTION(RWSTRING("_popNameSpaceCallBack"));
    RWASSERT(link);

    if (link)
    {
        VrmlNodeType       *t;

        t = (VrmlNodeType *) LLLink_GetData(link);
        LLinkList_RemoveLink(list, link);
        LLLink_Destroy(link);

        if (t)
        {
            /*
             * NOTE:  Instead of just deleting the VrmlNodeTypes, you will
             * probably want to reference count or garbage collect them, since
             * any nodes created as part of the PROTO implementation will
             * probably point back to their VrmlNodeType structure.
             */
            VrmlNodeType_Destroy(t);

            RWRETURN(TRUE);
        }

        RWRETURN(FALSE);
    }

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

RwBool
TypeList_popNameSpace(void)
{
    RWFUNCTION(RWSTRING("TypeList_popNameSpace"));

    if (!LLinkList_ForAllLinksII(&typeList, _popNameSpaceCallBack))
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

static              RwBool
_findCallBack(void *linkData, void *inout)
{
    RWFUNCTION(RWSTRING("_findCallBack"));
    RWASSERT(inout);

    if (inout)
    {
        void              **newInOut = (void **) inout;

        /* can have null linkData */
        if (linkData)
        {
            VrmlNodeType       *t;
            char               *name;

            t = (VrmlNodeType *) linkData;
            name = (RwChar *) newInOut[0];

            if (rwstrcmp(t->name, name) == 0)
            {
                newInOut[1] = t;
                /* early out, we've found what we're looking for */
                RWRETURN(FALSE);
            }
        }

        /* still have'nt fount what were looking for */
        newInOut[1] = NULL;

        RWRETURN(TRUE);
    }

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

VrmlNodeType       *
TypeList_find(const char *name)
{
    RWFUNCTION(RWSTRING("TypeList_find"));
    RWASSERT(name);

    if (name)
    {
        void  *inout[2];

        inout[0] = (void *) name;
        inout[1] = NULL;

        if (LLinkList_ForAllLinkData(&typeList, _findCallBack, inout))
        {
            if (inout[1])
            {
                RWRETURN((VrmlNodeType *) inout[1]);
            }
        }

        RWRETURN((VrmlNodeType *)NULL);
    }

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

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

   NameTypeRec methods

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

static NameTypeRec *
NameTypeRec_Create(const char *name, int type)
{
    RWFUNCTION(RWSTRING("NameTypeRec_Create"));
    RWASSERT(name);

    if (name)
    {

        NameTypeRec        *nr;

        nr = (NameTypeRec *) AbstractField_Create(name, type);

        RWRETURN(nr);
    }

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

static              RwBool
NameTypeRec_Destroy(NameTypeRec * nr)
{
    RWFUNCTION(RWSTRING("NameTypeRec_Destroy"));
    RWASSERT(nr);

    if (nr)
    {
        AbstractField_Destroy((AbstractField *) nr);

        RWRETURN(TRUE);
    }

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