/*
 * Proto.h
 *
 * 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.
 */

#ifndef _PROTO_H
#define _PROTO_H

#ifndef _ARRAY_H
# include "Array.h"
#endif
#ifndef _MAP_H
# include "Map.h"
#endif
#ifndef _DUNE_STRING_H
# include "MyString.h"
#endif
#ifndef _NODE_H
# include "Node.h"
#endif

#define INVALID_INDEX (-1)

class Element;
class EventIn;
class EventOut;
class Field;
class ExposedField;
class FieldValue;
class Scene;
class Node;

#include "StringArray.h"
#include "NodeList.h"

class NodeScript;
class ScriptDialog;

class Proto {
public:

    // unlike other nodes, script nodes need to change their Proto dynamically
    friend class NodeScript;
    friend class ScriptDialog;
    friend class DynamicFieldsNode;

                          Proto(Scene *scene, const MyString &name);
                          // create a Proto as a X3D/Cover extension
                          Proto(Scene *scene, Proto *proto, int extensionFlag);
    virtual              ~Proto();

    FieldIndex metadata;

    int metadata_Field(void) const { return metadata; }

    void                  protoInitializer(Scene *scene, const MyString &name);

    bool                  avoidElement(Element *element, int flag);

    virtual int           getType() const { return -1; }
    virtual int           getNodeClass() const { return CHILD_NODE; }

    bool                  matchNodeClass(int childType) const;

    virtual Node         *create(Scene *scene);

    EventIn              *getEventIn(int index) const 
                             { return _eventIns[index]; }
    EventOut             *getEventOut(int index) const 
                             { return _eventOuts[index]; }
    Field                *getField(int index) const { return _fields[index]; }
    ExposedField         *getExposedField(int index) const 
                             { return _exposedFields[index]; }

    int                   getNumFields() const { return _fields.size(); }
    int                   getNumEventIns() const { return _eventIns.size(); }
    int                   getNumEventOuts() const { return _eventOuts.size(); }
    int                   getNumExposedFields() const 
                             { return _exposedFields.size(); }

    int                   lookupEventIn(const MyString &name, 
                                        bool x3d = false) const;
    int                   lookupEventOut(const MyString &name, 
                                         bool x3d = false) const;
    int                   lookupField(const MyString &name, 
                                      bool x3d = false) const;
    int                   lookupExposedField(const MyString &name,
                                             bool x3d = false) const;
    int                   lookupIsEventIn(Node *node, int eventIn,
                                          int elementType = -1) const;
    int                   lookupIsEventOut(Node *node, int eventOut,
                                           int elementType = -1) const;
    int                   lookupIsField(Node *node, int field) const;
    int                   lookupIsExposedField(Node *node, int field) const;

    virtual const MyString &getName(bool x3d) const { return _name; }

    bool                  canWriteElement(Element *element, bool x3d) const;
    int                   write(int filedes, int indent, int flags) const;
    int                   writeEvents(int filedes, int indent, int flags) const;

    int                   writeCDeclaration(int filedes, int lanugageFlag);
    int                   writeCDeclarationField(int f, int i, 
                                                 int lanugageFlag);
    int                   writeCConstructorField(int f, int i, 
                                                 int lanugageFlag);
    int                   writeCCallTreeField(int f, int i, 
                                              const char *functionName, 
                                              int lanugageFlag);

    void                  buildExportNames(void);
    const char           *getClassName(void)
                             { return _className; }

    MyString              buildCallbackClass(const char* name);
    MyString              buildCallbackName(const char* name);

    const char           *getRenderCallbackClass(void)
                             { return _renderCallbackClass; }
    const char           *getRenderCallbackName(void)
                             { return _renderCallbackName; }

    const char           *getTreeRenderCallbackClass(void)
                             { return _treeRenderCallbackClass; }
    const char           *getTreeRenderCallbackName(void)
                             { return _treeRenderCallbackName; }

    const char           *getDoWithDataCallbackClass(void)
                             { return _doWithDataCallbackClass; }
    const char           *getDoWithDataCallbackName(void)
                             { return _doWithDataCallbackName; }

    const char           *getTreeDoWithDataCallbackClass(void)
                             { return _treeDoWithDataCallbackClass; }
    const char           *getTreeDoWithDataCallbackName(void)
                             { return _treeDoWithDataCallbackName; }

    const char           *getEventsProcessedCallbackClass(void)
                             { return _eventsProcessedCallbackClass; }
    const char           *getEventsProcessedCallbackName(void)
                             { return _eventsProcessedCallbackName; }



    virtual const MyString &getCName(bool x3d) const { return _cName; }

    int                   addElement(Element *element);
    int                   addField(Field *field);
    int                   addExposedField(ExposedField *exposedField);

    void                  deleteElements(void);

    void                  define(Node *primaryNode, NodeList *nodes);
    int                   getNumNodes(void) { return _protoNodes.size(); }
    Node                 *getNode(int i) { return _protoNodes[i]; }
    void                  compareToIsNodes(Node *node);
    void                  addURLs(FieldValue *urls);

    bool                  isX3dExtensionProto(void) 
                             { return isExtensionProto(FF_X3D_ONLY); }
    bool                  isCoverExtensionProto(void) 
                             { return isExtensionProto(FF_COVER_ONLY); }
    bool                  isKambiExtensionProto(void) 
                             { return isExtensionProto(FF_KAMBI_ONLY); }
    bool                  isExtensionProto(int flag);
    bool                  isMismatchingProto(void);
    FieldValue           *getUrls(void) { return _urls; }
    bool                  isExternProto(void) { return _urls != NULL; }
    bool                  checkIsExtension(Element *element, int flag);

    virtual bool          isCoverProto(void) { return false; }
    virtual bool          isKambiProto(void) { return false; } 
    virtual bool          isScriptedProto(void) { return false; }

    void                  setScene(Scene *scene) { _scene = scene; }

    void                  convert2X3d(void) 
                             {
                             for (int i = 0; i < _protoNodes.size(); i++)
                                _protoNodes[i]->convert2X3d();
                             }

    void                  convert2Vrml(void) 
                             {
                             for (int i = 0; i < _protoNodes.size(); i++)
                                _protoNodes[i]->convert2X3d();
                             }
    bool                  isLoaded(void) { return _loaded; }
    void                  setLoaded(bool flag) { _loaded = flag; }

    void                setAppinfo(const MyString& appinfo) 
                           { _appinfo = appinfo; } 
    MyString            getAppinfo() const 
                           { return _appinfo; } 
    void                setDocumentation(const MyString& documentation) 
                           { _documentation = documentation; } 
    MyString            getDocumentation() const 
                           { return _documentation; } 

protected:
    Proto                *copy() const { return new Proto(*this); }
    int                   addField(int fieldType, const MyString &name,
                                   FieldValue *defaultValue,
                                   FieldValue *min = NULL,
                                   FieldValue *max = NULL);
    int                   addField(int fieldType, const MyString &name,
                                   FieldValue *defaultValue, int nodeType);
    int                   addField(int fieldType, const MyString &name,
                                   FieldValue *defaultValue, int flags,
                                   const char **strings);
    int                   addEventIn(int fieldType, const MyString &name);
    int                   addEventIn(int fieldType, const MyString &name,
                                     int flags);
    int                   addEventIn(int fieldType, const MyString &name,
                                     int flags, int field);
    int                   addEventOut(int fieldType, const MyString &name);
    int                   addEventOut(int fieldType, const MyString &name,
                                      int flags);
    int                   addExposedField(int fieldType, const MyString &name,
                                          FieldValue *defaultValue,
                                          FieldValue *min = NULL,
                                          FieldValue *max = NULL,
                                          const MyString &x3dName = "");
    int                   addExposedField(int fieldType, const MyString &name,
                                          FieldValue *defaultValue, 
                                          int nodeType,
                                          const MyString &x3dName = "");
    int                   addExposedField(int fieldType, const MyString &name,
                                          FieldValue *defaultValue, int flags,
                                          const char **strings);
    int                   addExposedField(int fieldType, const MyString &name, 
                                          FieldValue *defaultValue, 
                                          FieldValue *min, FieldValue *max,
                                          int nodeType, int flags, 
                                          const char **strings,
                                          const MyString &x3dName);
    void                  deleteEventOut(int i);
    int                   lookupSimpleEventIn(const MyString &name, 
                                              bool x3d) const;
    int                   lookupSimpleEventOut(const MyString &name,
                                               bool x3d) const;
    void                  setIsNodeIndex(void);
    void                  setFieldFlags(FieldIndex index, int flags);

    void                  handleVrmlCut(EventOut *eventOut);

protected:
    Scene                *_scene;
    MyString              _name;
    Array<EventIn *>      _eventIns;
    Array<EventOut *>     _eventOuts;
    Array<Field *>        _fields;
    Array<ExposedField *> _exposedFields;

    MyString            _appinfo;
    MyString            _documentation;

    // for PROTO's:
    Array<Node *>         _protoNodes;
    int                   _nodeIndex;
    FieldValue           *_urls;
    bool                  _loaded;
    StringArray           _nameOfFieldsWithDefaultValue;

    MyString              _cName;
    MyString              _className;

    MyString              _renderCallbackClass;
    MyString              _renderCallbackName;

    MyString              _treeRenderCallbackClass;
    MyString              _treeRenderCallbackName;

    MyString              _doWithDataCallbackClass;
    MyString              _doWithDataCallbackName;

    MyString              _treeDoWithDataCallbackClass;
    MyString              _treeDoWithDataCallbackName;

    MyString              _eventsProcessedCallbackClass;
    MyString              _eventsProcessedCallbackName;
};

class NodePROTO : public Node
{
public:
                        NodePROTO(Scene *scene, Proto *proto);
    void                createPROTO(void);

    virtual bool        isPROTO(void) { return true; }

    virtual int         getType() const; 
    virtual int         getNodeClass() const;
    virtual int         getProfile(void) const;
    virtual Node       *copy() const { return new NodePROTO(*this); }

    virtual Proto      *getPrimaryProto() const
                           { return _indexedNodes[0]->getProto(); }

    virtual bool        showFields() { return true; }

    virtual void        preDraw();
    virtual void        draw();
    virtual void        draw(int pass);

    virtual void        update();
    virtual void        reInit();

    virtual FieldValue *getField(int index) const;
    virtual FieldValue *getField(FieldIndex index) const;
    virtual void        setField(int index, FieldValue *value);
    virtual void        setField(FieldIndex index, FieldValue *value);
    virtual void        sendEvent(int eventOut, double timestamp, 
                                  FieldValue *value);
    virtual void        receiveEvent(int eventIn, double timestamp, 
                                     FieldValue *value);
    void                buildIndexedNodes(void);
    Node               *getIsNode(int nodeIndex);
    void                appendToIndexedNodes(Node *node);
    virtual bool        isJoint(void) { return _jointRotationField != -1; }
    virtual bool        isHumanoid(void) { return _isHumanoid; }
    virtual void        drawHandles();
    virtual int         countPolygons(void);
    virtual int         countPrimitives(void);
    virtual int         countPolygons1Sided(void);
    virtual int         countPolygons2Sided(void);
    virtual Vec3f       getHandle(int handle, int *constraint, int *field);
    virtual void        setHandle(int handle, const Vec3f &v);
    virtual void        transform();
    virtual bool        isEXTERNPROTO(void);
    void                getComponentsInBranch(DoWithNodeCallback callback,
                                              void *data);
    virtual bool        canWriteAc3d();
    virtual int         writeAc3d(int filedes, int indent);
    virtual void        handleAc3dMaterial(ac3dMaterialCallback callback, 
                                           Scene* scene);
    virtual bool        canWriteCattGeo();
    virtual int         writeCattGeo(int filedes, int indent);

    virtual bool        hasProtoNodes(void) { return _indexedNodes.size() > 0; }
    Node               *getProtoRoot(void);

protected:
    NodeList            _nodes;
    Array<Node *>       _indexedNodes;
    int                 _jointRotationField;
    bool                _isHumanoid;
};

typedef Array<Proto *>  ProtoArray;
int getMaskedNodeClass(int nodeClass);
bool matchNodeClass(int nodeType, int childType, bool repeat = true);

#endif // _PROTO_H
