@Override
  public void a(Packet108ButtonClick packet) {
    if (this.player.activeContainer instanceof ContainerEnchantTable)
      if (this.player.activeContainer.windowId == packet.a
          && this.player.activeContainer.c(this.player)) {
        // Store our pre-event values
        CraftPlayer player = (CraftPlayer) this.player.getBukkitEntity();
        Inventory inventory = getActiveInventory();
        ContainerEnchantTable table = (ContainerEnchantTable) this.player.activeContainer;
        ItemStack initial = table.a.getItem(0).cloneItemStack();
        int level = this.player.expLevel;

        if (table.a(this.player, packet.b)) {
          ItemStack after = ((ContainerEnchantTable) this.player.activeContainer).a.getItem(0);
          int afterLevel = this.player.expLevel;

          SpoutCraftItemStack before = SpoutCraftItemStack.fromItemStack(initial);
          SpoutCraftItemStack result = SpoutCraftItemStack.fromItemStack(after);
          InventoryEnchantEvent event =
              new InventoryEnchantEvent(player, inventory, before, result, level, afterLevel);
          Bukkit.getServer().getPluginManager().callEvent(event);

          if (event.isCancelled()) {
            player.setLevel(level);
            table.a.setItem(0, initial);
          } else {
            player.setLevel(event.getLevelAfter());
            table.a.setItem(0, SpoutCraftItemStack.createNMSItemStack(event.getResult()));
          }
        }
        this.player.activeContainer.a();
      } else super.a(packet);
  }
  public boolean handleInventoryClick(
      Packet102WindowClick packet,
      InventorySlotType type,
      SpoutCraftItemStack slot,
      SpoutCraftItemStack cursor,
      Inventory inventory) {
    InventoryClickEvent event = null;
    Result result = Result.DEFAULT;
    boolean success = false;
    final int LEFT_CLICK = 0;
    final int RIGHT_CLICK = 1;
    int click = packet.c;

    // clicked on bottom player inventory
    if (!(this.player.activeContainer instanceof ContainerPlayer)
        && this.player.defaultContainer instanceof ContainerPlayer
        && packet.b >= inventory.getSize()) {
      int activeSlot = packet.b - inventory.getSize() + 9;
      if (activeSlot >= this.getPlayer().getInventory().getSize()) {
        activeSlot -= this.getPlayer().getInventory().getSize();
      }
      type = getInventorySlotType(activeSlot);
      event =
          new InventoryPlayerClickEvent(
              this.getPlayer(),
              this.getPlayer().getInventory(),
              type,
              slot,
              cursor,
              activeSlot,
              click == LEFT_CLICK,
              packet.f,
              activeLocation);
    } else {
      event =
          new InventoryClickEvent(
              this.getPlayer(),
              inventory,
              type,
              slot,
              cursor,
              packet.b,
              click == LEFT_CLICK,
              packet.f,
              activeLocation);
    }

    if (event != null) {
      Bukkit.getServer().getPluginManager().callEvent(event);
      result = event.getResult();
      cursor = SpoutCraftItemStack.getCraftItemStack(event.getCursor());
      slot = SpoutCraftItemStack.getCraftItemStack(event.getItem());
    }

    // initialize setup
    ItemStack itemstack = slot != null ? slot.getHandle() : null;
    ItemStack cursorstack = cursor != null ? cursor.getHandle() : null;

    // NOTE: Successful means that its successful as-is; thus, only becomes true for default
    // behaviour

    switch (result) {
      case DEFAULT:
        itemstack = this.player.activeContainer.a(packet.b, packet.c, packet.f, this.player);
        success = ItemStack.equals(packet.e, itemstack);
        break;
      case DENY:
        if (packet.b != -999) { // Only swap if target is not OUTSIDE
          if (itemstack != null) {
            setActiveSlot(packet.b, itemstack);
            setCursorSlot((ItemStack) null);
          }
          if (event.getCursor() != null) {
            setActiveSlot(packet.b, itemstack);
            setCursorSlot(cursorstack);
          }
        }

        break;
      case ALLOW: // Allow the placement unconditionally
        if (packet.b == -999) { // Clicked outside, just defer to default
          itemstack = this.player.activeContainer.a(packet.b, packet.c, packet.f, this.player);
        } else {
          if (click == LEFT_CLICK
              && (itemstack != null
                  && cursorstack != null
                  && itemstack.doMaterialsMatch(cursorstack))) {
            // Left-click full slot with full cursor of same item; merge stacks
            itemstack.count += cursorstack.count;
            cursorstack = null;
          } else if (click == LEFT_CLICK
              || (itemstack != null
                  && cursorstack != null
                  && !itemstack.doMaterialsMatch(cursorstack))) {
            // Either left-click, or right-click full slot with full cursor of different item; just
            // swap contents
            ItemStack temp = itemstack;
            itemstack = cursorstack;
            cursorstack = temp;
          } else if (click == RIGHT_CLICK) { // Right-click with either slot or cursor empty
            if (itemstack == null) { // Slot empty; drop one
              if (cursorstack != null) {
                itemstack = cursorstack.a(1);
                if (cursorstack.count == 0) {
                  cursorstack = null;
                }
              }
            } else if (cursorstack == null) { // Cursor empty; take half
              cursorstack = itemstack.a((itemstack.count + 1) / 2);
            } else { // Neither empty, but same item; drop one
              ItemStack drop = cursorstack.a(1);
              itemstack.count += drop.count;
              if (cursorstack.count == 0) {
                cursorstack = null;
              }
            }
          }
          // update the stacks
          setActiveSlot(packet.b, itemstack);
          setCursorSlot(cursorstack);
        }
        break;
    }
    return success;
  }
  @Override
  public void a(Packet102WindowClick packet) {
    if (this.player.dead) return;

    if (this.player.activeContainer.windowId == packet.a
        && this.player.activeContainer.c(this.player)) {
      Inventory inventory = getActiveInventory();
      CraftPlayer player = (CraftPlayer) this.player.getBukkitEntity();
      ItemStack before = ItemStack.b(packet.e);
      ItemStack cursorBefore = this.player.inventory.l();
      SpoutCraftItemStack slot = SpoutCraftItemStack.fromItemStack(before);
      SpoutCraftItemStack cursor = SpoutCraftItemStack.fromItemStack(cursorBefore);
      InventorySlotType type = getActiveInventorySlotType(packet.b);
      boolean clickSuccessful = true;
      final int windowId = packet.a;

      // alert of a newly opened inventory
      if (!activeInventory) {
        activeInventory = true;
        InventoryOpenEvent event =
            new InventoryOpenEvent(player, inventory, getDefaultInventory(), activeLocation);
        Bukkit.getServer().getPluginManager().callEvent(event);
        if (event.isCancelled()) {
          this.player.D();
          activeInventory = false;
          activeLocation = null;
          return;
        }
      }

      // Fire InventoryChange or InventoryCraft event
      if (packet.b != -999) {
        if (inventory instanceof CraftingInventory) {
          CraftingInventory crafting = (CraftingInventory) inventory;
          InventoryCrafting recipe = null;
          if (inventory instanceof SpoutCraftingInventory) {
            recipe = ((SpoutCraftingInventory) crafting).getMatrixHandle();
          } else {
            recipe = (InventoryCrafting) ((SpoutCraftInventoryPlayer) crafting).getMatrixHandle();
          }

          SpoutCraftItemStack craftResult =
              SpoutCraftItemStack.fromItemStack(CraftingManager.getInstance().craft(recipe));
          SpoutCraftItemStack[] recipeContents = new SpoutCraftItemStack[recipe.getSize()];
          for (int i = 0; i < recipe.getSize(); i++) {
            org.bukkit.inventory.ItemStack temp = crafting.getMatrix()[i];
            recipeContents[i] = SpoutCraftItemStack.getCraftItemStack(temp);
          }

          SpoutCraftItemStack[][] matrix = null;
          if (recipe.getSize() == 4) {
            matrix =
                new SpoutCraftItemStack[][] {
                  Arrays.copyOfRange(recipeContents, 0, 2), Arrays.copyOfRange(recipeContents, 2, 4)
                };
          } else if (recipe.getSize() == 9) {
            matrix =
                new SpoutCraftItemStack[][] {
                  Arrays.copyOfRange(recipeContents, 0, 3),
                  Arrays.copyOfRange(recipeContents, 3, 6),
                  Arrays.copyOfRange(recipeContents, 6, 9)
                };
          }
          // Clicking to grab the crafting result
          if (type == InventorySlotType.RESULT) {
            InventoryCraftEvent craftEvent =
                new InventoryCraftEvent(
                    this.getPlayer(),
                    crafting,
                    this.activeLocation,
                    type,
                    packet.b,
                    matrix,
                    craftResult,
                    cursor,
                    packet.c == 0,
                    packet.f);
            Bukkit.getServer().getPluginManager().callEvent(craftEvent);
            craftEvent.getInventory().setResult(craftEvent.getResult());
            cursor = SpoutCraftItemStack.getCraftItemStack(craftEvent.getCursor());
            if (craftEvent.isCancelled()) {
              craftEvent.getInventory().setMatrix(recipeContents);
              setCursorSlot(cursor != null ? cursor.getHandle() : null);
              clickSuccessful = false;
            }
          }
        }
      }

      if (clickSuccessful) {
        clickSuccessful = handleInventoryClick(packet, type, slot, cursor, inventory);
      }

      if (clickSuccessful) {
        this.player.netServerHandler.sendPacket(new Packet106Transaction(windowId, packet.d, true));
        this.player.h = true;
        this.player.activeContainer.a();
        this.player.D();
        this.player.h = false;
      } else {
        this.getEntityList()
            .a(Integer.valueOf(this.player.activeContainer.windowId), Short.valueOf(packet.d));
        this.player.netServerHandler.sendPacket(
            new Packet106Transaction(windowId, packet.d, false));
        this.player.activeContainer.a(this.player, false);
        ArrayList<ItemStack> arraylist = new ArrayList<ItemStack>();

        for (int i = 0; i < this.player.activeContainer.e.size(); ++i) {
          arraylist.add(((Slot) this.player.activeContainer.e.get(i)).getItem());
        }

        this.player.a(this.player.activeContainer, arraylist);
      }
    }
  }