/*
 * Proto.cpp
 *
 * Copyright (C) 1999 Stephen F. White, 2005 J. "MUFTI" Scheurich
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include "stdafx.h"
#include <ctype.h>

#ifndef _WIN32
# include <unistd.h>
# include <fcntl.h>
#endif

#include "resource.h"
#include "Proto.h"
#include "Scene.h"
#include "EventIn.h"
#include "EventOut.h"
#include "Field.h"
#include "FieldValue.h"
#include "ExposedField.h"
#include "Node.h"
#include "NodeTransform.h"

// code for a resuable constructor
void Proto::protoInitializer(Scene *scene, const MyString &name)
{
    _scene = scene;
    _name = name;
    _protoNodes.resize(0);
    _eventIns.resize(0);
    _eventOuts.resize(0);
    _fields.resize(0);
    _exposedFields.resize(0);
    _urls = NULL;
    _loaded = false;
    buildExportNames();
}

Proto::Proto(Scene *scene, const MyString &name)
{
    protoInitializer(scene, name);
    metadata.set(
          addExposedField(SFNODE, "metadata", new SFNode(NULL), METADATA_NODE));
    setFieldFlags(metadata, FF_X3D_ONLY | FF_HIDDEN);
}   

Proto::Proto(Scene *scene, Proto *proto, int extensionFlag)
{
    bool x3d = false;
    int flag = 0;
    const char *extensionName = "";
    if (extensionFlag & FF_X3D_ONLY) { 
        extensionName = "X3d";
        flag = FF_X3D_ONLY;
        x3d = true;
    } else if (extensionFlag & FF_COVER_ONLY) {
        extensionName = "Cover";
        flag = FF_COVER_ONLY;
    } else if (extensionFlag & FF_KAMBI_ONLY) {
        extensionName = "Kambi";
        flag = FF_KAMBI_ONLY;
    }
    int len = 128;
    len += strlen(extensionName) + strlen(proto->getName(x3d));
    char *protoName = new char[len + 1]; 
    int count = 1;
    // search new PROTO name
    do {
        mysnprintf(protoName, len, "%s%s%d", extensionName, 
                   (const char *)proto->getName(x3d), count++);
    } while (scene->getProto(protoName) != NULL);
    protoInitializer(scene, protoName);
    _protoNodes[0] = proto->create(scene);
    _protoNodes[0]->setInsideProto();
    int i;
    for (i = 0; i < _protoNodes[0]->getProto()->getNumEventIns(); i++) {
         EventIn *eventIn = _protoNodes[0]->getProto()->getEventIn(i);
         if (eventIn->getExposedField() == NULL) {
             const MyString name = eventIn->getName(x3d);
             if (avoidElement(eventIn, flag))
                 continue;
             addEventIn(eventIn->getType(), name);
             if (!(eventIn->getFlags() & flag)) {
                 int dstEventIn = proto->lookupEventIn(name, x3d);
                 EventIn *isEvent = _protoNodes[0]->getProto()->getEventIn(i);
                 isEvent->addIs(_protoNodes[0], i, EL_EVENT_IN,
                                this, dstEventIn, EOF_IS);
             }
         } 
    }          
    for (i = 0; i < _protoNodes[0]->getProto()->getNumEventOuts(); i++) {
         EventOut *eventOut = _protoNodes[0]->getProto()->getEventOut(i);
         if (eventOut->getExposedField() == NULL) {
             const MyString name = eventOut->getName(x3d);
             if (avoidElement(eventOut, flag))
                 continue;
             addEventOut(eventOut->getType(), name);
             if (!(eventOut->getFlags() & flag)) {
                 int dstEventOut = proto->lookupEventOut(name, x3d);
                 EventOut *isEvent = _protoNodes[0]->getProto()->getEventOut(i);
                 isEvent->addIs(_protoNodes[0], i, EL_EVENT_OUT,
                                this, dstEventOut, EIF_IS);
             }
         } 
    }          
    for (i = 0; i < _protoNodes[0]->getProto()->getNumFields(); i++) {
         Field *field = _protoNodes[0]->getProto()->getField(i);
         const MyString name = field->getName(x3d);
         if (avoidElement(field, flag))
             continue;
         if (field->getExposedField())
             addExposedField(field->getType(), name, field->getDefault(x3d));
         else
             addField(field->getType(), name, field->getDefault(x3d));
         if (!(field->getFlags() & flag)) {
              int dstField = proto->lookupField(name, x3d);
              Field *isField = _protoNodes[0]->getProto()->getField(i);
              isField->addIs(_protoNodes[0], i, field->getType(),
                             this, dstField);
         } 
    }          
    metadata.set(
          addExposedField(SFNODE, "metadata", new SFNode(NULL), METADATA_NODE));
    setFieldFlags(metadata, FF_X3D_ONLY);
}

Proto::~Proto()
{
    int i;

// the following line would cause a crash when tested with efence
//    for (i = 0; i < _fields.size(); i++) delete _fields[i];
    for (i = 0; i < _eventIns.size(); i++) 
        delete _eventIns[i];
    for (i = 0; i < _eventOuts.size(); i++) 
        delete _eventOuts[i];
    for (i = 0; i < _exposedFields.size(); i++) 
        delete _exposedFields[i];

    for (i = 0; i < _protoNodes.size(); i++) 
        if (_protoNodes[i])
            _protoNodes[i]->unref();
}

bool Proto::avoidElement(Element *element, int flag)
{
    if (flag == FF_X3D_ONLY)
        if (element->getFlags() & FF_COVER_ONLY)
            return true;
    if (flag == FF_X3D_ONLY)
        if (element->getFlags() & FF_KAMBI_ONLY)
            return true;
    if (flag == FF_X3D_ONLY)
        if (element->getFlags() & FF_VRML_ONLY)
            return true;
    if (flag == FF_COVER_ONLY)
        if (element->getFlags() & FF_X3D_ONLY)
            return true;
    if (flag == FF_KAMBI_ONLY)
        if (element->getFlags() & FF_X3D_ONLY)
            return true;
    return false;  
}

Node *
Proto::create(Scene *scene)
{
    return new NodePROTO(scene, this);
}

MyString
Proto::buildCallbackClass(const char *name)
{
    MyString callbackClass = "";
    callbackClass += TheApp->getCPrefix();
    callbackClass += getCName(true);
    callbackClass += name;
    callbackClass += "Callback";
    return callbackClass;
}

MyString
Proto::buildCallbackName(const char *name)
{
    MyString callbackName = "";
    callbackName += TheApp->getCPrefix();
    callbackName += name;
    callbackName += "Callback";
    callbackName += getCName(true);
    return callbackName;
}


void
Proto::buildExportNames()
{
    _cName = "";
    if (isalpha(_name[0]))
        _cName += _name[0];
    else
        _cName += "A";  
    for (int i = 1; i < _name.length(); i++)
        if (isalnum(_name[i]))
            _cName += _name[i];
        else
            _cName += "_";

    _className = "";
    _className += TheApp->getCPrefix();
    _className += getCName(true);

    _renderCallbackClass = buildCallbackClass("Render");
    _renderCallbackName = buildCallbackName("render");

    _treeRenderCallbackClass = buildCallbackClass("TreeRender");
    _treeRenderCallbackName = buildCallbackName("treeRender");

    _doWithDataCallbackClass = buildCallbackClass("DoWithData");
    _doWithDataCallbackName = buildCallbackName("doWithData");

    _treeDoWithDataCallbackClass = buildCallbackClass("TreeDoWithData");
    _treeDoWithDataCallbackName = buildCallbackName("treeDoWithData");

    _eventsProcessedCallbackClass = buildCallbackClass("EventsProcessed");
    _eventsProcessedCallbackName = buildCallbackName("eventsProcessed");
}

void 
Proto::handleVrmlCut(EventOut *event)
{
    if (strcmp(getName(false), "VrmlCut") == 0) {
        if (this != _scene->getProto("VrmlCut")) {
            Proto *vrmlCutProto = _scene->getProto("VrmlCut");
            if (vrmlCutProto != NULL) {
                MyString outName = "";
                outName += event->getName(false);
                EventOut *eventOut = new EventOut(event->getType(), outName);
                vrmlCutProto->addElement(eventOut);
            }
        }
    } 
}

void
Proto::deleteElements(void) 
{
    for (int i = 0; i < _fields.size(); i++)
        if (_fields[i]->getDefault(false)->isDefaultValue())
            _nameOfFieldsWithDefaultValue.append(_fields[i]->getName(false));
    int restSize = 1;
    _fields.resize(restSize);
    _eventIns.resize(restSize);
    _eventOuts.resize(restSize);
    _exposedFields.resize(restSize);
}

int
Proto::addElement(Element *element)
{
    switch( element->getElementType() ) {
      case EL_FIELD:
        {
        Field *field = (Field *) element;
        if (field->getDefault(false) == NULL) {
            swDebugf("ignoring element %s\n", (const char*)field->getName(false));
            return -1; 
        }
        for (int i = 0; i < _nameOfFieldsWithDefaultValue.size(); i++)
            if (field->getName(false) == _nameOfFieldsWithDefaultValue[i])
                field->getDefault(false)->setIsDefaultValue();
        _fields.append(field);
        }
        return _fields.size() - 1;
      case EL_EVENT_IN:
        _eventIns.append((EventIn *) element);
        break;
      case EL_EVENT_OUT:
        handleVrmlCut((EventOut *) element);
        _eventOuts.append((EventOut *) element);
        break;
      case EL_EXPOSED_FIELD:
        return addExposedField((ExposedField *) element);
      default:
        _scene->errorf("internal error:  unexpected element in Proto");
        break;
    }
    return -1;
}

int
Proto::addField(int fieldType, const MyString &name, FieldValue *defaultValue,
                FieldValue *min, FieldValue *max)
{
    _fields.append(new Field(fieldType, name, defaultValue, NULL,
                             min, max, ANY_NODE));
    return _fields.size() - 1;
}

int
Proto::addField(int fieldType, const MyString &name, FieldValue *defaultValue,
                int nodeType)
{
    _fields.append(new Field(fieldType, name, defaultValue, NULL,
                             NULL, NULL, nodeType));
    return _fields.size() - 1;
}

int
Proto::addField(int fieldType, const MyString &name, FieldValue *defaultValue,
                int flags, const char **strings)
{
    _fields.append(new Field(fieldType, name, defaultValue, NULL,
                             NULL, NULL, ANY_NODE, flags, strings));
    return _fields.size() - 1;
}


int
Proto::addField(Field *field)
{
    _fields.append(field);
    return _fields.size() - 1;
}

int
Proto::addEventIn(int fieldType, const MyString &name)
{
    _eventIns.append(new EventIn(fieldType, name));
    return _eventIns.size() - 1;
}

int
Proto::addEventIn(int fieldType, const MyString &name, int flags)
{
    _eventIns.append(new EventIn(fieldType, name, flags));
    return _eventIns.size() - 1;
}

int
Proto::addEventIn(int fieldType, const MyString &name, int flags, int field)
{
    _eventIns.append(new EventIn(fieldType, name, flags));
    _eventIns[_eventIns.size() - 1]->setField(field); 
    _fields[field]->setEventIn(_eventIns.size() - 1);
    return _eventIns.size() - 1;
}

int
Proto::addEventOut(int fieldType, const MyString &name)
{
    _eventOuts.append(new EventOut(fieldType, name));
    return _eventOuts.size() - 1;
}

int
Proto::addEventOut(int fieldType, const MyString &name, int flags)
{
    _eventOuts.append(new EventOut(fieldType, name, flags));
    return _eventOuts.size() - 1;
}

int
Proto::addExposedField(int fieldType, const MyString &name,
                       FieldValue *defaultValue, 
                       FieldValue *min, FieldValue *max, 
                       const MyString &x3dName)
{
    return addExposedField(new ExposedField(fieldType, name, defaultValue, 
                                            min, max, ANY_NODE, 0, 
                                            NULL, x3dName));
}

int
Proto::addExposedField(int fieldType, const MyString &name,
                       FieldValue *defaultValue, int nodeType, 
                       const MyString &x3dName)
{
    return addExposedField(new ExposedField(fieldType, name,
                                            defaultValue, NULL, NULL, 
                                            nodeType, 0, NULL, x3dName));
}

int
Proto::addExposedField(int fieldType, const MyString &name,
                       FieldValue *defaultValue, int flags, 
                       const char **strings)
{
    return addExposedField(new ExposedField(fieldType, name,
                                            defaultValue, NULL, NULL, 
                                            0, flags, strings));
}

int
Proto::addExposedField(int fieldType, const MyString &name, 
                           FieldValue *defaultValue, 
                           FieldValue *min, FieldValue *max,
                           int nodeType, int flags, const char **strings,
                           const MyString &x3dName)
{
    return addExposedField(new ExposedField(fieldType, name,
                                            defaultValue, min, max, 
                                            nodeType, flags, strings, x3dName));
}

int
Proto::addExposedField(ExposedField *exposedField)
{
    const MyString &name = exposedField->getName(false);
    const MyString &x3dName = exposedField->getName(true);
    int type = exposedField->getType();
    FieldValue *value = exposedField->getValue();
    FieldValue *min = exposedField->getMin();
    FieldValue *max = exposedField->getMax();
    int flags = exposedField->getFlags();
    int nodeType = exposedField->getNodeType();
    const char **strings = exposedField->getStrings();

    _exposedFields.append(exposedField);

    // now add a hidden Field with the same name
    _fields.append(new Field(type, name, value, exposedField, min, max, 
                             nodeType, FF_HIDDEN | flags, strings, x3dName));

    exposedField->setField(_fields.size() - 1);

    // now add a hidden EventIn called set_<name>
    char buf[1024];
    mysnprintf(buf, 1024, "set_%s", (const char *) name);
    _eventIns.append(new EventIn(type, buf, flags, exposedField));

    exposedField->setEventIn(_eventIns.size() - 1);
    // now add a hidden EventOut called <name>_changed
    mysnprintf(buf, 1024, "%s_changed", (const char *) name);
    _eventOuts.append(new EventOut(type, buf, flags, exposedField));

    exposedField->setEventOut(_eventOuts.size() - 1);

    return _fields.size() - 1;
}

void
Proto::deleteEventOut(int index)
{
    _eventOuts.remove(index);
}

int
Proto::lookupSimpleEventIn(const MyString &name, bool x3d) const
{
    for (int i = 0; i < _eventIns.size(); i++)
        if (_eventIns[i]->getName(x3d) == name) return i;
    if (_protoNodes.size() > 0)
        _protoNodes[0]->getProto()->lookupSimpleEventIn(name, x3d);
    return INVALID_INDEX;
}

int
Proto::lookupEventIn(const MyString &name, bool x3d) const
{
    int index;

    if ((index = lookupSimpleEventIn(name, x3d)) == INVALID_INDEX)
    {
        // simple search failed; look for an exposedField
        if ((index = lookupExposedField(name, x3d)) != INVALID_INDEX) {

            // now look up the corresponding eventIn
            char buf[1024];
            mysnprintf(buf, 1024, "set_%s", (const char *) name);
            index = lookupSimpleEventIn(buf, x3d);
        }
    }
    return index;
}

int
Proto::lookupSimpleEventOut(const MyString &name, bool x3d) const
{
    for (int i = 0; i < _eventOuts.size(); i++)
        if (_eventOuts[i]->getName(x3d) == name)
            return i;
    if (_protoNodes.size() > 0)
        _protoNodes[0]->getProto()->lookupSimpleEventOut(name, x3d);
    return INVALID_INDEX;
}

int
Proto::lookupEventOut(const MyString &name, bool x3d) const
{
    int index;

    if ((index = lookupSimpleEventOut(name, x3d)) == INVALID_INDEX)
    {
        // simple search failed; look for an exposedField
        if ((index = lookupExposedField(name, x3d)) != INVALID_INDEX) {

            // now look up the corresponding eventOut
            char buf[1024];
            mysnprintf(buf, 1024, "%s_changed", (const char *) name);
            index = lookupSimpleEventOut(buf, x3d);
        }
    }
    return index;
}

int
Proto::lookupField(const MyString &name, bool x3d) const
{
    for (int i = 0; i < _fields.size(); i++)
        if (_fields[i]->getName(x3d) == name)
            return i;
    if (_protoNodes.size() > 0)
        _protoNodes[0]->getProto()->lookupField(name, x3d);
    return INVALID_INDEX;
}

int
Proto::lookupExposedField(const MyString &name, bool x3d) const
{
    for (int i = 0; i < _exposedFields.size(); i++)
        if (_exposedFields[i]->getName(x3d) == name)
            return i;

    return INVALID_INDEX;
}

bool 
Proto::canWriteElement(Element *element, bool x3d) const
{
    if (x3d) {
        if (element->getFlags() & FF_COVER_ONLY)
            return false;
        if (element->getFlags() & FF_KAMBI_ONLY)
            return false;
        if (element->getFlags() & FF_VRML_ONLY)
            return false;
    } else
        if (element->getFlags() & FF_X3D_ONLY)
            return false;
    return true;
}

int 
Proto::writeEvents(int f, int indent, int flags) const
{
    int i;
    for (i = 0; i < _fields.size(); i++)
        if (canWriteElement(_fields[i], isX3d(flags)))
            RET_ONERROR( _fields[i]->write(f, indent, flags) )
    for (i = 0; i < _eventIns.size(); i++)
        if (canWriteElement(_eventIns[i], isX3d(flags)))
            RET_ONERROR( _eventIns[i]->write(f, indent, flags) )
    for (i = 0; i < _eventOuts.size(); i++)
        if (canWriteElement(_eventOuts[i], isX3d(flags)))
            RET_ONERROR( _eventOuts[i]->write(f, indent, flags) )
    for (i = 0; i < _exposedFields.size(); i++)
        if (canWriteElement(_exposedFields[i], isX3d(flags)))
                RET_ONERROR( _exposedFields[i]->write(f, indent, flags) )
    return(0);
}

static bool setIsProtoToBranch(Node *node, void *data)
{
    node->ref();
    node->setInsideProto();
    return true;
}


void
Proto::define(Node *primaryNode, NodeList *nodes)
{
    if (primaryNode == NULL)
        return; 
    _protoNodes[0] = primaryNode;
    for (int i = 0; i < (nodes->size() + 1); i++) {
        if (i > 0)
            _protoNodes[i] = nodes->get(i - 1);
        if (_protoNodes[i] != NULL)
            _protoNodes[i]->doWithBranch(setIsProtoToBranch, NULL);
    }
    setIsNodeIndex();
}

int Proto::write(int f, int indent, int flags) const
{
    int indent2 = indent + TheApp->GetIndent();
    bool x3d = isX3d(flags);
    bool externProto = (_urls != NULL);
    const char *appinfo = getAppinfo();
    const char *documentation = getDocumentation();
    if (isX3dXml(flags)) {
        RET_ONERROR( indentf(f, indent) )
        RET_ONERROR( mywritestr(f, "<") )
        if (externProto)
            RET_ONERROR( mywritestr(f, "Extern") )
        RET_ONERROR( mywritestr(f, "ProtoDeclare name='") )
        RET_ONERROR( mywritestr(f, getName(x3d)) )
        RET_ONERROR( mywritestr(f, "' ") )
        if (strlen(appinfo) > 0) {
            RET_ONERROR( mywritestr(f, " appinfo='") )
            RET_ONERROR( mywritestr(f, appinfo) )
            RET_ONERROR( mywritestr(f, "'") )
        }
        if (strlen(documentation) > 0) {
            RET_ONERROR( mywritestr(f, " documentation='") )
            RET_ONERROR( mywritestr(f, documentation) )
            RET_ONERROR( mywritestr(f, "'") )
        }
        if (externProto) {
            RET_ONERROR( mywritestr(f, " url=") )
            const char *oldBase = _scene->getURL();
            const char *newBase = _scene->getNewURL();
            FieldValue *urls = _urls;
            if (!TheApp->GetKeepURLs())
                urls = rewriteField(_urls, oldBase, newBase);
            RET_ONERROR( urls->writeXml(f, indent2) )
        }
        RET_ONERROR( mywritestr(f, ">\n") )
        TheApp->incSelectionLinenumber();

        RET_ONERROR( indentf(f, indent2) )
        RET_ONERROR( mywritestr(f, "<ProtoInterface>\n") )
        TheApp->incSelectionLinenumber();

        RET_ONERROR( writeEvents(f, indent2, 
                                 flags | (externProto ? WITHOUT_VALUE: 0)) )

        RET_ONERROR( indentf(f, indent2) )
        RET_ONERROR( mywritestr(f, "</ProtoInterface>\n") )
        TheApp->incSelectionLinenumber();

        if (!externProto) {

            RET_ONERROR( indentf(f, indent2) )
            RET_ONERROR( mywritestr(f, "<ProtoBody>\n") )
            TheApp->incSelectionLinenumber();    

            int i;    
            for (i = 0; i < _protoNodes.size(); i++) {
                if (::isX3dXml(flags))
                    RET_ONERROR( _protoNodes.get(i)->writeXml(f, indent2) )
                else
                    RET_ONERROR( _protoNodes.get(i)->write(f, indent2) )
            }
            for (i = 0; i < _protoNodes.size(); i++) 
                RET_ONERROR( _protoNodes.get(i)->writeRoutes(f, indent2) )
            _scene->writeRouteStrings(f, indent, true);

            RET_ONERROR( indentf(f, indent2) )
            RET_ONERROR( mywritestr(f, "</ProtoBody>\n") )
            TheApp->incSelectionLinenumber();    
        }
        RET_ONERROR( indentf(f, indent) )
        RET_ONERROR( mywritestr(f, "</") )
        if (externProto)
            RET_ONERROR( mywritestr(f, "Extern") )
        RET_ONERROR( mywritestr(f, "ProtoDeclare>\n") )
        RET_ONERROR( mywritestr(f, "\n") )
        TheApp->incSelectionLinenumber();
    } else {
        if (strlen(appinfo) > 0) {
            RET_ONERROR( mywritestr(f, "# ") )
            RET_ONERROR( mywritestr(f, getName(x3d)) )
            RET_ONERROR( mywritestr(f, " appinfo='") )
            RET_ONERROR( mywritestr(f, appinfo) )
            RET_ONERROR( mywritestr(f, "'") )
            RET_ONERROR( mywritestr(f, "\n") )
            TheApp->incSelectionLinenumber();
        }
        if (strlen(documentation) > 0) {
            RET_ONERROR( mywritestr(f, "# ") )
            RET_ONERROR( mywritestr(f, getName(x3d)) )
            RET_ONERROR( mywritestr(f, " documentation='") )
            RET_ONERROR( mywritestr(f, documentation) )
            RET_ONERROR( mywritestr(f, "'") )
            RET_ONERROR( mywritestr(f, "\n") )
            TheApp->incSelectionLinenumber();
        }
        RET_ONERROR( indentf(f, indent) )
        if (_urls != NULL)
            RET_ONERROR( mywritestr(f, "EXTERN") )
        RET_ONERROR( mywritestr(f, "PROTO ") )
        RET_ONERROR( mywritestr(f, getName(x3d)) )
        if (!TheApp->GetkrFormating()) {
            RET_ONERROR( mywritestr(f, "\n") )
            TheApp->incSelectionLinenumber();
            RET_ONERROR( indentf(f, indent2) )
        } else
            RET_ONERROR( mywritestr(f, " ") )
        RET_ONERROR( mywritestr(f, "[\n") )
        TheApp->incSelectionLinenumber();
    
        RET_ONERROR( writeEvents(f, indent2, 
                                 flags | (externProto ? WITHOUT_VALUE : 0)) )
    
        if (!TheApp->GetkrFormating()) {
            TheApp->incSelectionLinenumber();
            RET_ONERROR( indentf(f, indent2) )
        } else
            RET_ONERROR( indentf(f, indent) )
    
        RET_ONERROR( mywritestr(f, "]") )

        RET_ONERROR( indentf(f, indent) )
        if (_urls != NULL) {
            const char *oldBase = _scene->getURL();
            const char *newBase = _scene->getNewURL();
            FieldValue *urls = _urls;
            if (!TheApp->GetKeepURLs())
                urls = rewriteField(_urls, oldBase, newBase);
            if (((MFString *)urls)->getSize() == 1) {
                RET_ONERROR( mywritestr(f, "\n") )
                TheApp->incSelectionLinenumber();
            }
            RET_ONERROR( urls->write(f, 0) )
        } else {
            RET_ONERROR( mywritestr(f, "\n") )
            TheApp->incSelectionLinenumber();
            RET_ONERROR( mywritestr(f, "{\n") )
            TheApp->incSelectionLinenumber();
            int i;
    
            for (i = 0; i < _protoNodes.size(); i++) 
                RET_ONERROR( _protoNodes.get(i)->write(f, indent) )
    
            for (i = 0; i < _protoNodes.size(); i++) 
                RET_ONERROR( _protoNodes.get(i)->writeRoutes(f, indent) )
            _scene->writeRouteStrings(f, indent, true);

            RET_ONERROR( indentf(f, indent) )
            RET_ONERROR( mywritestr(f, "}\n") )
            TheApp->incSelectionLinenumber();
        }
        RET_ONERROR( mywritestr(f, "\n") )
        TheApp->incSelectionLinenumber();

    }
    return(0);
}

/*
bool 
Proto::isPROTO()
{
    if (_protoNodes.size() == 0) 
       return false;
    else
       return true;
}
*/

void 
Proto::compareToIsNodes(Node *node)
{
    int i;
    for (i = 0; i < _fields.size(); i++)
        for (int j = 0; j < _fields[i]->getNumIs(); j++)
            if (_fields[i]->getIsNode(j) == node)
                _fields[i]->setIsNodeIndex(j, _nodeIndex);
    for (i = 0; i < _eventIns.size(); i++)
        for (int j = 0; j < _eventIns[i]->getNumIs(); j++)
            if (_eventIns[i]->getIsNode(j) == node)
                _eventIns[i]->setIsNodeIndex(j,_nodeIndex);
    for (i = 0; i < _eventOuts.size(); i++)
        for (int j = 0; j < _eventOuts[i]->getNumIs(); j++)
            if (_eventOuts[i]->getIsNode(j) == node)
                _eventOuts[i]->setIsNodeIndex(j, _nodeIndex);
    for (i = 0; i < _exposedFields.size(); i++)
        for (int j = 0; j < _exposedFields[i]->getNumIs(); j++)
            if (_exposedFields[i]->getIsNode(j) == node)
                _exposedFields[i]->setIsNodeIndex(j, _nodeIndex);
    _nodeIndex++;
}

static bool setIsNodeIndexToBranch(Node *node, void *data)
{
    ((Proto *)data)->compareToIsNodes(node);
    return true;
}

void 
Proto::setIsNodeIndex(void)
{
    _nodeIndex = 0;
    for (int i = 0; i < getNumNodes(); i++)
        if (_protoNodes[i] != NULL)
            _protoNodes[i]->doWithBranch(setIsNodeIndexToBranch, this);
}

int
Proto::lookupIsField(Node *node, int field) const
{
    for (int i = 0; i < _fields.size(); i++)
        for (int j = 0; j < _fields[i]->getNumIs(); j++)
            if (_fields[i]->getIsNode(j) == node)
                if (_fields[i]->getIsField(j) == field)
                    return i;
    return -1;
}

int
Proto::lookupIsEventIn(Node *node, int eventIn, int elementType) const
{
    for (int i = 0; i < _eventIns.size(); i++)
        for (int j = 0; j < _eventIns[i]->getNumIs(); j++)
            if (_eventIns[i]->getIsNode(j) == node)
                if (_eventIns[i]->getIsField(j) == eventIn) {
                    if ((elementType != -1) && 
                        (_eventIns[i]->getIsElementType(j) != elementType))
                        continue;
                    return i;
                }
    return -1;
}

int
Proto::lookupIsEventOut(Node *node, int eventOut, int elementType) const
{
    for (int i = 0; i < _eventOuts.size(); i++)
        for (int j = 0; j < _eventOuts[i]->getNumIs(); j++)
            if (_eventOuts[i]->getIsNode(j) == node) {
                if (_eventOuts[i]->getIsField(j) == eventOut) {
                    if ((elementType != -1) && 
                        (_eventOuts[i]->getIsElementType(j) != elementType))
                        continue;
                    return i;
                }
            }
    return -1;
}


int
Proto::lookupIsExposedField(Node *node, int exposedField) const
{
    for (int i = 0; i < _exposedFields.size(); i++)
        for (int j = 0; j < _exposedFields[i]->getNumIs(); j++)
            if (_exposedFields[i]->getIsNode(j) == node)
                if (_exposedFields[i]->getIsField(j) == exposedField)
                    return i;
    return -1;
}

void
Proto::addURLs(FieldValue *urls)
{
    _urls = urls;
}

bool
Proto::checkIsExtension(Element *element, int flag)
{
    bool ret = false;
    bool x3d = (flag == FF_X3D_ONLY) ? true : false;
    const char* elementName = element->getElementName(false);
    if (element->getNumIs() == 1) {
        Proto *proto = element->getIsNode(0)->getProto();
        const char *isElementName = NULL;
        switch (element->getType()) {
          case EL_FIELD:
            isElementName = proto->getField(element->getIsField(0))->
                            getElementName(x3d);
            break;
          case EL_EVENT_IN:
            isElementName = proto->getEventIn(element->getIsField(0))->
                            getElementName(x3d);
            break;
          case EL_EVENT_OUT:
            isElementName = proto->getEventOut(element->getIsField(0))->
                            getElementName(x3d);
            break;
          case EL_EXPOSED_FIELD:
            isElementName = proto->getExposedField(element->getIsField(0))->
                            getElementName(x3d);
            break;
          default:
            return false;
        }
        if (strcmp(elementName, isElementName) == 0)
            ret = true;
        else
            return false;
    } else if (element->getNumIs() == 0) {
        Proto *proto = _protoNodes[0]->getProto();
        int field = -1;
        switch (element->getType()) {
          case EL_FIELD:
            field = proto->lookupField(elementName, x3d);
            break;
          case EL_EVENT_IN:
            field = proto->lookupEventIn(elementName, x3d);
            break;
          case EL_EVENT_OUT:
            field = proto->lookupEventOut(elementName, x3d);
            break;
          case EL_EXPOSED_FIELD:
            field = proto->lookupExposedField(elementName, x3d);
            break;
          default:
            return false;
        }
        if (field == -1)
            return false;
        if (proto->getField(field)->getFlags() & flag)
            ret = true;
        else
           return false;  
    } else
        return false;
    return ret;
}

bool
Proto::isExtensionProto(int flag)
{
    bool ret = false;
    bool x3d = (flag == FF_X3D_ONLY) ? true : false;
    int i;
    for (i = 0; i < _fields.size(); i++) {
        const char* name = _fields[i]->getName(x3d);
        Element *element = _fields[i];
        ExposedField *exposedField = _fields[i]->getExposedField();
        if (exposedField) 
            element = exposedField;
        if ((element->getNumIs() == 1) && (_protoNodes.size() > 0)) {
            Proto *proto = element->getIsNode(0)->getProto();
            Field *field = proto->getField(element->getIsField(0));
            if (strcmp(name, field->getName(x3d)) == 0)
                ret = true;
            else
                return false;
        } else if (element->getNumIs() == 0) {
            if ((flag == FF_COVER_ONLY) && 
                (_fields[i]->getFlags() & FF_X3D_ONLY))
                continue;
            if ((flag == FF_KAMBI_ONLY) && 
                (_fields[i]->getFlags() & FF_X3D_ONLY))
                continue;
            if (_protoNodes.size() < 1)
                continue;
            if (_protoNodes[0] == NULL)
                continue;
            Proto *proto = _protoNodes[0]->getProto();
            int field = proto->lookupField(name, x3d);
            if (field == -1)
                return false;
            if (proto->getField(field)->getFlags() & flag)
                ret = true;
            else
               return false;  
        } else
            return false;
    }
    for (i = 0; i < _eventIns.size(); i++) {
        const char* name = _eventIns[i]->getName(x3d);
        if (_eventIns[i]->getExposedField()) {
            // already handled 
            continue;
        }
        if (_eventIns[i]->getNumIs() == 1) {
            if ((flag == FF_COVER_ONLY) && 
                (_eventIns[i]->getFlags() & FF_X3D_ONLY))
                continue;
            if ((flag == FF_KAMBI_ONLY) && 
                (_eventIns[i]->getFlags() & FF_X3D_ONLY))
                continue;
            if (_protoNodes.size() < 1)
                continue;
            Proto *proto = _eventIns[i]->getIsNode(0)->getProto();
            EventIn *field = proto->getEventIn(_eventIns[i]->getIsField(0));
            if (strcmp(name, field->getName(x3d)) == 0)
                ret = true;
            else
                return false;
        } else if (_eventIns[i]->getNumIs() == 0) {
            if (_protoNodes.size() < 1)
                continue;
            Proto *proto = _protoNodes[0]->getProto();
            int field = proto->lookupEventIn(name, x3d);
            if (field == -1)
                return false;
            if (proto->getEventIn(field)->getFlags() & flag)
                ret = true;
            else
               return false;  
        } else
            return false;
    }
    for (i = 0; i < _eventOuts.size(); i++) {
        const char* name = _eventOuts[i]->getName(x3d);
        if (_eventOuts[i]->getExposedField()) {
            // already handled 
            continue;
        }
        if (_eventOuts[i]->getNumIs() == 1) {
            Proto *proto = _eventOuts[i]->getIsNode(0)->getProto();
            EventOut *field = proto->getEventOut(_eventOuts[i]->getIsField(0));
            if (strcmp(name, field->getName(x3d)) == 0)
                ret = true;
            else
                return false;
        } else if (_eventOuts[i]->getNumIs() == 0) {
            if ((flag == FF_COVER_ONLY) && 
                (_eventOuts[i]->getFlags() & FF_X3D_ONLY))
                continue;
            if ((flag == FF_KAMBI_ONLY) && 
                (_eventOuts[i]->getFlags() & FF_X3D_ONLY))
                continue;
            if (_protoNodes.size() < 1)
                continue;
            Proto *proto = _protoNodes[0]->getProto();
            int field = proto->lookupEventOut(name, x3d);
            if (field == -1)
                return false;
            if (proto->getEventOut(field)->getFlags() & flag)
                ret = true;
            else
               return false;  
        } else
            return false;
    }
    return ret;
}

bool                  
Proto::isMismatchingProto(void) {
   if (isKambiProto() && !(TheApp->getKambiMode()))
       return true;
   if (isCoverProto() && !(TheApp->getCoverMode()))
       return true;   
   return false;
}


void
Proto::setFieldFlags(FieldIndex index, int flags)
{ 
    Field *field = _fields[index];
    ExposedField *exposedField = field->getExposedField();
    if (exposedField) {
        exposedField->addFlags(flags);
        _eventIns[exposedField->getEventIn()]->addFlags(flags);
        _eventOuts[exposedField->getEventOut()]->addFlags(flags);
    }
    field->addFlags(flags); 
}

int
Proto::writeCDeclaration(int f, int languageFlag)
{
    int i;
    if (_name[0] == '#')
        return 0;
    if (languageFlag & C_SOURCE)
        RET_ONERROR( mywritestr(f, "struct ") )    
    else 
        RET_ONERROR( mywritestr(f, "class ") )
    RET_ONERROR( mywritestr(f, getClassName()) )
    if (languageFlag & CC_SOURCE) {
        RET_ONERROR( mywritestr(f, " : public ") )
        RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
    }
    if (languageFlag & JAVA_SOURCE) {
        RET_ONERROR( mywritestr(f, " extends ") )
        RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
    }
    RET_ONERROR( mywritestr(f, " {\n") )
    if (languageFlag & CC_SOURCE)
         RET_ONERROR( mywritestr(f, "public:\n") )
    // data
    if (languageFlag & C_SOURCE) {
        SFNode node;
        RET_ONERROR( mywritestr(f, "    ") )
        RET_ONERROR( mywritestr(f, node.getTypeC(languageFlag)) )
        RET_ONERROR( mywritestr(f, " m_parent;\n") )
        RET_ONERROR( mywritestr(f, "    ") )
        RET_ONERROR( mywritestr(f, node.getTypeC(languageFlag)) )
        RET_ONERROR( mywritestr(f, " m_protoRoot;\n") )
        RET_ONERROR( mywritestr(f, "    int") )
        RET_ONERROR( mywritestr(f, " m_type;\n") )
    }
    for (i = 0; i < _fields.size(); i++)
        RET_ONERROR( writeCDeclarationField(f, i, languageFlag) )
    if (languageFlag & CC_SOURCE) {
        // callbacks
        RET_ONERROR( mywritestr(f, "\n") )
        RET_ONERROR( mywritestr(f, "    static ") )
        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback renderCallback;\n") )

        RET_ONERROR( mywritestr(f, "    static ") )
        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback treeRenderCallback;\n") )

        RET_ONERROR( mywritestr(f, "    static ") )
        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback doWithDataCallback;\n") )

        RET_ONERROR( mywritestr(f, "    static ") )
        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback treeDoWithDataCallback;\n") )

        RET_ONERROR( mywritestr(f, "    static ") )
        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback eventsProcessedCallback;\n") )

        RET_ONERROR( mywritestr(f, "\n") )
    } else if (languageFlag & JAVA_SOURCE) {        
        // callbacks
        if ((getType() != VRML_COMMENT) && !(isMismatchingProto())) {
            RET_ONERROR( mywritestr(f, "    static ") ) 
            RET_ONERROR( mywritestr(f, getRenderCallbackClass()) )
            RET_ONERROR( mywritestr(f, " ") ) 
            RET_ONERROR( mywritestr(f, getRenderCallbackName()) )
            RET_ONERROR( mywritestr(f, ";\n") ) 
    
            RET_ONERROR( mywritestr(f, "    static void set") ) 
            RET_ONERROR( mywritestr(f, getRenderCallbackClass()) )
            RET_ONERROR( mywritestr(f, "(") ) 
            RET_ONERROR( mywritestr(f, getRenderCallbackClass()) )
            RET_ONERROR( mywritestr(f, " node) {\n") ) 
            RET_ONERROR( mywritestr(f, "        ") ) 
            RET_ONERROR( mywritestr(f, getRenderCallbackName()) )
            RET_ONERROR( mywritestr(f, " = node;\n") ) 
            RET_ONERROR( mywritestr(f, "    }\n\n") ) 

            RET_ONERROR( mywritestr(f, "    static ") ) 
            RET_ONERROR( mywritestr(f, getTreeRenderCallbackClass()) )
            RET_ONERROR( mywritestr(f, " ") ) 
            RET_ONERROR( mywritestr(f, getTreeRenderCallbackName()) )
            RET_ONERROR( mywritestr(f, ";\n") ) 
     
            RET_ONERROR( mywritestr(f, "    static void set") ) 
            RET_ONERROR( mywritestr(f, getTreeRenderCallbackClass()) )
            RET_ONERROR( mywritestr(f, "(") ) 
            RET_ONERROR( mywritestr(f, getTreeRenderCallbackClass()) )
            RET_ONERROR( mywritestr(f, " node) {\n") ) 
            RET_ONERROR( mywritestr(f, "        ") ) 
            RET_ONERROR( mywritestr(f, getTreeRenderCallbackName()) )
            RET_ONERROR( mywritestr(f, " = node;\n") ) 
            RET_ONERROR( mywritestr(f, "    }\n\n") ) 
    
            RET_ONERROR( mywritestr(f, "    static ") ) 
            RET_ONERROR( mywritestr(f, getDoWithDataCallbackClass()) )
            RET_ONERROR( mywritestr(f, " ") ) 
            RET_ONERROR( mywritestr(f, getDoWithDataCallbackName()) )
            RET_ONERROR( mywritestr(f, ";\n") ) 
    
            RET_ONERROR( mywritestr(f, "    static void set") ) 
            RET_ONERROR( mywritestr(f, getDoWithDataCallbackClass()) )
            RET_ONERROR( mywritestr(f, "(") ) 
            RET_ONERROR( mywritestr(f, getDoWithDataCallbackClass()) )
            RET_ONERROR( mywritestr(f, " node) {\n") ) 
            RET_ONERROR( mywritestr(f, "        ") ) 
            RET_ONERROR( mywritestr(f, getDoWithDataCallbackName()) )
            RET_ONERROR( mywritestr(f, " = node;\n") ) 
            RET_ONERROR( mywritestr(f, "    }\n\n") ) 

            RET_ONERROR( mywritestr(f, "    static ") ) 
            RET_ONERROR( mywritestr(f, getTreeDoWithDataCallbackClass()) )
            RET_ONERROR( mywritestr(f, " ") ) 
            RET_ONERROR( mywritestr(f, getTreeDoWithDataCallbackName()) )
            RET_ONERROR( mywritestr(f, ";\n") ) 
    
            RET_ONERROR( mywritestr(f, "    static void set") ) 
            RET_ONERROR( mywritestr(f, getTreeDoWithDataCallbackClass()) )
            RET_ONERROR( mywritestr(f, "(") ) 
            RET_ONERROR( mywritestr(f, getTreeDoWithDataCallbackClass()) )
            RET_ONERROR( mywritestr(f, " node) {\n") ) 
            RET_ONERROR( mywritestr(f, "        ") ) 
            RET_ONERROR( mywritestr(f, getTreeDoWithDataCallbackName()) )
            RET_ONERROR( mywritestr(f, " = node;\n\n") ) 
            RET_ONERROR( mywritestr(f, "    }\n\n") ) 

            RET_ONERROR( mywritestr(f, "    static ") ) 
            RET_ONERROR( mywritestr(f, getEventsProcessedCallbackClass()) )
            RET_ONERROR( mywritestr(f, " ") ) 
            RET_ONERROR( mywritestr(f, getEventsProcessedCallbackName()) )
            RET_ONERROR( mywritestr(f, ";\n") ) 

            RET_ONERROR( mywritestr(f, "    static void set") ) 
            RET_ONERROR( mywritestr(f, getEventsProcessedCallbackClass()) )
            RET_ONERROR( mywritestr(f, "(") ) 
            RET_ONERROR( mywritestr(f, getEventsProcessedCallbackClass()) )
            RET_ONERROR( mywritestr(f, " node) {\n") ) 
            RET_ONERROR( mywritestr(f, "        ") ) 
            RET_ONERROR( mywritestr(f, getEventsProcessedCallbackName()) )
            RET_ONERROR( mywritestr(f, " = node;\n") ) 
            RET_ONERROR( mywritestr(f, "    }\n\n") ) 
        }
    }

    if (languageFlag & C_SOURCE) {
        RET_ONERROR( mywritestr(f, "}") )
        RET_ONERROR( mywritestr(f, ";") )
        RET_ONERROR( mywritestr(f, "\n\n") )
    }
    if (!(languageFlag & C_SOURCE))
        RET_ONERROR( mywritestr(f, "    ") )
    if (languageFlag & C_SOURCE)
        RET_ONERROR( mywritestr(f, "void ") )
    // constructor
    RET_ONERROR( mywritestr(f, getClassName()) )
    if (languageFlag & C_SOURCE)
        RET_ONERROR( mywritestr(f, "Init") )
    RET_ONERROR( mywritestr(f, "(") )
    if (languageFlag & C_SOURCE) {
        RET_ONERROR( mywritestr(f, "struct ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "* self") )
    }
    RET_ONERROR( mywritestr(f, ") {\n") )
    if (languageFlag & C_SOURCE) {
        RET_ONERROR( mywritestr(f, "    self->m_protoRoot = NULL;\n") )
    }
    if (!(languageFlag & JAVA_SOURCE))
        for (i = 0; i < _fields.size(); i++)
            RET_ONERROR( writeCConstructorField(f, i, languageFlag) )
    if (!(languageFlag & C_SOURCE))
        RET_ONERROR( mywritestr(f, "    ") )
    RET_ONERROR( mywritestr(f, "}\n") )
    if (languageFlag & C_SOURCE)
        RET_ONERROR( mywritestr(f, "\n") )
 
    if (languageFlag & C_SOURCE) {
        // callback declarations
        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getRenderCallbackClass()) )
        RET_ONERROR( mywritestr(f, " = NULL;\n") )

        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getTreeRenderCallbackClass()) )
        RET_ONERROR( mywritestr(f, " = NULL;\n") )

        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getDoWithDataCallbackClass()) )
        RET_ONERROR( mywritestr(f, " = NULL;\n") )

        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getTreeDoWithDataCallbackClass()) )
        RET_ONERROR( mywritestr(f, " = NULL;\n\n") )

        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getEventsProcessedCallbackClass()) )
        RET_ONERROR( mywritestr(f, " = NULL;\n\n") )

        // functions
        RET_ONERROR( mywritestr(f, "void ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "Render(") )
        RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
        RET_ONERROR( mywritestr(f, "* self, void *dataptr) {") )
        RET_ONERROR( mywritestr(f, "\n") )
        RET_ONERROR( mywritestr(f, "    if (") )
        RET_ONERROR( mywritestr(f, getRenderCallbackClass()) )
        RET_ONERROR( mywritestr(f, ")\n") )
        RET_ONERROR( mywritestr(f, "        ") )
        RET_ONERROR( mywritestr(f, getRenderCallbackClass()) )
        RET_ONERROR( mywritestr(f, "(self, dataptr);\n") )
        RET_ONERROR( mywritestr(f, "}\n") )

        RET_ONERROR( mywritestr(f, "void ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "TreeRender(") )
        RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
        RET_ONERROR( mywritestr(f, "* self, void *dataptr)") )
        RET_ONERROR( mywritestr(f, " {\n") )
        RET_ONERROR( mywritestr(f, "    int i;\n") )
        RET_ONERROR( mywritestr(f, "    if (") )
        RET_ONERROR( mywritestr(f, "((struct ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "*)self)->m_protoRoot != NULL)\n") )
        RET_ONERROR( mywritestr(f, "        ") )
        RET_ONERROR( mywritestr(f, "treeRenderCallback(((struct ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "*)self)->m_protoRoot, dataptr);\n") )
        RET_ONERROR( mywritestr(f, "    else {\n") )

        for (i = 0; i < _fields.size(); i++)
            RET_ONERROR( writeCCallTreeField(f, i, "treeRenderCallback", languageFlag) )

        RET_ONERROR( mywritestr(f, "        ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "Render(self, dataptr);\n") )

        RET_ONERROR( mywritestr(f, "    }\n") )
        RET_ONERROR( mywritestr(f, "}\n") )

        // functions
        RET_ONERROR( mywritestr(f, "void ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "DoWithData(") )
        RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
        RET_ONERROR( mywritestr(f, "* self, void *dataptr)") )
        RET_ONERROR( mywritestr(f, " {\n") )
        RET_ONERROR( mywritestr(f, "    if (") )
        RET_ONERROR( mywritestr(f, getDoWithDataCallbackClass()) )
        RET_ONERROR( mywritestr(f, ")\n") )
        RET_ONERROR( mywritestr(f, "        ") )
        RET_ONERROR( mywritestr(f, getDoWithDataCallbackClass()) )
        RET_ONERROR( mywritestr(f, "(self, dataptr);\n") )
        RET_ONERROR( mywritestr(f, "}\n") )

        RET_ONERROR( mywritestr(f, "void ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "TreeDoWithData(") )
        RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
        RET_ONERROR( mywritestr(f, "* self, ") )
        RET_ONERROR( mywritestr(f, "void *dataptr)") )
        RET_ONERROR( mywritestr(f, " {\n") )
        RET_ONERROR( mywritestr(f, "    int i;\n") )

        RET_ONERROR( mywritestr(f, "    if (") )
        RET_ONERROR( mywritestr(f, "((struct ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "*)self)->m_protoRoot != NULL)\n") )
        RET_ONERROR( mywritestr(f, "        ") )
        RET_ONERROR( mywritestr(f, "treeDoWithDataCallback(((struct ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "*)self)->m_protoRoot, dataptr);\n") )
        RET_ONERROR( mywritestr(f, "    else {\n") )

        for (i = 0; i < _fields.size(); i++)
            RET_ONERROR( writeCCallTreeField(f, i, "treeDoWithDataCallback", languageFlag) )

        RET_ONERROR( mywritestr(f, "        ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "DoWithData(self, dataptr);\n") )

        RET_ONERROR( mywritestr(f, "    }\n") )
        RET_ONERROR( mywritestr(f, "}\n") )

        RET_ONERROR( mywritestr(f, "void ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "TreeEventsProcessed(") )
        RET_ONERROR( mywritestr(f, TheApp->getCNodeName()) ) 
        RET_ONERROR( mywritestr(f, "* self, ") )
        RET_ONERROR( mywritestr(f, "void *dataptr)") )
        RET_ONERROR( mywritestr(f, " {\n") )
        RET_ONERROR( mywritestr(f, "    int i;\n") )

        RET_ONERROR( mywritestr(f, "    if (") )
        RET_ONERROR( mywritestr(f, "((struct ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "*)self)->m_protoRoot != NULL)\n") )
        RET_ONERROR( mywritestr(f, "        ") )
        RET_ONERROR( mywritestr(f, "treeEventsProcessed(((struct ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "*)self)->m_protoRoot, dataptr);\n") )
        RET_ONERROR( mywritestr(f, "    else {\n") )

        for (i = 0; i < _fields.size(); i++)
            RET_ONERROR( writeCCallTreeField(f, i, "treeEventsProcessed",
                                             languageFlag) )

        RET_ONERROR( mywritestr(f, "        if (") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "EventsProcessedCallback != NULL)\n") )
        RET_ONERROR( mywritestr(f, "            ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "EventsProcessedCallback(self, dataptr);\n") )

        RET_ONERROR( mywritestr(f, "    }\n") )
        RET_ONERROR( mywritestr(f, "}\n") )
    }
    if (languageFlag & CC_SOURCE) {
        // memberfunctions
        RET_ONERROR( mywritestr(f, "    virtual void treeRender(void *dataptr) {\n") )
        RET_ONERROR( mywritestr(f, "        int i;\n") )

        RET_ONERROR( mywritestr(f, "        if (treeRenderCallback) {\n") )
        RET_ONERROR( mywritestr(f, "            treeRenderCallback(this, ") )
        RET_ONERROR( mywritestr(f, "dataptr);\n") )
        RET_ONERROR( mywritestr(f, "            return;\n") )
        RET_ONERROR( mywritestr(f, "        }\n") )

        RET_ONERROR( mywritestr(f, "        if (m_protoRoot != NULL)\n") )
        RET_ONERROR( mywritestr(f, "            m_protoRoot->") )
        RET_ONERROR( mywritestr(f, "treeRender(dataptr);\n") )
        RET_ONERROR( mywritestr(f, "        else {\n") )

        for (i = 0; i < _fields.size(); i++)
            RET_ONERROR( writeCCallTreeField(f, i, "treeRender", languageFlag) )

        RET_ONERROR( mywritestr(f, "            render(dataptr);\n") )
        RET_ONERROR( mywritestr(f, "        }\n") )

        RET_ONERROR( mywritestr(f, "    }\n") )

        RET_ONERROR( mywritestr(f, "    virtual void render(void *dataptr) {\n") )
        RET_ONERROR( mywritestr(f, "        if (renderCallback)\n") )
        RET_ONERROR( mywritestr(f, "            renderCallback(this, dataptr);") )
        RET_ONERROR( mywritestr(f, "\n    }\n") )

        RET_ONERROR( mywritestr(f, "    virtual void treeDoWithData(void *dataptr)") )
        RET_ONERROR( mywritestr(f, " {\n") )
        RET_ONERROR( mywritestr(f, "        int i;\n") )

        RET_ONERROR( mywritestr(f, "        if (treeDoWithDataCallback) {\n") )
        RET_ONERROR( mywritestr(f, "            treeDoWithDataCallback(this, ") )
        RET_ONERROR( mywritestr(f, "dataptr);\n") )
        RET_ONERROR( mywritestr(f, "            return;\n") )
        RET_ONERROR( mywritestr(f, "        }\n") )


        RET_ONERROR( mywritestr(f, "        if (m_protoRoot != NULL)\n") )
        RET_ONERROR( mywritestr(f, "            m_protoRoot->") )
        RET_ONERROR( mywritestr(f, "treeDoWithData(dataptr);\n") )
        RET_ONERROR( mywritestr(f, "        else {\n") )

        for (i = 0; i < _fields.size(); i++)
            RET_ONERROR( writeCCallTreeField(f, i, "treeDoWithData", 
                                             languageFlag) )

        RET_ONERROR( mywritestr(f, "            doWithData(dataptr);\n") )

        RET_ONERROR( mywritestr(f, "        }\n") )
        RET_ONERROR( mywritestr(f, "    }\n") )

        RET_ONERROR( mywritestr(f, "    virtual void doWithData(void *dataptr)") )
        RET_ONERROR( mywritestr(f, " {\n") )
        RET_ONERROR( mywritestr(f, "        if (doWithDataCallback)\n") )
        RET_ONERROR( mywritestr(f, "            doWithDataCallback(this, dataptr);" ) )
        RET_ONERROR( mywritestr(f, "\n") )
        RET_ONERROR( mywritestr(f, "    }\n") )
    
        RET_ONERROR( mywritestr(f, "}") )
        RET_ONERROR( mywritestr(f, ";") )
        RET_ONERROR( mywritestr(f, "\n\n") )

        // callbacks
        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "::renderCallback = NULL;\n") )

        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "::treeRenderCallback = NULL;\n") )

        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "::doWithDataCallback = NULL;\n") )

        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "::treeDoWithDataCallback = NULL;\n") )

        RET_ONERROR( mywritestr(f, TheApp->getCPrefix()) )
        RET_ONERROR( mywritestr(f, "Callback ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "::eventsProcessedCallback = NULL;\n") )
    }
    if (languageFlag & JAVA_SOURCE) {
        // memberfunctions
        RET_ONERROR( mywritestr(f, "    void treeRender() {\n") )
        RET_ONERROR( mywritestr(f, "        if (") )
        RET_ONERROR( mywritestr(f, getClassName()) ) 
        RET_ONERROR( mywritestr(f, ".") )
        RET_ONERROR( mywritestr(f, getTreeRenderCallbackName()) )
        RET_ONERROR( mywritestr(f, " != null) {\n") )
        RET_ONERROR( mywritestr(f, "            ") )
        RET_ONERROR( mywritestr(f, getClassName()) ) 
        RET_ONERROR( mywritestr(f, ".") )
        RET_ONERROR( mywritestr(f, getTreeRenderCallbackName()) )
        RET_ONERROR( mywritestr(f, ".treeRender(this);\n") )
        RET_ONERROR( mywritestr(f, "            return;\n") )
        RET_ONERROR( mywritestr(f, "        }\n") )
        for (i = 0; i < _fields.size(); i++)
            RET_ONERROR( writeCCallTreeField(f, i, "treeRender", languageFlag) )
        RET_ONERROR( mywritestr(f, "        if (m_protoRoot != null)\n") )
        RET_ONERROR( mywritestr(f, "            m_protoRoot.") )
        RET_ONERROR( mywritestr(f, "treeRender();\n") )
        RET_ONERROR( mywritestr(f, "        if (") )
        RET_ONERROR( mywritestr(f, getClassName()) ) 
        RET_ONERROR( mywritestr(f, ".") )
        RET_ONERROR( mywritestr(f, getRenderCallbackName()) )
        RET_ONERROR( mywritestr(f, " != null)\n") )
        RET_ONERROR( mywritestr(f, "            ") )
        RET_ONERROR( mywritestr(f, getClassName()) ) 
        RET_ONERROR( mywritestr(f, ".") )
        RET_ONERROR( mywritestr(f, getRenderCallbackName()) )
        RET_ONERROR( mywritestr(f, ".render(this);\n") )
        RET_ONERROR( mywritestr(f, "    }\n") )

        RET_ONERROR( mywritestr(f, "    void treeDoWithData() {\n") )
        RET_ONERROR( mywritestr(f, "        if (") )
        RET_ONERROR( mywritestr(f, getClassName()) ) 
        RET_ONERROR( mywritestr(f, ".") )
        RET_ONERROR( mywritestr(f, getTreeDoWithDataCallbackName()) )
        RET_ONERROR( mywritestr(f, " != null) {\n") )
        RET_ONERROR( mywritestr(f, "            ") )
        RET_ONERROR( mywritestr(f, getClassName()) ) 
        RET_ONERROR( mywritestr(f, ".") )
        RET_ONERROR( mywritestr(f, getTreeDoWithDataCallbackName()) )
        RET_ONERROR( mywritestr(f, ".treeDoWithData(this);\n") )
        RET_ONERROR( mywritestr(f, "            return;\n") )
        RET_ONERROR( mywritestr(f, "        }\n") )
        for (i = 0; i < _fields.size(); i++)
            RET_ONERROR( writeCCallTreeField(f, i, "treeDoWithData", 
                                             languageFlag) )
        RET_ONERROR( mywritestr(f, "        if (m_protoRoot != null)\n") )
        RET_ONERROR( mywritestr(f, "            m_protoRoot.") )
        RET_ONERROR( mywritestr(f, "treeDoWithData();\n") )
        RET_ONERROR( mywritestr(f, "        if (") )
        RET_ONERROR( mywritestr(f, getClassName()) ) 
        RET_ONERROR( mywritestr(f, ".") )
        RET_ONERROR( mywritestr(f, getDoWithDataCallbackName()) )
        RET_ONERROR( mywritestr(f, " != null)\n") )
        RET_ONERROR( mywritestr(f, "            ") )
        RET_ONERROR( mywritestr(f, getClassName()) ) 
        RET_ONERROR( mywritestr(f, ".") )
        RET_ONERROR( mywritestr(f, getDoWithDataCallbackName()) )
        RET_ONERROR( mywritestr(f, ".doWithData(this);\n") )
        RET_ONERROR( mywritestr(f, "    }\n") )

        RET_ONERROR( mywritestr(f, "}") )
    }

    RET_ONERROR( mywritestr(f, "\n\n") )

    if (languageFlag & JAVA_SOURCE) {
         // "callbacks"
         RET_ONERROR( mywritestr(f, "class ") )
         RET_ONERROR( mywritestr(f, getRenderCallbackClass()) )
         RET_ONERROR( mywritestr(f, " extends ") )
         RET_ONERROR( mywritestr(f, "RenderCallback {}\n") )

         RET_ONERROR( mywritestr(f, "class ") )
         RET_ONERROR( mywritestr(f, getTreeRenderCallbackClass()) )
         RET_ONERROR( mywritestr(f, " extends ") )
         RET_ONERROR( mywritestr(f, "TreeRenderCallback {}\n") )

         RET_ONERROR( mywritestr(f, "class ") )
         RET_ONERROR( mywritestr(f, getDoWithDataCallbackClass()) )
         RET_ONERROR( mywritestr(f, " extends") )
         RET_ONERROR( mywritestr(f, " DoWithDataCallback {};\n") )

         RET_ONERROR( mywritestr(f, "class ") )
         RET_ONERROR( mywritestr(f, getTreeDoWithDataCallbackClass()) )
         RET_ONERROR( mywritestr(f, " extends") )
         RET_ONERROR( mywritestr(f, " TreeDoWithDataCallback {};\n") )

         RET_ONERROR( mywritestr(f, "class ") )
         RET_ONERROR( mywritestr(f, getEventsProcessedCallbackClass()) )
         RET_ONERROR( mywritestr(f, " extends") )
         RET_ONERROR( mywritestr(f, " EventsProcessedCallback {};\n") )

         RET_ONERROR( mywritestr(f, "\n\n") )
    }
    return 0;
}

int 
Proto::writeCDeclarationField(int f, int i, int languageFlag)
{
    bool x3d = true;

    Field *field = getField(i);
    if (field->getFlags() & FF_VRML_ONLY)
        return 0;
    const char *name = field->getName(x3d);
    FieldValue *value = field->getDefault(x3d);
    RET_ONERROR( mywritestr(f, "    ") )
    RET_ONERROR( mywritestr(f, field->getDefault(x3d)->getTypeC(languageFlag)) )
    if (value->isArrayInC())
        if (languageFlag & JAVA_SOURCE) {
            if ((field->getType() != SFNODE) && (field->getType() != MFNODE))
                RET_ONERROR( mywritestr(f, "[]") )
        } else 
            RET_ONERROR( mywritestr(f, "*") )
    RET_ONERROR( mywritestr(f, " ") )
    RET_ONERROR( mywritestr(f, name) )
    RET_ONERROR( mywritestr(f, ";\n") )
    if (value->isArrayInC() && (!(languageFlag & JAVA_SOURCE))) {
        RET_ONERROR( mywritestr(f, "    int ") )
        RET_ONERROR( mywritestr(f, name) )
        RET_ONERROR( mywritestr(f, "_length") )
        RET_ONERROR( mywritestr(f, ";\n") )
    }
    return(0);
}

int 
Proto::writeCConstructorField(int f, int i, int languageFlag)
{
    bool x3d = true;

    Field *field = getField(i);
    if (field->getFlags() & FF_VRML_ONLY)
        return 0;
    FieldValue *value = field->getDefault(x3d);
    const char *name = field->getName(x3d);
    if (!(languageFlag & C_SOURCE))
        RET_ONERROR( mywritestr(f, "    ") )
    RET_ONERROR( mywritestr(f, "    ") )
    if (languageFlag & C_SOURCE)
        RET_ONERROR( mywritestr(f, "self->") )
    RET_ONERROR( mywritestr(f, name) )
    RET_ONERROR( mywritestr(f, " = ") )
    RET_ONERROR( mywritestr(f, value->getDefaultC(languageFlag)) )
    RET_ONERROR( mywritestr(f, ";\n") )
    if (value->isArrayInC() && (!(languageFlag & JAVA_SOURCE))) {
        if (!(languageFlag & C_SOURCE))
            RET_ONERROR( mywritestr(f, "    ") )
        RET_ONERROR( mywritestr(f, "    ") )
        if (languageFlag & C_SOURCE)
            RET_ONERROR( mywritestr(f, "self->") )
        RET_ONERROR( mywritestr(f, name) )
        RET_ONERROR( mywritestr(f, "_length") )
        RET_ONERROR( mywritestr(f, " = 0;\n") )
    }
    return(0);
}

int 
Proto::writeCCallTreeField(int f, int i, const char *functionName, 
                           int languageFlag)
{
    bool x3d = true;

    Field *field = getField(i);
    if ((field->getType() != SFNODE) && (field->getType() != MFNODE))
        return 0; 
    if (field->getFlags() & FF_VRML_ONLY)
        return 0;
    FieldValue *value = field->getDefault(x3d);
    const char *name = field->getName(x3d);
    MyString node = "";
    node += name;
    if (value->isArrayInC()) {
        if (languageFlag & CC_SOURCE)
            RET_ONERROR( mywritestr(f, "    ") )
        RET_ONERROR( mywritestr(f, "    ") )
        RET_ONERROR( mywritestr(f, "    ") )
        RET_ONERROR( mywritestr(f, "if (") )
        if (languageFlag & C_SOURCE) {
            RET_ONERROR( mywritestr(f, "((struct ") )
            RET_ONERROR( mywritestr(f, getClassName()) )
            RET_ONERROR( mywritestr(f, "*)self)->") )
        }
        RET_ONERROR( mywritestr(f, node) )
        if (languageFlag & JAVA_SOURCE)
            RET_ONERROR( mywritestr(f, " != null") )
        RET_ONERROR( mywritestr(f, ")\n") )

        if (languageFlag & CC_SOURCE)
            RET_ONERROR( mywritestr(f, "    ") )
        RET_ONERROR( mywritestr(f, "    ") )
        RET_ONERROR( mywritestr(f, "        ") )
        RET_ONERROR( mywritestr(f, "for (") )
        if (languageFlag & JAVA_SOURCE)
            RET_ONERROR( mywritestr(f, "int ") )
        RET_ONERROR( mywritestr(f, "i = 0; i < ") )
        if (languageFlag & C_SOURCE) {
            RET_ONERROR( mywritestr(f, "((struct ") )
            RET_ONERROR( mywritestr(f, getClassName()) )
            RET_ONERROR( mywritestr(f, "*)self)->") )
        }
        RET_ONERROR( mywritestr(f, name) )
        if (languageFlag & JAVA_SOURCE)
            RET_ONERROR( mywritestr(f, ".") )
        else
            RET_ONERROR( mywritestr(f, "_") )
        RET_ONERROR( mywritestr(f, "length; i++)\n") )
    }
    if (value->isArrayInC())
        node += "[i]";
    if (value->isArrayInC())
        RET_ONERROR( mywritestr(f, "        ") )
    if (languageFlag & CC_SOURCE)
        RET_ONERROR( mywritestr(f, "    ") )
    RET_ONERROR( mywritestr(f, "    ") )
    RET_ONERROR( mywritestr(f, "    ") )
    RET_ONERROR( mywritestr(f, "if (") )
    if (languageFlag & C_SOURCE) {
        RET_ONERROR( mywritestr(f, "((struct ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "*)self)->") )
    }
    RET_ONERROR( mywritestr(f, node) )
    if (languageFlag & JAVA_SOURCE)
        RET_ONERROR( mywritestr(f, " != null") )
    RET_ONERROR( mywritestr(f, ")\n") )
    if (value->isArrayInC())
        RET_ONERROR( mywritestr(f, "        ") )
    RET_ONERROR( mywritestr(f, "    ") )
    if (languageFlag & CC_SOURCE)
        RET_ONERROR( mywritestr(f, "    ") )
    RET_ONERROR( mywritestr(f, "    ") )
    RET_ONERROR( mywritestr(f, "    ") )
    if (languageFlag & C_SOURCE) {
        RET_ONERROR( mywritestr(f, functionName) )
        RET_ONERROR( mywritestr(f, "(((struct ") )
        RET_ONERROR( mywritestr(f, getClassName()) )
        RET_ONERROR( mywritestr(f, "*)self)->") )
        RET_ONERROR( mywritestr(f, node) )
        RET_ONERROR( mywritestr(f, ", dataptr);\n") )
    } else if (languageFlag & CC_SOURCE) {
        RET_ONERROR( mywritestr(f, node) )
        RET_ONERROR( mywritestr(f, "->") )
        RET_ONERROR( mywritestr(f, functionName) )
        RET_ONERROR( mywritestr(f, "(dataptr);\n") )
    } else if (languageFlag & JAVA_SOURCE) {
        RET_ONERROR( mywritestr(f, node) )
        RET_ONERROR( mywritestr(f, ".") )
        RET_ONERROR( mywritestr(f, functionName) )
        RET_ONERROR( mywritestr(f, "();\n") )
    }
    return(0);
}

bool 
Proto::matchNodeClass(int childType) const
{
    return ::matchNodeClass(getNodeClass(), childType);
}

void NodePROTO::appendToIndexedNodes(Node *node)
{
    if (node != NULL)
        _indexedNodes.append(node);
}

static bool buildNodeIndexInBranch(Node *node, void *data)
{
    NodePROTO *self = (NodePROTO *)data;
    self->appendToIndexedNodes(node);
    node->ref();
    node->setNodePROTO(self);
    return true;
}

void
NodePROTO::createPROTO(void)
{
    bool x3d = _scene->isX3d();
 
    _isHumanoid = false;
    int i;
    _nodes.resize(0);
    for (i = 0; i < _proto->getNumNodes(); i++) {
        if (_proto->getNode(i) != NULL) {
            _nodes[i] = _proto->getNode(i)->copy();   
            _proto->getNode(i)->copyChildrenTo(_nodes[i]);
        }
    }
    _indexedNodes.resize(0);
    for (i = 0; i < _proto->getNumNodes(); i++)
        if (_nodes[i] != NULL)
            _nodes[i]->doWithBranch(buildNodeIndexInBranch, this);
    for (i = 0; i < _indexedNodes.size(); i++)
        if (_indexedNodes[i] != NULL)
            _indexedNodes[i]->setScene(_scene);
    for (i = 0; i < _proto->getNumExposedFields(); i++) {
        ExposedField *field = _proto->getExposedField(i);
        if (field->getFlags() & FF_IS)
            for (int j = 0; j < field->getNumIs(); j++) {
                int fieldIndex = field->getIsNodeIndex(j);
                if (fieldIndex > 0) {
                    Node *isNode = getIsNode(fieldIndex);
                    FieldValue *value = field->getValue()->copy();
                    isNode->setField(field->getIsField(j), value);
                }
            }
    }
    _jointRotationField = -1;
    bool maybeJoint = false;
    if (strcmp(_proto->getName(x3d), "Joint") == 0)
        if (_nodes.size() > 0)
            if (_nodes[0] != NULL)
                if (_nodes[0]->getType() == VRML_TRANSFORM)
                    maybeJoint = true;
    if (strcmp(_proto->getName(x3d), "Humanoid") == 0)
        if (_nodes.size() > 0)
            if (_nodes[0] != NULL)
                if (_nodes[0]->getType() == VRML_TRANSFORM)
                    _isHumanoid = true;
    for (i = 0; i < _proto->getNumFields(); i++) {
        Field *field = _proto->getField(i);
        if (field->getFlags() & FF_IS)
            for (int j = 0; j < field->getNumIs(); j++) {
                int fieldIndex = field->getIsNodeIndex(j);
                if (fieldIndex > 0) {
                    Node *isNode = getIsNode(fieldIndex);
                    FieldValue *value = field->getDefault(x3d);
                    if (value && isNode) {
                        value = value->copy();
                        isNode->setField(field->getIsField(j), value);
                    }
                }
            }
        // search for Joint.rotation field for H-Anim handle
        if (maybeJoint)
            if (strcmp(field->getName(false), "rotation") == 0)
                if (field->getType() == SFROTATION) {
                    _jointRotationField = i;         
                    _scene->setHasJoints();
                }                      
    }    
}

NodePROTO::NodePROTO(Scene *scene, Proto *proto)
  : Node(scene, proto)
{
    createPROTO();
}

bool
NodePROTO::isEXTERNPROTO(void)
{
    if (isPROTO())
        if (_proto->isExternProto())
            return true;
    return false;
}

static bool updateNode(Node *node, void *data)
{
    node->update();
    return true;
}


void
NodePROTO::update()
{
    for (int i = 0; i < _proto->getNumNodes(); i++) {
        if (_proto->getNode(i) != NULL)
            _proto->getNode(i)->doWithBranch(updateNode, NULL);
    }
}

static bool reInitNode(Node *node, void *data)
{
    node->reInit();
    return true;
}


void
NodePROTO::reInit()
{
    bool x3d = _scene->isX3d();
    int i;
    for (i = 0; i < _proto->getNumFields(); i++) { 
         FieldValue *fieldValue = getField(i);
         if (fieldValue == NULL)
             continue;
         if (fieldValue->isDefaultValue())
             fieldValue = _proto->getField(i)->getDefault(x3d);
         fieldValue = newFieldValue(_proto->getField(i)->getType(), fieldValue);
         _scene->setField(this, i, fieldValue);
    }
    for (i = 0; i < _proto->getNumNodes(); i++) {
        if (_proto->getNode(i) != NULL)
            _proto->getNode(i)->doWithBranch(reInitNode, NULL);
    }
}

Node *
NodePROTO::getIsNode(int nodeIndex)
{
    if (nodeIndex >= _indexedNodes.size()) {
        swDebugf("Bug: Internal parse error: nodeIndex >= _indexedNodes.size()\n");
        return NULL;
    }
    return _indexedNodes[nodeIndex];
}


void
NodePROTO::preDraw()
{
    if (_indexedNodes.size() > 0)
        if (_indexedNodes[0] != NULL)
            _indexedNodes[0]->preDraw();
}

void
NodePROTO::draw()
{
    if (_indexedNodes.size() > 0)
        if (_indexedNodes[0] != NULL)
            _indexedNodes[0]->draw();
}
  
void
NodePROTO::draw(int pass)
{
    if (_indexedNodes.size() > 0)
        if (_indexedNodes[0] != NULL)
            _indexedNodes[0]->draw(pass);
}   

int
NodePROTO::getType() const 
{ 
    if (_indexedNodes.size() > 0)
        if (_indexedNodes[0] != NULL)
            return _indexedNodes[0]->getType();
    return -1; 
}

int 
NodePROTO::getNodeClass() const 
{ 
    if (_indexedNodes.size() > 0)
        if (_indexedNodes[0] != NULL)
            return _indexedNodes[0]->getNodeClass() | PROTO_NODE;
    return Node::getNodeClass() | PROTO_NODE;
}

FieldValue *
NodePROTO::getField(int index) const
{
    return Node::getField(index);
}

void
NodePROTO::setField(int index, FieldValue *value)
{
    Node::setField(index, value);
    value->removeIsDefaultValue();
}

FieldValue *
NodePROTO::getField(FieldIndex index) const
{
    if (_nodes.size() > 0)
        if (_nodes[0] != NULL)
            return _nodes[0]->getField(index);
    return NULL;
}

void
NodePROTO::setField(FieldIndex index, FieldValue *value)
{
    if (_nodes.size() > 0)
        if (_nodes[0] != NULL)
            _nodes[0]->setField(index, value);
}


void
NodePROTO::sendEvent(int eventOut, double timestamp, FieldValue *value)
{
    Node::sendEvent(eventOut, timestamp, value);
}

void
NodePROTO::receiveEvent(int eventIn, double timestamp, FieldValue *value)
{
    Node::receiveEvent(eventIn, timestamp, value);
}

void        
NodePROTO::drawHandles()
{
    if (isJoint())
        if (_nodes.size() > 0)
            if (_nodes[0] != NULL)
                ((NodeTransform *)_nodes[0])->drawRotationHandles(0.1f);
}

void        
NodePROTO::transform()
{
    if (isJoint() || isHumanoid())
        if (_nodes.size() > 0)
            if (_nodes[0] != NULL)
                ((NodeTransform *)_nodes[0])->transform();
}

int         
NodePROTO::countPolygons(void)
{
    if (_nodes.size() > 0)
        if (_nodes[0] != NULL)
            return _nodes[0]->countPolygons();
    return 0;
}

int         
NodePROTO::countPrimitives(void)
{
    if (_nodes.size() > 0)
        if (_nodes[0] != NULL)
            return _nodes[0]->countPrimitives();
    return 0;
}

int         
NodePROTO::countPolygons1Sided(void)
{
    if (_nodes.size() > 0)
        if (_nodes[0] != NULL)
            return _nodes[0]->countPolygons1Sided();
    return 0;
}

int         
NodePROTO::countPolygons2Sided(void)
{
    if (_nodes.size() > 0)
        if (_nodes[0] != NULL)
            return _nodes[0]->countPolygons2Sided();
    return 0;
}


Vec3f       
NodePROTO::getHandle(int handle, int *constraint, int *field)
{
    if (isJoint())
        if (_nodes.size() > 0)
            if (_nodes[0] != NULL)
                return _nodes[0]->getHandle(handle, constraint, field);
    *field = -1;
    return Vec3f(0.0f, 0.0f, 0.0f);
}

void
NodePROTO::setHandle(int handle, const Vec3f &v)
{
    if (isJoint())
        if (_nodes.size() > 0)
            if (_nodes[0] != NULL) {
                NodeTransform *transform = (NodeTransform *)_nodes[0];
                transform->setHandle(handle, v);
               _scene->setField(this, _jointRotationField, 
                                new SFRotation(transform->rotation()->getQuat()));
            }
}

static bool getProfileInBranch(Node *node, void *data)
{
    int *profile = (int *)data;
    
    if (node != NULL)
        if (node->getProfile() > *profile)
            *profile = node->getProfile();
    if (*profile == PROFILE_FULL)
        return false;
    return true;
}


int
NodePROTO::getProfile(void) const
{
    int profile = PROFILE_IMMERSIVE;
    for (int i = 0; i < _nodes.size(); i++)
         if (profile != PROFILE_FULL)
             _nodes[i]->doWithBranch(getProfileInBranch, &profile);
    return profile;
}

void                
NodePROTO::getComponentsInBranch(DoWithNodeCallback callback, void *data) 
{
    for (int i = 0; i < _nodes.size(); i++)
        _nodes[i]->doWithBranch(callback, data);
}

bool
NodePROTO::canWriteAc3d()
{
   if (_nodes.size() > 0)
       return _nodes.canWriteAc3d();   
   return false;
}

int
NodePROTO::writeAc3d(int filedes, int indent)
{
   if (_nodes.size() > 0)
       return _nodes.writeAc3d(filedes, indent);   
   return 0;
}


void
NodePROTO::handleAc3dMaterial(ac3dMaterialCallback callback, Scene* scene)
{
   for (int i = 0; i < _nodes.size(); i++)
       _nodes.get(i)->handleAc3dMaterial(callback, scene);
}

bool 
NodePROTO::canWriteCattGeo()
{
   if (_nodes.size() > 0)
       return _nodes.canWriteCattGeo();   
   return false;
}

int         
NodePROTO::writeCattGeo(int filedes, int indent) 
{
   if (_nodes.size() > 0)
       return _nodes.writeCattGeo(this, filedes, indent);   
   return 0;
}

Node *
NodePROTO::getProtoRoot(void)
{ 
    if (_indexedNodes.size() <= 0)
        return NULL;
    return _indexedNodes[0];
}

int getMaskedNodeClass(int nodeClass)
{
    const unsigned int mask = ~(
                                NOT_SELF_NODE |
                                TEXTURE_NODE |
                                TEXTURE_COORDINATE_NODE |
                                TEXTURE_TRANSFORM_NODE |
                                GEOMETRY_NODE |
                                CHILD_NODE |
                                GROUPING_NODE |
                                URL_NODE |
                                PROTO_NODE
                               );

    return nodeClass & mask;
}

bool matchNodeClass(int nodeType, int childType, bool repeat)
{
    int typelist[] = {
        NOT_SELF_NODE,
        TEXTURE_NODE,
        TEXTURE_COORDINATE_NODE,
        TEXTURE_TRANSFORM_NODE,
        GEOMETRY_NODE,
        CHILD_NODE,
        GROUPING_NODE,
        URL_NODE,
        PROTO_NODE
    };
    for (int i = 0; i < (sizeof(typelist) / sizeof(int)); i++)
        if ((nodeType & typelist[i]) && 
            (childType & typelist[i]))
            return true;

    if (getMaskedNodeClass(nodeType) == childType)
        return true;

    switch (getMaskedNodeClass(nodeType)) {
      case AUDIO_CLIP_OR_MOVIE_TEXTURE_NODE:
        switch (getMaskedNodeClass(childType)) { 
          case AUDIO_CLIP_NODE:
            return true;
          case MOVIE_TEXTURE_NODE:
            return true;
        }
        break;
      case BODY_COLLIDABLE_OR_BODY_COLLISION_SPACE_NODE:
        switch (getMaskedNodeClass(childType)) { 
          case BODY_COLLIDABLE_NODE:
            return true;
          case BODY_COLLISION_SPACE_NODE:
            return true;
        }
        break;
      case NURBS_TEXTURE_COORDINATE_OR_TEXTURE_COORDINATE_NODE:
        switch (getMaskedNodeClass(childType)) { 
          case NURBS_TEXTURE_COORDINATE_NODE:
            return true;
          case TEXTURE_COORDINATE_NODE:
            return true;
        }
        break;
/*
      not needed, all massDensity nodes are primitive geometry nodes
      case PRIMITIVE_GEOMETRY_OR_MASS_DENSITY_MODEL_NODE:
        switch (getMaskedNodeClass(childType)) { 
          case PRIMITIVE_GEOMETRY_NODE:
            return true;
          case MASS_DENSITY_MODEL_NODE:
            return true;
        }
        break;
*/
      case SHAPE_OR_LOD_NODE:
        switch (getMaskedNodeClass(childType)) { 
          case SHAPE_NODE:
            return true;
          case LOD_NODE:
            return true;
        }
        break;
      case SHAPE_OR_INLINE_NODE:
        switch (getMaskedNodeClass(childType)) { 
          case SHAPE_NODE:
            return true;
          case INLINE_NODE:
            return true;
        }
        break;
      case SPOTLIGHT_OR_DIRECTIONALLIGHT_OR_VIEWPOINT_NODE:
        switch (getMaskedNodeClass(childType)) {
          case VRML_SPOT_LIGHT:
            return true;
          case VRML_DIRECTIONAL_LIGHT:
            return true;
          case VIEWPOINT_NODE:
            return true;
          case VIEWPOINT_GROUP_NODE:
            return true;
        }
        break;
      case VIEWPOINT_OR_VIEWPOINT_GROUP_NODE:
        switch (getMaskedNodeClass(childType)) {
          case VIEWPOINT_NODE:
            return true;
          case VIEWPOINT_GROUP_NODE:
            return true;
        }
        break;
      case GENERATED_TEXTURE_COORDINATE_NODE:
        switch (getMaskedNodeClass(childType)) {
          case X3D_TEXTURE_COORDINATE_GENERATOR:
            return true;
          case KAMBI_MULTI_GENERATED_TEXTURE_COORDINATE:
            return true;
          case KAMBI_PROJECTED_TEXTURE_COORDINATE:
            return true;
        }
        break;
    }
    if (repeat)
        return matchNodeClass(childType, nodeType, false);
    return false;
}

