package gr8pefish.ironbackpacks.container.backpack;
import gr8pefish.ironbackpacks.api.items.backpacks.interfaces.IBackpack;
import gr8pefish.ironbackpacks.capabilities.player.PlayerWearingBackpackCapabilities;
import gr8pefish.ironbackpacks.container.slot.AdvancedNestingBackpackSlot;
import gr8pefish.ironbackpacks.container.slot.BackpackSlot;
import gr8pefish.ironbackpacks.container.slot.NestingBackpackSlot;
import gr8pefish.ironbackpacks.integration.InterModSupport;
import gr8pefish.ironbackpacks.items.backpacks.ItemBackpack;
import gr8pefish.ironbackpacks.items.upgrades.UpgradeMethods;
import gr8pefish.ironbackpacks.util.helpers.IronBackpacksHelper;
import invtweaks.api.container.ChestContainer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.ClickType;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* The container of the backpack when it is opened normally.
*/
@ChestContainer //Inventory tweaks
public class ContainerBackpack extends Container {
private EntityPlayer player; //the player
private InventoryBackpack inventory; //the inventory
private ItemStack backpackStack; //the backpack as an itemstack
private ItemBackpack backpackItem; //the backpack as an items
private int xSize = 0; //the x size
private int ySize = 0; //the y size
private int totalSize = 0; //the total size
public ContainerBackpack(InventoryBackpack backpackInventory, int xSize, int ySize){
this.player = backpackInventory.getPlayer();
this.inventory = backpackInventory;
this.backpackStack = backpackInventory.getBackpackStack();
this.backpackItem = (ItemBackpack) backpackStack.getItem(); //TODO: hardcoded as ItemBackpack
this.xSize = xSize;
this.ySize = ySize;
this.totalSize = backpackItem.getSize(backpackStack);
layoutContainer(this.player.inventory, backpackInventory, xSize, ySize);
}
//overloaded constructor for when size is irrelevant
public ContainerBackpack(InventoryBackpack backpackInventory){
this.player = backpackInventory.getPlayer();
this.inventory = backpackInventory;
this.backpackStack = backpackInventory.getBackpackStack();
this.backpackItem = (ItemBackpack) backpackStack.getItem(); //TODO: hardcoded as ItemBackpack
this.totalSize = backpackItem.getSize(backpackStack);
layoutContainer(this.player.inventory, backpackInventory, xSize, ySize);
}
public EntityPlayer getPlayer() {
return player;
}
public InventoryBackpack getInventoryBackpack() {
return inventory;
}
/**
* Adds the slots to the continer.
* @param playerInventory - the player's inventory
* @param backpackInventory - the backpack inventory
* @param xSize - the x location
* @param ySize - the y location
*/
//credit to cpw here for basic layout of adding backpack's slots
protected void layoutContainer(IInventory playerInventory, IInventory backpackInventory, int xSize, int ySize){
//adds chest's slots
ItemStack baseBackpack = IronBackpacksHelper.getBackpack(player);
ArrayList<ItemStack> upgrades = IronBackpacksHelper.getUpgradesAppliedFromNBT(baseBackpack);
for (int backpackRow = 0; backpackRow < backpackItem.getRowCount(backpackStack); backpackRow++) {
for (int backpackCol = 0; backpackCol < backpackItem.getRowLength(backpackStack); backpackCol++) {
if (UpgradeMethods.hasNestingUpgrade(upgrades)){
addSlotToContainer(new NestingBackpackSlot(backpackInventory, backpackCol + backpackRow * backpackItem.getRowLength(backpackStack), 20 + backpackCol * 18, 18 + backpackRow * 18, backpackStack));
}else if (UpgradeMethods.hasNestingAdvancedUpgrade(upgrades)) {
addSlotToContainer(new AdvancedNestingBackpackSlot(backpackInventory, backpackCol + backpackRow * backpackItem.getRowLength(backpackStack), 20 + backpackCol * 18, 18 + backpackRow * 18, backpackStack));
}else{
addSlotToContainer(new BackpackSlot(backpackInventory, backpackCol + backpackRow * backpackItem.getRowLength(backpackStack), 20 + backpackCol * 18, 18 + backpackRow * 18));
}
}
}
//adds player's inventory
int leftCol = (xSize - 162) / 2 + 1;
for (int playerInvRow = 0; playerInvRow < 3; playerInvRow++){
for (int playerInvCol = 0; playerInvCol < 9; playerInvCol++){
addSlotToContainer(new Slot(playerInventory, playerInvCol + playerInvRow * 9 + 9, leftCol + playerInvCol * 18, ySize - (4 - playerInvRow) * 18 - 10));
}
}
//adds player's hotbar
for (int hotbarSlot = 0; hotbarSlot < 9; hotbarSlot++){
addSlotToContainer(new Slot(playerInventory, hotbarSlot, leftCol + hotbarSlot * 18, ySize - 24));
}
}
@Override //copied from IronChests
public ItemStack transferStackInSlot(EntityPlayer p, int i){
ItemStack itemstack = null;
Slot slot = (Slot) inventorySlots.get(i);
if (slot != null && slot.getHasStack()){
ItemStack itemstack1 = slot.getStack();
itemstack = itemstack1.copy();
if (i < totalSize){ //if clicking from backpack to player
if (!mergeItemStack(itemstack1, totalSize, inventorySlots.size(), true))
return null;
} else if (!((BackpackSlot) inventorySlots.get(1)).acceptsStack(itemstack1))
return null;
else if (!mergeItemStack(itemstack1, 0, totalSize, false))
return null;
if (itemstack1.stackSize == 0)
slot.putStack(null);
else
slot.onSlotChanged();
}
return itemstack;
}
/**
* Checks if the items can be put into the backpack, for use with the filter upgrade
* @param itemToPutInBackpack - the itemstack to put in
* @return - the remaining itemstack (null if it has been put it, the remaining otherwise)
*/
public ItemStack transferStackInSlot(ItemStack itemToPutInBackpack){
if (!mergeItemStack(itemToPutInBackpack, 0, backpackItem.getSize(backpackStack), false)) //stack, startIndex, endIndex, flag
return null;
else if (!((BackpackSlot) inventorySlots.get(1)).acceptsStack(itemToPutInBackpack)) //slot 1 is always a backpackSlot
return null;
return itemToPutInBackpack;
}
@Override
public boolean canInteractWith(EntityPlayer player) {
return true;
}
@Override
public void onContainerClosed(EntityPlayer player) {
super.onContainerClosed(player);
if (!player.worldObj.isRemote) //server side
this.inventory.onGuiSaved(player);
}
@Override
public ItemStack slotClick(int slot, int dragType, ClickType clickTypeIn, EntityPlayer player) {
// this will prevent the player from interacting with the items that opened the inventory:
ItemStack currPack = PlayerWearingBackpackCapabilities.getCurrentBackpack(player);
if (slot >= 0 && getSlot(slot) != null && getSlot(slot).getHasStack() && ItemStack.areItemStacksEqual(getSlot(slot).getStack(), currPack) && dragType == 0) {
return null;
}else if (dragType == 1 && slot >= 0 && getSlot(slot) != null && getSlot(slot).getHasStack()){ //right click on non-empty slot
if(getSlot(slot).getStack().getItem() instanceof IBackpack) { //has to be a backpack
ItemStack stack = getSlot(slot).getStack();
if (!ItemStack.areItemStackTagsEqual(stack, IronBackpacksHelper.getBackpack(player))) {//can't right click the same backpack you have open, causes it to not update correctly and dupe items
if (stack != null) {
stack.useItemRightClick(player.worldObj, player, EnumHand.MAIN_HAND);
}
}
return null;
}else if(InterModSupport.isEnderStorageLoaded && InterModSupport.isEnderPouch(getSlot(slot).getStack().getItem())) {
ItemStack stack = getSlot(slot).getStack();
stack.useItemRightClick(player.worldObj, player, EnumHand.MAIN_HAND);
return null;
}else if(getSlot(slot).getStack().getItem() instanceof IInventory) { //TODO: Dangerous inter-mod IInventory code, test this
// if (!(getSlot(slot).getStack().getItem() instanceof IBackpack)) { //my backpacks handled already by client mouse event
ItemStack stack = getSlot(slot).getStack();
if (stack != null) {
stack.useItemRightClick(player.worldObj, player, EnumHand.MAIN_HAND);
}
return null;
// }
//ToDo: Possibly add normal here and remove compat from clientClickEvent
}
}
return super.slotClick(slot, dragType, clickTypeIn, player);
}
//======================================================================HELPER METHODS=========================================================================
/**
* Saves via updating the inventory.
* @param player - the player
*/
public void save(EntityPlayer player) {
if (!player.worldObj.isRemote) { //server side
this.inventory.onGuiSaved(player);
}
}
/**
* Moves items from the backpack to the player's inventory
*/
public void backpackToInventory(){
for (int i = 0; i <= totalSize-1; i++) {
transferStackInSlot(player, i);
}
}
/**
* Moves items from the player's inventory to the backpack
*/
public void inventoryToBackpack(){
int start = totalSize;
int end = start + player.inventory.getSizeInventory() - InventoryPlayer.getHotbarSize() - 5; //(4 for armor, 1 for offhand)
for (int i = start; i < end; i++){
transferStackInSlot(player, i);
}
}
/**
* Moves items from the player's hotbar to the backpack
*/
public void hotbarToBackpack(){
int start = totalSize + player.inventory.getSizeInventory() - InventoryPlayer.getHotbarSize() - 5;
int end = start + InventoryPlayer.getHotbarSize(); //offhand
ItemStack openedPack = IronBackpacksHelper.getBackpack(player); //access once here instead of in the loop
for (int slot = start; slot < end; slot++){ //for each slot in hotbar
if (slot >= 0 && getSlot(slot) != null && getSlot(slot).getHasStack()) { //non-empty slot
if (!(getSlot(slot).getStack().getItem() instanceof IBackpack)) { //not a backpack
transferStackInSlot(player, slot); //transfer it
} else {
ItemStack stack = getSlot(slot).getStack();
if (!ItemStack.areItemStackTagsEqual(stack, openedPack)) { //can't move the same backpack you have open
transferStackInSlot(player, slot);
}
}
}
}
}
@ChestContainer.RowSizeCallback //Inventory tweaks compatibility
public int getNumColumns(){
return backpackItem.getRowLength(backpackStack);
}
@ChestContainer.IsLargeCallback //Inventory tweaks compatibility
public boolean getVerticalButtons(){
return false;
}
//===================================================================Sorting Algorithm==================================================================
//TODO: comment/document these more
/**
* Sorts the backpack by merging the stacks that can be, swapping out empty spaced to condense the pack, and then reorders via alphabetization of the stacks' display names.
*/
public void sort(){
if (!inventorySlots.isEmpty() && !inventoryItemStacks.isEmpty()){
mergeStacks(); //merge all the items together into the least possible number of stacks
swapStacks(); //then swap out the null stacks/blank spaces so that the backpack's stacks with items are condensed into the smallest area possible
reorderStacks(); //finally reorder the stacks (I am using alphabetization by the stack's display names).
}
}
private void mergeStacks(){
for (int i = 0; i < totalSize; i++){
Slot tempSlot = (Slot) inventorySlots.get(i);
if (tempSlot!= null && tempSlot.getHasStack()){
ItemStack tempStack = tempSlot.getStack();
if (tempStack!= null && tempStack.stackSize < tempStack.getMaxStackSize()){
fillSlot(tempSlot, i+1);
}
}
}
}
private void fillSlot(Slot slotToFill, int startIndex){
ItemStack stackToFill = slotToFill.getStack();
int fillAmt = stackToFill.getMaxStackSize() - stackToFill.stackSize;
if (fillAmt > 0){
for (int i = startIndex; i < totalSize; i++){
stackToFill = slotToFill.getStack();
fillAmt = stackToFill.getMaxStackSize() - stackToFill.stackSize;
Slot tempSlot = (Slot) inventorySlots.get(i);
if (tempSlot != null && tempSlot.getHasStack()){
ItemStack tempStack = tempSlot.getStack();
if (tempStack.stackSize > 0 && tempStack.isItemEqual(stackToFill) && ItemStack.areItemStackTagsEqual(tempStack, stackToFill)){
if (tempStack.stackSize > fillAmt){
tempSlot.decrStackSize(fillAmt);
slotToFill.putStack(new ItemStack(stackToFill.getItem(), stackToFill.getMaxStackSize(), stackToFill.getItemDamage()));
slotToFill.onSlotChanged();
break;
}else{
tempSlot.putStack(null);
slotToFill.putStack(new ItemStack(stackToFill.getItem(), stackToFill.stackSize + tempStack.stackSize, stackToFill.getItemDamage()));
slotToFill.onSlotChanged();
}
}
}
}
}
}
private void swapStacks(){
ArrayList<Integer> indicesOfSlotsWithItems = new ArrayList<Integer>();
for (int i = 0; i < totalSize; i++){
Slot tempSlot = (Slot) inventorySlots.get(i);
if (tempSlot!= null && tempSlot.getHasStack()){
ItemStack tempStack = tempSlot.getStack();
if (tempStack != null && tempStack.stackSize > 0){
indicesOfSlotsWithItems.add(i);
}
}
}
if (!indicesOfSlotsWithItems.isEmpty()) {
if (indicesOfSlotsWithItems.get(indicesOfSlotsWithItems.size() - 1) != (indicesOfSlotsWithItems.size() - 1)) { //if not already swapped so no null slots,
for (int i = 0; i < indicesOfSlotsWithItems.size(); i++) {
Slot tempSlot = (Slot) inventorySlots.get(i);
if (tempSlot != null) {
if (!tempSlot.getHasStack()) {
swapNull(tempSlot, (Slot) inventorySlots.get(indicesOfSlotsWithItems.get(i)));
}
}
}
}
}
}
private void swapNull(Slot nullSlot, Slot stackSlot){
if (stackSlot != null && stackSlot.getHasStack()) {
nullSlot.putStack(stackSlot.getStack());
stackSlot.putStack(null);
stackSlot.onSlotChanged();
}
}
private void reorderStacks(){
ArrayList<ItemStack> itemStacks = new ArrayList<ItemStack>(countLengthOfStacks());
for (int i = 0; i < totalSize; i++){
Slot tempSlot = (Slot) inventorySlots.get(i);
if (tempSlot != null && tempSlot.getHasStack()) {
itemStacks.add(tempSlot.getStack());
} else {
break;
}
}
if (!itemStacks.isEmpty()) {
if (this.inventory.getSortType().equals("id")) { //sort by ID
Collections.sort(itemStacks, new ItemStackIDComparator());
} else { //sort by name
Collections.sort(itemStacks, new ItemStackNameComparator());
}
for (int i = 0; i < itemStacks.size(); i++) {
Slot tempSlot = (Slot) inventorySlots.get(i);
tempSlot.putStack(itemStacks.get(i));
}
}
}
private class ItemStackNameComparator implements Comparator<ItemStack> {
@Override
public int compare(ItemStack stack1, ItemStack stack2) {
return stack1.getDisplayName().compareToIgnoreCase(stack2.getDisplayName());
}
}
private class ItemStackIDComparator implements Comparator<ItemStack> {
@Override
public int compare(ItemStack stack1, ItemStack stack2) {
int item1ID;
int item2ID;
if (stack1.getItem() instanceof IBackpack)
item1ID = -100;
else
item1ID = Item.getIdFromItem(stack1.getItem());
if (stack2.getItem() instanceof IBackpack)
item2ID = -100;
else
item2ID = Item.getIdFromItem(stack2.getItem());
if (item1ID == item2ID) return 0;
return (item1ID > item2ID) ? 1 : -1;
}
}
private int countLengthOfStacks(){
int total = 0;
for (int i = 0; i < totalSize; i++){
Slot tempSlot = (Slot) inventorySlots.get(i);
if (tempSlot != null && tempSlot.getHasStack()) {
total++;
} else {
return total;
}
}
return total;
}
}