public static boolean doCollectParallel(final MinecartManiaMinecart minecart) {
   final ArrayList<Block> blockList = minecart.getParallelBlocks();
   for (final Block block : blockList) {
     if (block.getState() instanceof Chest) {
       final MinecartManiaChest chest =
           MinecartManiaWorld.getMinecartManiaChest((Chest) block.getState());
       final ArrayList<Sign> signList =
           SignUtils.getAdjacentMinecartManiaSignList(chest.getLocation(), 1);
       for (final Sign sign : signList) {
         for (int i = 0; i < sign.getNumLines(); i++) {
           if (sign.getLine(i).toLowerCase().contains("parallel")) {
             sign.setLine(i, "[Parallel]");
             if (!minecart.isMovingAway(block.getLocation())) {
               if (chest.addItem(minecart.getType().getId())) {
                 minecart.kill(false);
                 return true;
               }
             }
           }
         }
       }
     }
   }
   return false;
 }
 public static ArrayList<CompassDirection> getRestrictedDirections(
     MinecartManiaMinecart minecart) {
   ArrayList<CompassDirection> restricted = new ArrayList<CompassDirection>(4);
   ArrayList<Sign> signList = SignUtils.getAdjacentSignList(minecart, 2);
   for (Sign sign : signList) {
     for (int i = 0; i < 4; i++) {
       if (sign.getLine(i).toLowerCase().contains("restrict")) {
         String[] directions = sign.getLine(i).split(":");
         if (directions.length > 1) {
           for (int j = 1; j < directions.length; j++) {
             if (directions[j].contains("N")) {
               restricted.add(CompassDirection.NORTH);
             }
             if (directions[j].contains("S")) {
               restricted.add(CompassDirection.SOUTH);
             }
             if (directions[j].contains("E")) {
               restricted.add(CompassDirection.EAST);
             }
             if (directions[j].contains("W")) {
               restricted.add(CompassDirection.WEST);
             }
           }
           return restricted;
         }
       }
     }
   }
   return restricted;
 }
 public static boolean isNoCollection(final MinecartManiaChest chest) {
   final ArrayList<Sign> signList =
       SignUtils.getAdjacentMinecartManiaSignList(chest.getLocation(), 2);
   for (final Sign sign : signList) {
     for (int i = 0; i < sign.getNumLines(); i++) {
       if (sign.getLine(i).toLowerCase().contains("no collection")) {
         sign.setLine(i, "[No Collection]");
         return true;
       }
     }
   }
   return false;
 }
  // NOTE: Bukkit getRelative() still seems to follow old directionality. As a quick fix,
  //       all calls to getAdjacentTrack() have been changed into "wrong" looking ones!
  public static Location getSpawnLocationSignOverride(final MinecartManiaChest chest) {
    final ArrayList<Sign> signList =
        SignUtils.getAdjacentMinecartManiaSignList(chest.getLocation(), 2);
    final Location spawn = chest.getLocation();
    Location result = null;
    final Block neighbor =
        chest.getNeighborChest() != null ? chest.getNeighborChest().getLocation().getBlock() : null;

    for (final Sign sign : signList) {
      for (int i = 0; i < sign.getNumLines(); i++) {
        if (sign.getLine(i).toLowerCase().contains("spawn north")) {
          sign.setLine(i, "[Spawn North]");
          result = getAdjacentTrack(spawn.getBlock(), BlockFace.EAST);
          if ((result == null) && (neighbor != null))
            return getAdjacentTrack(neighbor, BlockFace.EAST);
          else return result;
        }
        if (sign.getLine(i).toLowerCase().contains("spawn east")) {
          sign.setLine(i, "[Spawn East]");
          result = getAdjacentTrack(spawn.getBlock(), BlockFace.SOUTH);
          if ((result == null) && (neighbor != null))
            return getAdjacentTrack(neighbor, BlockFace.SOUTH);
          else return result;
        }
        if (sign.getLine(i).toLowerCase().contains("spawn south")) {
          sign.setLine(i, "[Spawn South]");
          result = getAdjacentTrack(spawn.getBlock(), BlockFace.WEST);
          if ((result == null) && (neighbor != null))
            return getAdjacentTrack(neighbor, BlockFace.WEST);
          else return result;
        }
        if (sign.getLine(i).toLowerCase().contains("spawn west")) {
          sign.setLine(i, "[Spawn West]");
          result = getAdjacentTrack(spawn.getBlock(), BlockFace.NORTH);
          if ((result == null) && (neighbor != null))
            return getAdjacentTrack(neighbor, BlockFace.NORTH);
          else return result;
        }
      }
    }

    return null;
  }
  public static Material getMinecartType(final MinecartManiaChest chest) {
    final ArrayList<com.afforess.minecartmaniacore.signs.Sign> signList =
        SignUtils.getAdjacentMinecartManiaSignList(chest.getLocation(), 2);
    for (final com.afforess.minecartmaniacore.signs.Sign sign : signList) {
      if (sign instanceof MinecartTypeSign) {
        final MinecartTypeSign type = (MinecartTypeSign) sign;
        if (type.canDispenseMinecartType(Material.MINECART)) {
          if (chest.contains(Material.MINECART)) return Material.MINECART;
        }
        if (type.canDispenseMinecartType(Material.POWERED_MINECART)) {
          if (chest.contains(Material.POWERED_MINECART)) return Material.POWERED_MINECART;
        }
        if (type.canDispenseMinecartType(Material.STORAGE_MINECART)) {
          if (chest.contains(Material.STORAGE_MINECART)) return Material.STORAGE_MINECART;
        }
      }
    }

    // Returns standard minecart by default
    return Material.MINECART;
  }
  public static void doItemCompression(final MinecartManiaStorageCart minecart) {
    final HashSet<Block> blockList = minecart.getAdjacentBlocks(minecart.getRange());
    for (final Block block : blockList) {
      if (block.getTypeId() == Material.WORKBENCH.getId()) {
        final ArrayList<Sign> signList =
            SignUtils.getAdjacentMinecartManiaSignList(block.getLocation(), 2);
        for (final Sign sign : signList) {
          for (int i = 0; i < sign.getNumLines(); i++) {
            if (sign.getLine(i).toLowerCase().contains("compress items")
                || sign.getLine(i).toLowerCase().contains("compress")) {
              sign.setLine(i, "[Compress Items]");
              // TODO handling for custom recipies?
              final Material[][] compressable = {
                {
                  Material.IRON_INGOT,
                  Material.GOLD_INGOT,
                  Material.INK_SACK,
                  Material.DIAMOND,
                  Material.CLAY_BALL,
                  Material.SNOW_BALL
                },
                {
                  Material.IRON_BLOCK,
                  Material.GOLD_BLOCK,
                  Material.LAPIS_BLOCK,
                  Material.DIAMOND_BLOCK,
                  Material.CLAY,
                  Material.SNOW_BLOCK
                }
              };
              int n = 0;
              for (final Material m : compressable[0]) {
                final ItemStack masItem =
                    new ItemStack(m.getId(), (Material.INK_SACK == m) ? 4 : 0);
                final int amtPerBlock = getNumItemsInBlock(m);
                int amt = 0;
                int slot = 0;
                for (final ItemStack item : minecart.getContents()) {
                  if ((item != null)
                      && (item.getTypeId() == masItem.getTypeId())
                      && (item.getDurability() == masItem.getDurability())) {
                    amt += item.getAmount();
                    minecart.setItem(slot, null);
                  }
                  slot++;
                }
                int compressedAmt = amt / amtPerBlock;
                final int left = amt % amtPerBlock;
                while (compressedAmt > 0) {
                  minecart.addItem(compressable[1][n].getId(), Math.min(64, compressedAmt));
                  compressedAmt -= Math.min(64, compressedAmt);
                }
                if (left > 0) {
                  minecart.addItem(compressable[0][n].getId(), left);
                }

                n++;
              }
            }
          }
        }
      }
    }
  }
  public static void doCrafting(final MinecartManiaStorageCart minecart) {
    // Efficiency. Don't process overlapping tiles repeatedly, waste of time
    final int interval =
        minecart.getDataValue("Craft Interval") == null
            ? -1
            : (Integer) minecart.getDataValue("Craft Interval");
    if (interval > 0) {
      minecart.setDataValue("Craft Interval", interval - 1);
    } else {
      minecart.setDataValue("Craft Interval", minecart.getRange() / 2);
      final HashSet<Block> blockList = minecart.getAdjacentBlocks(minecart.getRange());
      for (final Block block : blockList) {
        if (block.getTypeId() == Material.WORKBENCH.getId()) {
          final ArrayList<Sign> signList =
              SignUtils.getAdjacentMinecartManiaSignList(block.getLocation(), 2);
          for (final Sign sign : signList) {
            if (sign.getLine(0).toLowerCase().contains("craft items")) {
              sign.setLine(0, "[Craft Items]");
              // For each line on the sign
              String itemListString = "";
              for (int i = 1; i < sign.getNumLines(); i++) {
                if (i > 1) {
                  itemListString += ":";
                }
                itemListString += sign.getLine(i);
              }
              for (final SpecificMaterial item :
                  ItemUtils.getItemStringListToMaterial(itemListString.split(":"))) {
                // Get the recipe, if possible
                final RecipeData recipe = RecipeManager.findRecipe(item);

                if (recipe == null) {
                  continue; // Skip if we can't find it.
                }
                if ((recipe.ingredients == null) || (recipe.ingredients.size() == 0)) {
                  continue;
                }

                boolean outOfIngredients = false;

                int loops = 0;

                final List<ItemStack> fixedIngredients = new ArrayList<ItemStack>();

                debug(
                    minecart,
                    "RECIPE: "
                        + recipe.results.toString()
                        + " (d: "
                        + recipe.results.getDurability()
                        + ")");
                // Until we're out of ingredients, or the loop has been executed 64 times.
                while (!outOfIngredients && (loops < 64)) {
                  fixedIngredients.clear();

                  loops++;
                  // Loop through the list of ingredients for this recipe
                  for (final ItemStack stack : recipe.ingredients) {
                    boolean found = false;

                    if (stack.getDurability() == (short) -1) {
                      // See what we have
                      ItemStack subitem = null;
                      for (int is = 0; is < minecart.size(); is++) {
                        final ItemStack si = minecart.getItem(is);
                        if ((si != null) && (si.getTypeId() == stack.getTypeId())) {
                          subitem = si;
                          break;
                        }
                      }
                      if (subitem == null) {
                        continue;
                      }
                      stack.setDurability(subitem.getDurability());

                      // See if we have the needed ingredient
                      final int num = minecart.amount(stack.getTypeId(), stack.getDurability());
                      if (minecart.amount(stack.getTypeId(), stack.getDurability())
                          < stack.getAmount()) {
                        continue;
                      } else {
                        debug(
                            minecart,
                            "Cart has "
                                + num
                                + " "
                                + recipe.results.toString()
                                + " (d: "
                                + recipe.results.getDurability()
                                + ")!");
                        found = true;
                        break;
                      }
                    } else {
                      if (stack.getDurability() == -1) {
                        stack.setDurability((short) 0);
                      }

                      // See if we have the needed ingredients
                      if (minecart.amount(stack.getTypeId(), stack.getDurability())
                          >= stack.getAmount()) {
                        found = true;
                      } else {
                        debug(
                            minecart,
                            "OOI: " + stack.toString() + " (d: " + stack.getDurability() + ")");
                        outOfIngredients = true;
                        break;
                      }
                    }
                    if (!found) {
                      outOfIngredients = true;
                      debug(
                          minecart,
                          "OOI: " + stack.toString() + " (d: " + stack.getDurability() + ")");
                      break;
                    } else {
                      // debug(minecart, "Ingredient found: " + stack.toString() + " (d: " +
                      // stack.getDurability() + ")");
                      fixedIngredients.add(stack);
                    }
                  }

                  if (outOfIngredients) {
                    break;
                  }

                  // Double-check
                  debug(
                      minecart,
                      "Recipe for "
                          + recipe.results.toString()
                          + " (d: "
                          + recipe.results.getDurability()
                          + ")");
                  for (final ItemStack stack : fixedIngredients) {
                    if (minecart.canRemoveItem(
                        stack.getTypeId(), stack.getAmount(), stack.getDurability())) {
                      debug(
                          minecart,
                          " + " + stack.toString() + " (d: " + stack.getDurability() + ")");
                    } else {
                      debug(
                          minecart,
                          "OOI: " + stack.toString() + " (d: " + stack.getDurability() + ")");
                      outOfIngredients = true;
                      break;
                    }
                  }

                  if (outOfIngredients) {
                    break;
                  }

                  if (!minecart.canAddItem(recipe.results)) {
                    debug(minecart, "CAI: " + recipe.results.toString());
                    outOfIngredients = true;
                    break;
                  }

                  // Loop through again to actually remove the items
                  for (final ItemStack stack : fixedIngredients) {
                    debug(
                        minecart,
                        "[Craft Items] Removed "
                            + stack.toString()
                            + " (d: "
                            + stack.getDurability()
                            + ") from minecart!");
                    minecart.removeItem(
                        stack.getTypeId(), stack.getAmount(), stack.getDurability());
                  }
                  // Take it from the cart
                  minecart.addItem(recipe.results);
                  debug(
                      minecart,
                      "[Craft Items] Added " + recipe.results.toString() + " to minecart!");
                }
              }
            }
          }
        }
      }
    }
  }
  public static void processStation(MinecartEvent event) {
    MinecartManiaMinecart minecart = event.getMinecart();

    ArrayList<Sign> signList = SignUtils.getAdjacentSignList(minecart, 2);
    for (Sign sign : signList) {
      convertCraftBookSorter(sign);
      for (int k = 0; k < 4; k++) {
        // Setup initial data
        String str = sign.getLine(k);
        String newLine = str;
        String val[] = str.split(":");
        if (val.length != 2) {
          continue;
        }
        // Strip header and ending characters
        val[0] = StringUtils.removeBrackets(val[0]);
        val[1] = StringUtils.removeBrackets(val[1]);
        // Strip whitespace
        val[0] = val[0].trim();
        val[1] = val[1].trim();
        boolean valid = false;
        // end of data setup

        for (StationCondition e : StationCondition.values()) {
          if (e.result(minecart, val[0])) {
            valid = true;
            break;
          }
        }

        if (valid) {
          CompassDirection direction = CompassDirection.NO_DIRECTION;

          for (StationDirection e : StationDirection.values()) {
            direction = e.direction(minecart, val[1]);
            if (direction != CompassDirection.NO_DIRECTION) {
              break;
            }
          }

          // Special case - if we are at a launcher, set the launch speed as well
          // mze 2012-08-29: Remove it because it makes more problems than it solves...
          /*if (event instanceof MinecartLaunchedEvent && direction != null && direction != CompassDirection.NO_DIRECTION) {
          	minecart.setMotion(direction, 0.6D);
          	((MinecartLaunchedEvent)event).setLaunchSpeed(minecart.minecart.getVelocity());
          }*/

          // setup sign formatting
          newLine = StringUtils.removeBrackets(newLine);
          char[] ch = {' ', ':'};
          newLine = WordUtils.capitalize(newLine, ch);
          newLine = StringUtils.addBrackets(newLine);

          boolean handled = false;
          // Handle minecart destruction
          if (direction == null) {
            // vanish sign, minecart is just gone ;)
            if (val[1].equals("V") || val[1].toLowerCase().contains("vanish")) {
              minecart.kill(false);
            }

            minecart.kill();
            handled = true;
          } else if (MinecartUtils.validMinecartTrack(
              minecart.minecart.getWorld(),
              minecart.getX(),
              minecart.getY(),
              minecart.getZ(),
              2,
              direction)) {
            int data =
                DirectionUtils.getMinetrackRailDataForDirection(direction, minecart.getDirection());
            if (data != -1) {
              handled = true;

              // Force the game to remember the old data of the rail we are on, and reset it once we
              // are done
              Block oldBlock =
                  MinecartManiaWorld.getBlockAt(
                      minecart.minecart.getWorld(),
                      minecart.getX(),
                      minecart.getY(),
                      minecart.getZ());
              ArrayList<Integer> blockData = new ArrayList<Integer>();
              blockData.add(new Integer(oldBlock.getX()));
              blockData.add(new Integer(oldBlock.getY()));
              blockData.add(new Integer(oldBlock.getZ()));
              blockData.add(new Integer(oldBlock.getData()));
              minecart.setDataValue("old rail data", blockData);

              // change the track dirtion
              MinecartManiaWorld.setBlockData(
                  minecart.minecart.getWorld(),
                  minecart.getX(),
                  minecart.getY(),
                  minecart.getZ(),
                  data);
            } else if (DirectionUtils.getOppositeDirection(direction)
                .equals(minecart.getDirection())) {
              // format the sign
              minecart.reverse();
              handled = true;
            }
          }

          if (handled) {
            event.setActionTaken(true);
            // format the sign
            sign.setLine(k, newLine);
            sign.update(true);
            return;
          }
        }
      }
    }
  }