/**
   * Updates the arcane recipe.
   *
   * @param inv
   */
  @Override
  public void onCraftMatrixChanged(final IInventory inv) {
    // Set the active recipe
    this.activeRecipe =
        ArcaneRecipeHelper.INSTANCE.findMatchingArcaneResult(
            inv, CRAFTING_MATRIX_SLOT, 9, this.player);

    ItemStack craftResult = null;

    // Set the result slot
    if (this.activeRecipe != null) {
      craftResult =
          ArcaneRecipeHelper.INSTANCE.getRecipeOutput(
              inv, CRAFTING_MATRIX_SLOT, 9, this.activeRecipe);
    }

    this.resultSlot.putStack(craftResult);

    // Update the save state
    if (EffectiveSide.isServerSide()) {
      this.sendSaveState(false);
    }

    // Sync
    this.detectAndSendChanges();
  }
  /**
   * Determines the current save state.
   *
   * @return
   */
  private CoreSaveState getSaveState() {
    CoreSaveState saveState;

    // Is there a core handler?
    if (!this.kCoreHandler.hasCore()) {
      saveState = CoreSaveState.Disabled_MissingCore;
    }
    // Is there a valid recipe?
    else if (this.activeRecipe == null) {
      saveState = CoreSaveState.Disabled_InvalidRecipe;
    } else {
      // Get the recipe output
      ItemStack recipeOutput =
          ArcaneRecipeHelper.INSTANCE.getRecipeOutput(
              this.internalInventory, CRAFTING_MATRIX_SLOT, 9, this.activeRecipe);

      // Ensure there is an output
      if (recipeOutput == null) {
        saveState = CoreSaveState.Disabled_InvalidRecipe;
      } else {
        // Does the core already have this recipe, or one that produces the same result, stored?
        boolean isNew = !this.kCoreHandler.hasPatternFor(recipeOutput);

        // Would the recipe be a new pattern?
        if (isNew) {
          // Is there room for the recipe?
          if (this.kCoreHandler.hasRoomToStorePattern()) {
            // Enable saving
            saveState = CoreSaveState.Enabled_Save;
          } else {
            // Core is full
            saveState = CoreSaveState.Disabled_CoreFull;
          }
        } else {
          // Enable deleting
          saveState = CoreSaveState.Enabled_Delete;
        }
      }
    }

    return saveState;
  }
  /**
   * Attempts to save or delete the active pattern from the kcore.
   *
   * @param player
   */
  public void onClientRequestSaveOrDelete(final EntityPlayer player) {
    // Get the current save state
    CoreSaveState saveState = this.getSaveState();

    int gridSize =
        ContainerKnowledgeInscriber.CRAFTING_ROWS * ContainerKnowledgeInscriber.CRAFTING_COLS;
    Object[] inputs = new Object[gridSize];

    if (saveState == CoreSaveState.Enabled_Save) {
      // Build the ingredient list

      // Is the recipe shaped?
      if (this.activeRecipe instanceof ShapedArcaneRecipe) {
        ShapedArcaneRecipe recipe = (ShapedArcaneRecipe) this.activeRecipe;
        for (int slotNumber = 0; slotNumber < recipe.input.length; ++slotNumber) {
          inputs[slotNumber] = this.preparePatternInput(recipe.input[slotNumber], slotNumber);
        }
      }
      // Is the recipe shapeless?
      else if (this.activeRecipe instanceof ShapelessArcaneRecipe) {
        ShapelessArcaneRecipe recipe = (ShapelessArcaneRecipe) this.activeRecipe;
        ArrayList ings = recipe.getInput();
        for (int slotNumber = 0; slotNumber < ings.size(); ++slotNumber) {
          inputs[slotNumber] = this.preparePatternInput(ings.get(slotNumber), slotNumber);
        }
      } else {
        // Unknown recipe type.
        return;
      }

      // Get the aspect cost
      AspectList recipeAspects =
          ArcaneRecipeHelper.INSTANCE.getRecipeAspectCost(
              this.internalInventory, CRAFTING_MATRIX_SLOT, 9, this.activeRecipe);

      // Create the pattern
      ArcaneCraftingPattern pattern =
          new ArcaneCraftingPattern(
              this.kCoreSlot.getStack(), recipeAspects, this.resultSlot.getStack(), inputs);

      // Add the pattern
      this.kCoreHandler.addPattern(pattern);

      // Update the slots
      this.updatePatternSlots();
      this.loadPattern(pattern);

      // Update the save state
      this.sendSaveState(true);

      // Mark the inscriber as dirty
      this.inscriber.markDirty();
    } else if (saveState == CoreSaveState.Enabled_Delete) {
      // Get the pattern for the result item
      ArcaneCraftingPattern pattern =
          this.kCoreHandler.getPatternForItem(this.resultSlot.getStack());

      // Ensure there is a pattern for it
      if (pattern != null) {
        // Remove it
        this.kCoreHandler.removePattern(pattern);

        // Update the slots
        this.updatePatternSlots();

        // Update the save state
        this.sendSaveState(false);

        // Mark the inscriber as dirty
        this.inscriber.markDirty();
      }
    }
  }