/**
   * Get the value of a field. If the field is a primitive type, it will return a class representing
   * the value. For arrays or nodes it will return the instance directly.
   *
   * @param index The index of the field to change.
   * @return The class representing the field value
   * @throws InvalidFieldException The field index is not known
   */
  public VRMLFieldData getFieldValue(int index) throws InvalidFieldException {
    VRMLFieldData fieldData = fieldLocalData.get();

    switch (index) {
      case FIELD_BBOX_SIZE:
        fieldData.clear();
        fieldData.floatArrayValue = bboxSize;
        fieldData.dataType = VRMLFieldData.FLOAT_ARRAY_DATA;
        fieldData.numElements = 1;
        break;

      case FIELD_BBOX_CENTER:
        fieldData.clear();
        fieldData.floatArrayValue = bboxCenter;
        fieldData.dataType = VRMLFieldData.FLOAT_ARRAY_DATA;
        fieldData.numElements = 1;
        break;

      case FIELD_CHILDREN:
        fieldData.clear();

        VRMLNodeType[] kids = new VRMLNodeType[vfChildren.size()];
        vfChildren.toArray(kids);

        fieldData.nodeArrayValue = kids;
        fieldData.dataType = VRMLFieldData.NODE_ARRAY_DATA;
        fieldData.numElements = childCount;
        break;

      default:
        super.getFieldValue(index);
    }

    return fieldData;
  }
  /**
   * Notification that the construction phase of this node has finished. If the node would like to
   * do any internal processing, such as setting up geometry, then go for it now.
   */
  public void setupFinished() {
    super.setupFinished();

    if (!inSetup) return;

    inSetup = false;
  }
  /**
   * Send a routed value from this node to the given destination node. The route should use the
   * appropriate setValue() method of the destination node. It should not attempt to cast the node
   * up to a higher level. Routing should also follow the standard rules for the loop breaking and
   * other appropriate rules for the specification.
   *
   * @param time The time that this route occurred (not necessarily epoch time. Should be treated as
   *     a relative value only)
   * @param srcIndex The index of the field in this node that the value should be sent from
   * @param destNode The node reference that we will be sending the value to
   * @param destIndex The index of the field in the destination node that the value should be sent
   *     to.
   */
  public void sendRoute(double time, int srcIndex, VRMLNodeType destNode, int destIndex) {

    // Simple impl for now.  ignores time and looping. Note that for a
    // couple of the fields, if the array size is greater than the number
    // of components in it, we create a temporary array to send. This is
    // a negative hit, but it is very rare that someone will route out of
    // these fields, so we don't consider it to be a major impact compared
    // to the performance of having to reallocate the arrays every time
    // someone sets the values, which will happen much, much more often.

    try {
      switch (srcIndex) {
        case FIELD_BOUNDARY_COLOR:
          destNode.setValue(destIndex, vfBoundaryColor, 4);
          break;

        case FIELD_BOUNDARY_WIDTH:
          destNode.setValue(destIndex, vfBoundaryWidth);
          break;

        case FIELD_BOUNDARY_MODE_S:
          destNode.setValue(destIndex, vfBoundaryModeS);
          break;

        case FIELD_BOUNDARY_MODE_T:
          destNode.setValue(destIndex, vfBoundaryModeT);
          break;

        case FIELD_MAGNIFICATION_FILTER:
          destNode.setValue(destIndex, vfMagnificationFilter);
          break;

        case FIELD_MINIFICATION_FILTER:
          destNode.setValue(destIndex, vfMinificationFilter);
          break;

        case FIELD_GENERATE_MIPMAPS:
          destNode.setValue(destIndex, vfGenerateMipMaps);
          break;

        case FIELD_ANISOTROPIC_MODE:
          destNode.setValue(destIndex, vfAnisotropicMode);
          break;

        case FIELD_ANISOTROPIC_FILTER_DEGREE:
          destNode.setValue(destIndex, vfAnisotropicFilterDegree);
          break;

        default:
          super.sendRoute(time, srcIndex, destNode, destIndex);
      }
    } catch (InvalidFieldException ife) {
      System.err.println("sendRoute: No field!" + ife.getFieldName());
    } catch (InvalidFieldValueException ifve) {
      System.err.println("sendRoute: Invalid field Value: " + ifve.getMessage());
    }
  }
  /**
   * Notification that the construction phase of this node has finished. If the node would like to
   * do any internal processing, such as setting up geometry, then go for it now.
   */
  public void setupFinished() {
    if (!inSetup) return;

    super.setupFinished();

    if (pShape != null) pShape.setupFinished();

    if (vfShape != null) vfShape.setupFinished();
  }
  /**
   * Change the reference count up or down by one for a given layer ID. If there is no reference to
   * the given layer ID previously, add one now. A listing of the layer IDs that reference this node
   * can be retrieved through {@link #getLayerIds()}.
   *
   * @param layer The id of the layer to modify the ref count on
   * @param add true to increment the reference count, false to decrement
   */
  public void updateRefCount(int layer, boolean add) {
    super.updateRefCount(layer, add);

    if (layerIds == null) return;

    int num_kids = vfChildren.size();

    for (int i = 0; i < num_kids; i++) {
      Object kid = vfChildren.get(i);

      if (kid != null) updateRefs((VRMLNodeType) kid, add);
    }
  }
  /**
   * Set the value of the field at the given index as an integer. This would be used to set SFInt32
   * field types.
   *
   * @param index The index of destination field to set
   * @param value The new value to use for the node
   * @throws InvalidFieldException The field index is not know
   */
  public void setValue(int index, int value)
      throws InvalidFieldException, InvalidFieldValueException {

    switch (index) {
      case FIELD_BOUNDARY_WIDTH:
        if (!inSetup) throw new InvalidFieldAccessException("Cannot set boundaryWidth field.");

        vfBoundaryWidth = value;
        break;

      default:
        super.setValue(index, value);
    }
  }
  /**
   * Set the value of the field at the given index as an boolean. This would be used to set SFBool
   * field types headlight and bind.
   *
   * @param index The index of destination field to set
   * @param value The new value to use for the node
   * @throws InvalidFieldException The field index is not know
   */
  public void setValue(int index, boolean value)
      throws InvalidFieldException, InvalidFieldValueException {

    switch (index) {
      case FIELD_GENERATE_MIPMAPS:
        if (!inSetup) throw new InvalidFieldAccessException("Cannot set generateMipMaps field.");

        vfGenerateMipMaps = value;
        break;

      default:
        super.setValue(index, value);
    }
  }
  /**
   * Set the value of the field at the given index as an array of floats. This would be used to set
   * MFFloat, SFVec2f, SFVec3f and SFRotation field types.
   *
   * @param index The index of destination field to set
   * @param value The new value to use for the node
   * @param numValid The number of valid values to copy from the array
   * @throws InvalidFieldException The field index is not known
   * @throws InvalidFieldValueException The value provided is out of range for the field type.
   */
  public void setValue(int index, float[] value, int numValid)
      throws InvalidFieldException, InvalidFieldValueException, InvalidFieldAccessException {

    switch (index) {
      case FIELD_BBOX_CENTER:
        setBboxCenter(value);
        break;

      case FIELD_BBOX_SIZE:
        setBboxSize(value);
        break;
      default:
        super.setValue(index, value, numValid);
    }
  }
  /**
   * Set the value of the field at the given index as a float. This would be used to set SFFloat
   * field types speed and visibilityLimit.
   *
   * @param index The index of destination field to set
   * @param value The new value to use for the node
   * @throws InvalidFieldException The field index is not know
   */
  public void setValue(int index, float value)
      throws InvalidFieldException, InvalidFieldValueException {

    switch (index) {
      case FIELD_ANISOTROPIC_FILTER_DEGREE:
        if (!inSetup)
          throw new InvalidFieldAccessException("Cannot set anistropicFilterDegree field.");

        vfAnisotropicFilterDegree = value;
        break;

      default:
        super.setValue(index, value);
    }
  }
  /**
   * Notification that the construction phase of this node has finished. If the node would like to
   * do any internal processing, such as setting up geometry, then go for it now.
   */
  public void setupFinished() {
    if (!inSetup) return;

    super.setupFinished();

    childCount = vfChildren.size();
    VRMLNodeType kid;

    for (int i = 0; i < childCount; i++) {
      kid = (VRMLNodeType) vfChildren.get(i);

      // Make sure the child is finished first.
      kid.setupFinished();
    }
  }
  /**
   * Set the value of the field at the given index as a String. This would be used to set SFString
   * field types.
   *
   * @param index The index of destination field to set
   * @param value The new value to use for the node
   */
  public void setValue(int index, String value)
      throws InvalidFieldException, InvalidFieldValueException {

    switch (index) {
      case FIELD_NAME:
        vfName = value;
        if (!inSetup) {
          hasChanged[FIELD_NAME] = true;
          fireFieldChanged(FIELD_NAME);
        }
        break;

      default:
        super.setValue(index, value);
    }
  }
  /**
   * Set the value of the field at the given index as a node. This would be used to set SFNode field
   * types.
   *
   * @param index The index of destination field to set
   * @param value The new value to use for the node
   * @throws InvalidFieldException The index does not match a known field
   */
  public void setValue(int index, VRMLNodeType child) throws InvalidFieldException {

    VRMLNodeType node = (VRMLNodeType) child;
    switch (index) {
      case FIELD_CHILDREN:
        if (!inSetup) clearChildren();

        if (child != null) addChildNode(node);

        hasChanged[FIELD_CHILDREN] = true;
        break;

      default:
        super.setValue(index, child);
    }
  }
  /**
   * Set the value of the field at the given index as an array of floats. This would be used to set
   * MFFloat field type avatarSize.
   *
   * @param index The index of destination field to set
   * @param value The new value to use for the node
   * @param numValid The number of valid values to copy from the array
   * @throws InvalidFieldException The field index is not known
   */
  public void setValue(int index, float[] value, int numValid)
      throws InvalidFieldException, InvalidFieldValueException {

    switch (index) {
      case FIELD_BOUNDARY_COLOR:
        if (!inSetup) throw new InvalidFieldAccessException("Cannot set boundaryColor field.");

        vfBoundaryColor[0] = value[0];
        vfBoundaryColor[1] = value[1];
        vfBoundaryColor[2] = value[2];
        vfBoundaryColor[3] = value[3];
        break;

      default:
        super.setValue(index, value, numValid);
    }
  }
  /**
   * Set the value of the field at the given index as a node. This would be used to set SFNode field
   * types.
   *
   * @param index The index of destination field to set
   * @param value The new value to use for the node
   * @throws InvalidFieldValueException The node does not match the required type.
   */
  public void setValue(int index, VRMLNodeType child)
      throws InvalidFieldException, InvalidFieldValueException {

    VRMLNodeType node = child;

    switch (index) {
      case FIELD_SHAPE:
        setShape(node);
        break;

      default:
        super.setValue(index, child);
    }

    if (!inSetup) {
      hasChanged[index] = true;
      fireFieldChanged(index);
    }
  }
  /**
   * Set the value of the field at the given index as an array of nodes. This would be used to set
   * MFNode field types.
   *
   * @param index The index of destination field to set
   * @param children The new nodes to use for the children
   * @throws InvalidFieldException The index does not match a known field
   */
  public void setValue(int index, VRMLNodeType[] children, int numValid)
      throws InvalidFieldException {

    switch (index) {
      case FIELD_CHILDREN:
        if (!inSetup) clearChildren();

        // Only add non-null nodes here. This is different to the
        // normal grouping node where a null is a significant item
        // in a child list. For the world root, there is no such
        // value.
        for (int i = 0; i < numValid; i++) if (children[i] != null) addChildNode(children[i]);

        hasChanged[FIELD_CHILDREN] = true;
        break;

      default:
        super.setValue(index, children, numValid);
    }
  }
  /**
   * Set the value of the field at the given index as a string. This would be used to set the
   * SFString field type "type".
   *
   * @param index The index of destination field to set
   * @param value The new value to use for the node
   * @throws InvalidFieldException The field index is not know
   */
  public void setValue(int index, String value)
      throws InvalidFieldException, InvalidFieldValueException {

    switch (index) {
      case FIELD_BOUNDARY_MODE_S:
        if (!inSetup) throw new InvalidFieldAccessException("Cannot set boundaryModeS field.");

        vfBoundaryModeS = value;
        break;

      case FIELD_BOUNDARY_MODE_T:
        if (!inSetup) throw new InvalidFieldAccessException("Cannot set boundaryModeT field.");

        vfBoundaryModeT = value;
        break;

      case FIELD_MAGNIFICATION_FILTER:
        if (!inSetup)
          throw new InvalidFieldAccessException("Cannot set magnificationFilter field.");

        vfMagnificationFilter = value;
        break;

      case FIELD_MINIFICATION_FILTER:
        if (!inSetup) throw new InvalidFieldAccessException("Cannot set minificationFilter field.");

        vfMinificationFilter = value;
        break;

      case FIELD_ANISOTROPIC_MODE:
        if (!inSetup) throw new InvalidFieldAccessException("Cannot set anistropicMode field.");

        vfAnisotropicMode = value;
        break;

      default:
        super.setValue(index, value);
    }
  }
  /**
   * Send a routed value from this node to the given destination node. The route should use the
   * appropriate setValue() method of the destination node. It should not attempt to cast the node
   * up to a higher level. Routing should also follow the standard rules for the loop breaking and
   * other appropriate rules for the specification.
   *
   * @param time The time that this route occurred (not necessarily epoch time. Should be treated as
   *     a relative value only)
   * @param srcIndex The index of the field in this node that the value should be sent from
   * @param destNode The node reference that we will be sending the value to
   * @param destIndex The index of the field in the destination node that the value should be sent
   *     to.
   */
  public void sendRoute(double time, int srcIndex, VRMLNodeType destNode, int destIndex) {

    // Simple impl for now.  ignores time and looping

    try {
      switch (srcIndex) {
        case FIELD_NAME:
          destNode.setValue(destIndex, vfName);
          break;
        case FIELD_SHAPE:
          if (pShape != null) destNode.setValue(destIndex, pShape);
          else destNode.setValue(destIndex, vfShape);
          break;

        default:
          super.sendRoute(time, srcIndex, destNode, destIndex);
      }
    } catch (InvalidFieldException ife) {
      System.err.println("sendRoute: No field!" + ife.getFieldName());
    } catch (InvalidFieldValueException ifve) {
      System.err.println("sendRoute: Invalid fieldValue: " + ifve.getMessage());
    }
  }
  /**
   * Get the value of a field. If the field is a primitive type, it will return a class representing
   * the value. For arrays or nodes it will return the instance directly.
   *
   * @param index The index of the field to change.
   * @return The class representing the field value
   * @throws InvalidFieldException The field index is not known
   */
  public VRMLFieldData getFieldValue(int index) throws InvalidFieldException {
    VRMLFieldData fieldData = fieldLocalData.get();

    switch (index) {
      case FIELD_NAME:
        fieldData.clear();
        fieldData.numElements = 1;
        fieldData.dataType = VRMLFieldData.STRING_DATA;
        fieldData.stringValue = vfName;
        break;
      case FIELD_SHAPE:
        fieldData.clear();
        if (pShape != null) fieldData.nodeValue = pShape;
        else fieldData.nodeValue = vfShape;
        fieldData.dataType = VRMLFieldData.NODE_DATA;
        break;

      case FIELD_BBOX_SIZE:
        fieldData.clear();
        fieldData.floatArrayValue = vfBboxSize;
        fieldData.dataType = VRMLFieldData.FLOAT_ARRAY_DATA;
        fieldData.numElements = 1;
        break;

      case FIELD_BBOX_CENTER:
        fieldData.clear();
        fieldData.floatArrayValue = vfBboxCenter;
        fieldData.dataType = VRMLFieldData.FLOAT_ARRAY_DATA;
        fieldData.numElements = 1;
        break;

      default:
        super.getFieldValue(index);
    }

    return fieldData;
  }