/** Construct a new action that handles the given wrapper and talks to the map panel */
  ShowChildrenAction(EntityWrapper eWrapper, AbstractView.AbstractImagePanel map) {

    I18nManager intl_mgr = I18nManager.getManager();

    String prop_name = eWrapper.getChildrenShown() ? HIDE_TITLE_PROP : SHOW_TITLE_PROP;

    putValue(NAME, intl_mgr.getString(prop_name));

    mapPanel = map;
    entityWrapper = eWrapper;

    // Only enable this if there are children to show
    Entity entity = eWrapper.getEntity();
    setEnabled(entity.hasChildren());
  }
  /**
   * Perform the rule check
   *
   * @param entity Entity object
   * @param command Command object
   * @param result The state of the rule processing
   * @return boolean True if rule passes, false otherwise
   */
  protected RuleEvaluationResult performCheck(
      Entity entity, Command command, RuleEvaluationResult result) {

    this.result = result;

    // Extract the relationship data
    String[] classRelationship =
        (String[])
            RulePropertyAccessor.getRulePropertyValue(
                entity, ChefX3DRuleProperties.RELATIONSHIP_CLASSIFICATION_PROP);

    int[] relationshipAmount =
        (int[])
            RulePropertyAccessor.getRulePropertyValue(
                entity, ChefX3DRuleProperties.RELATIONSHIP_AMOUNT_PROP);

    Enum[] relModifier =
        (Enum[])
            RulePropertyAccessor.getRulePropertyValue(
                entity, ChefX3DRuleProperties.RELATIONSHIP_MODIFIER_PROP);

    String[] colReplaceClass =
        (String[])
            RulePropertyAccessor.getRulePropertyValue(
                entity, ChefX3DRuleProperties.REPLACE_PROD_CLASS_PROP);

    // If any of these are null, do not proceed with collision check
    if (classRelationship == null || relationshipAmount == null || relModifier == null) {

      result.setResult(true);
      return (result);
    }

    // Maximum index limit for loops
    int maxIndex =
        Math.min(
            (Math.min(classRelationship.length, relationshipAmount.length)), relModifier.length);

    // Perform collision check - initial point at which a check should
    // be performed for movement rules.
    rch.performCollisionCheck(command, true, false, false);

    // Analyze class relationship data against collisions
    // (first time needs to be performed)
    rch.performCollisionAnalysis(
        classRelationship, null, null, colReplaceClass, maxIndex, null, false, null);

    ArrayList<Entity> replaceEntityMatches = collisionResults.getReplaceEntityMatches();

    if (replaceEntityMatches.size() > 0) {

      // If command is transient, just update the status bar and exit
      // otherwise, prompt to replace. If canceled, reset command back
      // to last known good.
      if (command.isTransient()) {

        String msg = intl_mgr.getString(STATUS_BAR_MSG);
        statusBar.setMessage(msg);

        if (command instanceof MoveEntityTransientCommand) {

          float maxDepthBuffer = 0;

          // look through all the items to be replaced and get
          // the largest depth buffer adjustment
          for (int i = 0; i < replaceEntityMatches.size(); i++) {

            Entity childEntity = replaceEntityMatches.get(i);

            Float depthBuffer =
                (Float)
                    RulePropertyAccessor.getRulePropertyValue(
                        childEntity, ChefX3DRuleProperties.CENTER_DEPTH_POS_BUFF_PROP);

            if (depthBuffer != null && depthBuffer > maxDepthBuffer) {
              maxDepthBuffer = depthBuffer;
            }
          }

          double[] pos = new double[3];
          ((MoveEntityTransientCommand) command).getPosition(pos);

          // adjust the item by the max depth buffer found
          ///////////////////////////////////////////////////////////
          // rem: think this should be plus instead of minus,
          // like the SetRelativePositionRule's usage
          // pos[2] -= maxDepthBuffer;
          pos[2] += maxDepthBuffer;
          ///////////////////////////////////////////////////////////

          ((MoveEntityTransientCommand) command).setPosition(pos);
        }

      } else {

        String msg = intl_mgr.getString(POP_UP_MSG);

        if (popUpConfirm.showMessage(msg)) {

          // Issue all of the new remove commands resulting from the
          // replace operation
          for (int i = 0; i < replaceEntityMatches.size(); i++) {

            Entity childEntity = replaceEntityMatches.get(i);

            int parentEntityID = childEntity.getParentEntityID();
            Entity parentEntity = model.getEntity(parentEntityID);

            if (childEntity != null && parentEntity != null) {

              SceneManagementUtility.removeChild(model, collisionChecker, childEntity, false);
            }
          }

          // Perform special case command handling for reparenting
          if (command instanceof AddEntityChildCommand
              || command instanceof AddEntityChildTransientCommand) {

            Entity origParentEntity = ((AddEntityChildCommand) command).getParentEntity();

            // If the replaceEntityMatches contains the parent that
            // the new entity is to be a child of, get the parent
            // of that parent and set that as the new parent of the
            // new add command.
            if (replaceEntityMatches.contains(origParentEntity)) {

              // Get the new parent entity to set
              int newParentId = origParentEntity.getParentEntityID();

              Entity newParentEntity = model.getEntity(newParentId);

              ((AddEntityChildCommand) command).setParentEntity(newParentEntity);

              // Correct the position
              double[] originalParentPos = new double[3];
              ((PositionableEntity) origParentEntity).getPosition(originalParentPos);

              double[] entityPos = new double[3];
              ((PositionableEntity) entity).getPosition(entityPos);

              entityPos[0] = originalParentPos[0] + entityPos[0];
              entityPos[1] = originalParentPos[1] + entityPos[1];
              entityPos[2] = originalParentPos[2];

              ((PositionableEntity) entity).setPosition(entityPos, false);
            }

          } else if (command instanceof TransitionEntityChildCommand) {

            Entity origParentEntity = ((TransitionEntityChildCommand) command).getEndParentEntity();

            // If the replaceEntityMatches contains the parent that
            // the new entity is to be a child of, get the parent
            // of that parent and set that as the new parent of the
            // transition command.
            if (replaceEntityMatches.contains(origParentEntity)) {

              // Get the new parent entity to set
              int newParentId = origParentEntity.getParentEntityID();

              Entity newParentEntity = model.getEntity(newParentId);

              TransitionEntityChildCommand tranCmd = (TransitionEntityChildCommand) command;

              tranCmd.setEndParentEntity(newParentEntity);

              // Correct the position
              double[] originalParentPos = new double[3];
              ((PositionableEntity) origParentEntity).getPosition(originalParentPos);

              double[] entityPos = new double[3];
              tranCmd.getEndPosition(entityPos);

              entityPos[0] = originalParentPos[0] + entityPos[0];
              entityPos[1] = originalParentPos[1] + entityPos[1];
              entityPos[2] = originalParentPos[2];

              tranCmd.setEndPosition(entityPos);
            }
          }

          result.setResult(true);
          return (result);

        } else {
          // Reset to last known good
          if (command instanceof AddEntityCommand
              || command instanceof AddEntityChildCommand
              || command instanceof AddEntityChildTransientCommand
              || command instanceof TransitionEntityChildCommand) {

            result.setApproved(false);
            result.setResult(false);
            return (result);
          }
        }
      }
    }

    result.setResult(true);
    return (result);
  }
  /**
   * Perform can move entity check. Only case where canBeMoved will be set to true is if the only
   * model entities found in collision are auto place entities. Or if the entity places the auto
   * place products. The samplingEntity passed in is the entity being acted on by the user that
   * should be removed from the list of collision entities found during analysis.
   *
   * @param model WorldModel to reference
   * @param samplingEntity Entity being adjusted by user, to be ignored
   * @param rch RuleCollisionHandler to reference in collision analysis
   */
  public void performCanMoveEntityCheck(
      WorldModel model, Entity samplingEntity, RuleCollisionHandler rch) {

    double[] position = new double[3];

    if (entity instanceof PositionableEntity) {

      ((PositionableEntity) entity).getPosition(position);

    } else {
      canBeMoved = false;
      return;
    }

    MoveEntityCommand mvCmd =
        new MoveEntityCommand(model, 0, (PositionableEntity) entity, position, position);

    rch.performCollisionCheck(mvCmd, true, false, false);

    if (!rch.performCollisionAnalysisHelper(
        entity, null, false, new int[] {samplingEntity.getEntityID()}, false)) {

      canBeMoved = false;
      return;
    }

    // Only circumstance where it can be moved is if it is only
    // colliding with its parent entity, or the collisions are
    // with entities that have been auto added or auto add.
    for (int i = 0; i < rch.getCollisionResults().getEntityMatches().size(); i++) {

      Entity tmpEntity = rch.getCollisionResults().getEntityMatches().get(i);

      if (entity.getParentEntityID() == tmpEntity.getEntityID()) {
        continue;
      }

      Boolean isAutoAdd =
          (Boolean)
              RulePropertyAccessor.getRulePropertyValue(
                  tmpEntity, ChefX3DRuleProperties.IS_AUTO_ADD_PRODUCT);

      Boolean autoAdds = AutoAddUtility.performsAutoAddOperations(tmpEntity);

      if (!isAutoAdd && !autoAdds) {
        canBeMoved = false;
        return;
      }

      Boolean useInstallPosRequirements =
          (Boolean)
              RulePropertyAccessor.getRulePropertyValue(
                  tmpEntity, ChefX3DRuleProperties.COLLISION_POSITION_REQUIREMENTS);

      if (useInstallPosRequirements) {
        canBeMoved = false;
        return;
      }

      Boolean useInstallPosMultiZoneRequirements =
          (Boolean)
              RulePropertyAccessor.getRulePropertyValue(
                  tmpEntity, ChefX3DRuleProperties.COLLISION_POSITION_MULTI_ZONE_REQUIREMENTS);

      if (useInstallPosMultiZoneRequirements) {
        canBeMoved = false;
        return;
      }
    }

    canBeMoved = true;
  }