/**
   * Tries to extract an item from the player inventory. Does account for fuzzy items.
   *
   * @param player the {@link EntityPlayer} to extract from
   * @param mode the {@link Actionable} to simulate or modulate the operation
   * @param patternItem which {@link ItemStack} to extract
   * @return null or a found {@link ItemStack}
   */
  private ItemStack extractItemFromPlayerInventory(
      EntityPlayer player, Actionable mode, ItemStack patternItem) {
    final InventoryAdaptor ia = InventoryAdaptor.getAdaptor(player, ForgeDirection.UNKNOWN);
    final AEItemStack request = AEItemStack.create(patternItem);
    final boolean isSimulated = mode == Actionable.SIMULATE;
    final boolean checkFuzzy =
        request.isOre()
            || patternItem.getItemDamage() == OreDictionary.WILDCARD_VALUE
            || patternItem.hasTagCompound()
            || patternItem.isItemStackDamageable();

    if (!checkFuzzy) {
      if (isSimulated) {
        return ia.simulateRemove(1, patternItem, null);
      } else {
        return ia.removeItems(1, patternItem, null);
      }
    } else {
      if (isSimulated) {
        return ia.simulateSimilarRemove(1, patternItem, FuzzyMode.IGNORE_ALL, null);
      } else {
        return ia.removeSimilarItems(1, patternItem, FuzzyMode.IGNORE_ALL, null);
      }
    }
  }
  public IAEItemStack readItem(ByteBuf stream) throws IOException {
    boolean hasItem = stream.readBoolean();

    if (hasItem) {
      return AEItemStack.loadItemStackFromPacket(stream);
    }

    return null;
  }
  @Override
  public TickRateModulation tickingRequest(final IGridNode node, int ticksSinceLastCall) {
    if (this.inv.getStackInSlot(9) != null) {
      this.pushOut(this.inv.getStackInSlot(9));

      // did it eject?
      if (this.inv.getStackInSlot(9) == null) {
        this.markDirty();
      }

      this.ejectHeldItems();
      this.updateSleepiness();
      this.progress = 0;
      return this.isAwake ? TickRateModulation.IDLE : TickRateModulation.SLEEP;
    }

    if (this.myPlan == null) {
      this.updateSleepiness();
      return TickRateModulation.SLEEP;
    }

    if (this.reboot) {
      ticksSinceLastCall = 1;
    }

    if (!this.isAwake) {
      return TickRateModulation.SLEEP;
    }

    this.reboot = false;
    int speed = 10;
    switch (this.upgrades.getInstalledUpgrades(Upgrades.SPEED)) {
      case 0:
        this.progress += this.userPower(ticksSinceLastCall, speed = 10, 1.0);
        break;
      case 1:
        this.progress += this.userPower(ticksSinceLastCall, speed = 13, 1.3);
        break;
      case 2:
        this.progress += this.userPower(ticksSinceLastCall, speed = 17, 1.7);
        break;
      case 3:
        this.progress += this.userPower(ticksSinceLastCall, speed = 20, 2.0);
        break;
      case 4:
        this.progress += this.userPower(ticksSinceLastCall, speed = 25, 2.5);
        break;
      case 5:
        this.progress += this.userPower(ticksSinceLastCall, speed = 50, 5.0);
        break;
    }

    if (this.progress >= 100) {
      for (int x = 0; x < this.craftingInv.getSizeInventory(); x++) {
        this.craftingInv.setInventorySlotContents(x, this.inv.getStackInSlot(x));
      }

      this.progress = 0;
      final ItemStack output = this.myPlan.getOutput(this.craftingInv, this.getWorldObj());
      if (output != null) {
        FMLCommonHandler.instance()
            .firePlayerCraftingEvent(
                Platform.getPlayer((WorldServer) this.getWorldObj()), output, this.craftingInv);

        this.pushOut(output.copy());

        for (int x = 0; x < this.craftingInv.getSizeInventory(); x++) {
          this.inv.setInventorySlotContents(
              x, Platform.getContainerItem(this.craftingInv.getStackInSlot(x)));
        }

        if (this.inv.getStackInSlot(10) == null) {
          this.forcePlan = false;
          this.myPlan = null;
          this.pushDirection = ForgeDirection.UNKNOWN;
        }

        this.ejectHeldItems();

        try {
          final TargetPoint where =
              new TargetPoint(
                  this.worldObj.provider.dimensionId, this.xCoord, this.yCoord, this.zCoord, 32);
          final IAEItemStack item = AEItemStack.create(output);
          NetworkHandler.instance.sendToAllAround(
              new PacketAssemblerAnimation(
                  this.xCoord, this.yCoord, this.zCoord, (byte) speed, item),
              where);
        } catch (final IOException e) {
          // ;P
        }

        this.markDirty();
        this.updateSleepiness();
        return this.isAwake ? TickRateModulation.IDLE : TickRateModulation.SLEEP;
      }
    }

    return TickRateModulation.FASTER;
  }
  public void craftOrGetItem(final PacketPatternSlot packetPatternSlot) {
    if (packetPatternSlot.slotItem != null && this.getCellInventory() != null) {
      final IAEItemStack out = packetPatternSlot.slotItem.copy();
      InventoryAdaptor inv = new AdaptorPlayerHand(this.getPlayerInv().player);
      final InventoryAdaptor playerInv =
          InventoryAdaptor.getAdaptor(this.getPlayerInv().player, ForgeDirection.UNKNOWN);

      if (packetPatternSlot.shift) {
        inv = playerInv;
      }

      if (inv.simulateAdd(out.getItemStack()) != null) {
        return;
      }

      final IAEItemStack extracted =
          Platform.poweredExtraction(
              this.getPowerSource(), this.getCellInventory(), out, this.getActionSource());
      final EntityPlayer p = this.getPlayerInv().player;

      if (extracted != null) {
        inv.addItems(extracted.getItemStack());
        if (p instanceof EntityPlayerMP) {
          this.updateHeld((EntityPlayerMP) p);
        }
        this.detectAndSendChanges();
        return;
      }

      final InventoryCrafting ic = new InventoryCrafting(new ContainerNull(), 3, 3);
      final InventoryCrafting real = new InventoryCrafting(new ContainerNull(), 3, 3);

      for (int x = 0; x < 9; x++) {
        ic.setInventorySlotContents(
            x,
            packetPatternSlot.pattern[x] == null
                ? null
                : packetPatternSlot.pattern[x].getItemStack());
      }

      final IRecipe r = Platform.findMatchingRecipe(ic, p.worldObj);

      if (r == null) {
        return;
      }

      final IMEMonitor<IAEItemStack> storage = this.getPatternTerminal().getItemInventory();
      final IItemList<IAEItemStack> all = storage.getStorageList();

      final ItemStack is = r.getCraftingResult(ic);

      for (int x = 0; x < ic.getSizeInventory(); x++) {
        if (ic.getStackInSlot(x) != null) {
          final ItemStack pulled =
              Platform.extractItemsByRecipe(
                  this.getPowerSource(),
                  this.getActionSource(),
                  storage,
                  p.worldObj,
                  r,
                  is,
                  ic,
                  ic.getStackInSlot(x),
                  x,
                  all,
                  Actionable.MODULATE,
                  ItemViewCell.createFilter(this.getViewCells()));
          real.setInventorySlotContents(x, pulled);
        }
      }

      final IRecipe rr = Platform.findMatchingRecipe(real, p.worldObj);

      if (rr == r && Platform.isSameItemPrecise(rr.getCraftingResult(real), is)) {
        final SlotCrafting sc = new SlotCrafting(p, real, this.cOut, 0, 0, 0);
        sc.onPickupFromSlot(p, is);

        for (int x = 0; x < real.getSizeInventory(); x++) {
          final ItemStack failed = playerInv.addItems(real.getStackInSlot(x));

          if (failed != null) {
            p.dropPlayerItemWithRandomChoice(failed, false);
          }
        }

        inv.addItems(is);
        if (p instanceof EntityPlayerMP) {
          this.updateHeld((EntityPlayerMP) p);
        }
        this.detectAndSendChanges();
      } else {
        for (int x = 0; x < real.getSizeInventory(); x++) {
          final ItemStack failed = real.getStackInSlot(x);
          if (failed != null) {
            this.getCellInventory()
                .injectItems(
                    AEItemStack.create(failed),
                    Actionable.MODULATE,
                    new MachineSource(this.getPatternTerminal()));
          }
        }
      }
    }
  }
  @Override
  public void serverPacketData(INetworkInfo manager, AppEngPacket packet, EntityPlayer player) {
    EntityPlayerMP pmp = (EntityPlayerMP) player;
    Container con = pmp.openContainer;

    if (con instanceof IContainerCraftingPacket) {
      IContainerCraftingPacket cct = (IContainerCraftingPacket) con;
      IGridNode node = cct.getNetworkNode();
      if (node != null) {
        IGrid grid = node.getGrid();
        if (grid == null) {
          return;
        }

        IStorageGrid inv = grid.getCache(IStorageGrid.class);
        IEnergyGrid energy = grid.getCache(IEnergyGrid.class);
        ISecurityGrid security = grid.getCache(ISecurityGrid.class);
        IInventory craftMatrix = cct.getInventoryByName("crafting");
        IInventory playerInventory = cct.getInventoryByName("player");

        Actionable realForFake = cct.useRealItems() ? Actionable.MODULATE : Actionable.SIMULATE;

        if (inv != null && this.recipe != null && security != null) {
          InventoryCrafting testInv = new InventoryCrafting(new ContainerNull(), 3, 3);
          for (int x = 0; x < 9; x++) {
            if (this.recipe[x] != null && this.recipe[x].length > 0) {
              testInv.setInventorySlotContents(x, this.recipe[x][0]);
            }
          }

          IRecipe r = Platform.findMatchingRecipe(testInv, pmp.worldObj);

          if (r != null && security.hasPermission(player, SecurityPermissions.EXTRACT)) {
            ItemStack is = r.getCraftingResult(testInv);

            if (is != null) {
              IMEMonitor<IAEItemStack> storage = inv.getItemInventory();
              IItemList all = storage.getStorageList();
              IPartitionList<IAEItemStack> filter = ItemViewCell.createFilter(cct.getViewCells());

              for (int x = 0; x < craftMatrix.getSizeInventory(); x++) {
                ItemStack patternItem = testInv.getStackInSlot(x);

                ItemStack currentItem = craftMatrix.getStackInSlot(x);
                if (currentItem != null) {
                  testInv.setInventorySlotContents(x, currentItem);
                  ItemStack newItemStack =
                      r.matches(testInv, pmp.worldObj) ? r.getCraftingResult(testInv) : null;
                  testInv.setInventorySlotContents(x, patternItem);

                  if (newItemStack == null || !Platform.isSameItemPrecise(newItemStack, is)) {
                    IAEItemStack in = AEItemStack.create(currentItem);
                    if (in != null) {
                      IAEItemStack out =
                          realForFake == Actionable.SIMULATE
                              ? null
                              : Platform.poweredInsert(energy, storage, in, cct.getSource());
                      if (out != null) {
                        craftMatrix.setInventorySlotContents(x, out.getItemStack());
                      } else {
                        craftMatrix.setInventorySlotContents(x, null);
                      }

                      currentItem = craftMatrix.getStackInSlot(x);
                    }
                  }
                }

                // True if we need to fetch an item for the recipe
                if (patternItem != null && currentItem == null) {
                  // Grab from network by recipe
                  ItemStack whichItem =
                      Platform.extractItemsByRecipe(
                          energy,
                          cct.getSource(),
                          storage,
                          player.worldObj,
                          r,
                          is,
                          testInv,
                          patternItem,
                          x,
                          all,
                          realForFake,
                          filter);

                  // If that doesn't get it, grab exact items from network (?)
                  // TODO see if this code is necessary
                  if (whichItem == null) {
                    for (int y = 0; y < this.recipe[x].length; y++) {
                      IAEItemStack request = AEItemStack.create(this.recipe[x][y]);
                      if (request != null) {
                        if (filter == null || filter.isListed(request)) {
                          request.setStackSize(1);
                          IAEItemStack out =
                              Platform.poweredExtraction(energy, storage, request, cct.getSource());
                          if (out != null) {
                            whichItem = out.getItemStack();
                            break;
                          }
                        }
                      }
                    }
                  }

                  // If that doesn't work, grab from the player's inventory
                  if (whichItem == null && playerInventory != null) {
                    whichItem = extractItemFromPlayerInventory(player, realForFake, patternItem);
                  }

                  craftMatrix.setInventorySlotContents(x, whichItem);
                }
              }
              con.onCraftMatrixChanged(craftMatrix);
            }
          }
        }
      }
    }
  }