/**
   * 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_MODE:
        fieldData.clear();
        fieldData.stringArrayValue = vfMode;
        fieldData.dataType = VRMLFieldData.STRING_ARRAY_DATA;
        fieldData.numElements = vfMode.length;
        break;

      case FIELD_SOURCE:
        fieldData.clear();
        fieldData.stringArrayValue = vfSource;
        fieldData.dataType = VRMLFieldData.STRING_ARRAY_DATA;
        fieldData.numElements = vfSource.length;
        break;

      case FIELD_FUNCTION:
        fieldData.clear();
        fieldData.stringArrayValue = vfFunction;
        fieldData.dataType = VRMLFieldData.STRING_ARRAY_DATA;
        fieldData.numElements = vfFunction.length;
        break;

      case FIELD_TEXTURE:
        VRMLNodeType kids[] = new VRMLNodeType[vfTexture.size()];
        vfTexture.toArray(kids);
        fieldData.clear();
        fieldData.nodeArrayValue = kids;
        fieldData.dataType = VRMLFieldData.NODE_ARRAY_DATA;
        fieldData.numElements = kids.length;
        break;

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

      case FIELD_ALPHA:
        fieldData.clear();
        fieldData.floatValue = vfAlpha;
        fieldData.dataType = VRMLFieldData.FLOAT_DATA;
        fieldData.numElements = 1;
        break;

      default:
        super.getFieldValue(index);
    }

    return fieldData;
  }
  /**
   * Set the value of the field at the given index as a float. This would be used to set SFFloat
   * field types.
   *
   * @param index The index of destination field to set
   * @param value The raw value string to be parsed
   * @throws InvalidFieldException The index is not a valid field
   * @throws InvalidFieldFormatException The string was not in a correct form for this field.
   * @throws InvalidFieldValueException The field value is not legal for the field specified.
   */
  public void setValue(int index, float value)
      throws InvalidFieldException, InvalidFieldFormatException, InvalidFieldValueException {

    switch (index) {
      case FIELD_ALPHA:
        vfAlpha = value;

        if (!inSetup) {
          hasChanged[FIELD_ALPHA] = true;
          fireFieldChanged(FIELD_ALPHA);
        }
        break;

      default:
        super.setValue(index, value);
    }
  }
  /**
   * 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 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 know
   */
  public void setValue(int index, VRMLNodeType[] children, int numValid)
      throws InvalidFieldException, InvalidFieldValueException {

    switch (index) {
      case FIELD_TEXTURE:
        if (!inSetup) vfTexture.clear();

        loaded = new boolean[numValid];
        for (int i = 0; i < numValid; i++) addTextureNode(children[i]);

        if (!inSetup) {
          hasChanged[FIELD_TEXTURE] = true;
          fireFieldChanged(FIELD_TEXTURE);
        }
        break;

      default:
        super.setValue(index, children, numValid);
    }
  }
  /**
   * 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 value The new value to use for the node
   * @throws InvalidFieldException The field index is not know
   */
  public void setValue(int index, VRMLNodeType child)
      throws InvalidFieldException, InvalidFieldValueException {

    switch (index) {
      case FIELD_TEXTURE:
        if (!inSetup) vfTexture.clear();

        loaded = new boolean[1];
        if (child != null) addTextureNode(child);

        if (!inSetup) {
          hasChanged[FIELD_TEXTURE] = true;
          fireFieldChanged(FIELD_TEXTURE);
        }
        break;

      default:
        super.setValue(index, child);
    }
  }
  /**
   * Set the value of the field at the given index as an array of strings. This would be used to set
   * MFString 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 know
   * @throws InvalidFieldValueException The field value is not legal for the field specified.
   */
  public void setValue(int index, String[] value, int numValid)
      throws InvalidFieldException, InvalidFieldValueException {

    switch (index) {
      case FIELD_MODE:
        setMode(value);
        break;

      case FIELD_FUNCTION:
        setFunction(value);
        break;

      case FIELD_SOURCE:
        setSource(value);
        break;

      default:
        super.setValue(index, value, numValid);
    }
  }
  /**
   * 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_MODE:
          destNode.setValue(destIndex, vfMode, vfMode.length);
          break;

        case FIELD_FUNCTION:
          destNode.setValue(destIndex, vfFunction, vfFunction.length);
          break;

        case FIELD_SOURCE:
          destNode.setValue(destIndex, vfSource, vfSource.length);
          break;

        case FIELD_COLOR:
          destNode.setValue(destIndex, vfColor, 3);
          break;

        case FIELD_TEXTURE:
          VRMLNodeType kids[] = new VRMLNodeType[vfTexture.size()];
          vfTexture.toArray(kids);
          destNode.setValue(destIndex, kids, kids.length);
          break;

        case FIELD_ALPHA:
          destNode.setValue(destIndex, vfAlpha);
          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());
    }
  }
  /**
   * Set the value of the field at the given index as a float. This would be used to set SFFloat
   * field types.
   *
   * @param index The index of destination field to set
   * @param value The raw value string to be parsed
   * @param numValid The number of valid values to copy from the array
   * @throws InvalidFieldException The index is not a valid field
   * @throws InvalidFieldFormatException The string was not in a correct form for this field.
   * @throws InvalidFieldValueException The field value is not legal for the field specified.
   */
  public void setValue(int index, float[] value, int numValid)
      throws InvalidFieldException, InvalidFieldFormatException, InvalidFieldValueException {

    switch (index) {
      case FIELD_COLOR:
        FieldValidator.checkColorVector("BaseMultitexture", value);

        vfColor[0] = value[0];
        vfColor[1] = value[1];
        vfColor[2] = value[2];

        if (!inSetup) {
          hasChanged[FIELD_COLOR] = true;
          fireFieldChanged(FIELD_COLOR);
        }
        break;

      default:
        super.setValue(index, value, numValid);
    }
  }
  /**
   * 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();

    int len = vfTexture.size();
    VRMLTextureNodeType tex;
    loaded = new boolean[len];

    for (int i = 0; i < len; i++) {
      tex = (VRMLTextureNodeType) vfTexture.get(i);
      tex.setupFinished();

      switch (tex.getTextureType()) {
        case TextureConstants.TYPE_SINGLE_2D:
          if (((VRMLTexture2DNodeType) tex).getImage() != null) loaded[i] = true;
          break;
        default:
          System.out.println("Unhandled texture type in BaseMultiTexture");
      }
    }

    inSetup = false;
  }