package crazypants.enderio.machine.vacuum;

import java.util.List;

import net.minecraft.command.IEntitySelector;
import net.minecraft.entity.Entity;
import net.minecraft.entity.IProjectile;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.AxisAlignedBB;
import crazypants.enderio.EnderIO;
import crazypants.enderio.ModObject;
import crazypants.enderio.TileEntityEio;
import crazypants.enderio.conduit.item.FilterRegister;
import crazypants.enderio.conduit.item.filter.IItemFilter;
import crazypants.enderio.conduit.item.filter.ItemFilter;
import crazypants.enderio.config.Config;
import crazypants.enderio.machine.IRedstoneModeControlable;
import crazypants.enderio.machine.RedstoneControlMode;
import crazypants.render.BoundingBox;
import crazypants.util.ItemUtil;
import net.minecraft.block.Block;

public class TileVacuumChest extends TileEntityEio implements IEntitySelector, IInventory, IRedstoneModeControlable {

  public static final int ITEM_ROWS = 3;
  public static final int ITEM_SLOTS = 9*ITEM_ROWS;
  public static final int FILTER_SLOTS = 5;

  private final ItemStack[] inv = new ItemStack[ITEM_SLOTS];
  private int range = Config.vacuumChestRange;
  private ItemFilter filter;
  private ItemStack filterItem;

  protected RedstoneControlMode redstoneControlMode = RedstoneControlMode.IGNORE;
  protected boolean redstoneCheckPassed;
  private boolean redstoneStateDirty = true;

  @Override
  public void updateEntity() {
    if(redstoneStateDirty) {
      updateRedstoneStatus();
    }
    if(redstoneCheckPassed && !isFull()) {
      doHoover();
    }
  }

  private void updateRedstoneStatus() {
    boolean prevRedstoneCheckPassed = redstoneCheckPassed;
    redstoneCheckPassed = RedstoneControlMode.isConditionMet(redstoneControlMode, this);
    redstoneStateDirty = false;
    if(redstoneCheckPassed != prevRedstoneCheckPassed) {
      updateBlock();
    }
  }

  public void onNeighborBlockChange(Block blockId) {
    redstoneStateDirty = true;
  }

  @Override
  public boolean isEntityApplicable(Entity entity) {
    if(entity.isDead) {
      return false;
    }
    if(entity instanceof IProjectile) {
      return entity.motionY < 0.01;
    }
    if(entity instanceof EntityItem) {
      ItemStack stack = ((EntityItem) entity).getEntityItem();
      return true;
    }
    return false;
  }

  private void doHoover() {

    int rangeSqr = range*range;
    BoundingBox bb = new BoundingBox(getLocation());
    AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
    aabb = aabb.expand(range, range, range);
    List<EntityItem> interestingItems = worldObj.selectEntitiesWithinAABB(EntityItem.class, aabb, this);

    for (EntityItem entity : interestingItems) {
      if(filter == null || filter.doesItemPassFilter(entity.getEntityItem())) {
        double x = (xCoord + 0.5D - entity.posX);
        double y = (yCoord + 0.5D - entity.posY);
        double z = (zCoord + 0.5D - entity.posZ);

        double distance = Math.sqrt(x * x + y * y + z * z);
        if(distance < 1.25) {
          hooverEntity(entity);
        } else {
          double speed = 0.06;
          double distScale = 1.0 - Math.min(0.9, distance / rangeSqr);
          distScale *= distScale;

          entity.motionX += x / distance * distScale * speed;
          entity.motionY += y / distance * distScale * 0.2;
          entity.motionZ += z / distance * distScale * speed;
        }
      }
    }
  }

  private void hooverEntity(Entity entity) {
    if(!worldObj.isRemote) {
      if(entity instanceof EntityItem && !entity.isDead) {
        EntityItem item = (EntityItem) entity;
        ItemStack stack = item.getEntityItem().copy();

        int numInserted = ItemUtil.doInsertItem(this, stack, null);
        stack.stackSize -= numInserted;
        item.setEntityItemStack(stack);
        if(stack.stackSize == 0) {
          item.setDead();
        }
      }
    }
  }

  private boolean isFull() {
    for (ItemStack stack : inv) {
      if(stack == null || stack.stackSize < stack.getMaxStackSize()) {
        return false;
      }
    }
    return true;
  }

  @Override
  public int getSizeInventory() {
    return inv.length;
  }

  @Override
  public int getInventoryStackLimit() {
    return 64;
  }

  @Override
  public ItemStack getStackInSlot(int slot) {
    if(slot < 0 || slot >= inv.length) {
      return null;
    }
    return inv[slot];
  }

  @Override
  public ItemStack decrStackSize(int fromSlot, int amount) {
    ItemStack fromStack = inv[fromSlot];
    if(fromStack == null) {
      return null;
    }
    if(fromStack.stackSize <= amount) {
      inv[fromSlot] = null;
      return fromStack;
    }
    ItemStack result = new ItemStack(fromStack.getItem(), amount, fromStack.getItemDamage());
    if(fromStack.stackTagCompound != null) {
      result.stackTagCompound = (NBTTagCompound) fromStack.stackTagCompound.copy();
    }
    fromStack.stackSize -= amount;
    return result;
  }

  @Override
  public void setInventorySlotContents(int slot, ItemStack contents) {

    if(slot < 0 || slot >= inv.length) {
      System.out.println("TileVacumChest.setInventorySlotContents: " + slot);
      return;
    }

    if(contents == null) {
      inv[slot] = contents;
    } else {
      inv[slot] = contents.copy();
    }

    if(contents != null && contents.stackSize > getInventoryStackLimit()) {
      contents.stackSize = getInventoryStackLimit();
    }
  }

  @Override
  public ItemStack getStackInSlotOnClosing(int var1) {
    return null;
  }

  @Override
  public String getInventoryName() {
    return ModObject.blockVacuumChest.unlocalisedName;
  }

  @Override
  public boolean hasCustomInventoryName() {
    return false;
  }

  @Override
  public boolean isUseableByPlayer(EntityPlayer var1) {
    return true;
  }

  @Override
  public void openInventory() {
  }

  @Override
  public void closeInventory() {
  }

  @Override
  public boolean isItemValidForSlot(int var1, ItemStack var2) {
    return true;
  }

  public boolean isItemValidForFilter(ItemStack itemstack) {
    return itemstack != null && itemstack.getItem() == EnderIO.itemBasicFilterUpgrade && itemstack.getItemDamage() == 0;
  }

  public int getRange() {
    return range;
  }

  private int limitRange(int range) {
    return Math.max(1, Math.min(Config.vacuumChestRange, range));
  }

  public void setRange(int range) {
    this.range = limitRange(range);
    updateBlock();
  }

  public ItemStack getFilterItem() {
    return filterItem;
  }

  public void setFilterItem(ItemStack filterItem) {
    IItemFilter newFilter = FilterRegister.getFilterForUpgrade(filterItem);
    if(newFilter == null || newFilter instanceof ItemFilter) {
      this.filterItem = filterItem;
      this.filter = (ItemFilter)newFilter;
      updateBlock();
    }
  }

  public void setFilterBlacklist(boolean isBlacklist) {
    if(filter != null) {
      filter.setBlacklist(isBlacklist);
      updateFilterItem();
    }
  }

  public void setFilterMatchMeta(boolean matchMeta) {
    if(filter != null) {
      filter.setMatchMeta(matchMeta);
      updateFilterItem();
    }
  }

  public boolean hasItemFilter() {
    return filter != null;
  }

  public ItemFilter getItemFilter() {
    return filter;
  }

  public void setItemFilterSlot(int slot, ItemStack stack) {
    if(slot >= 0 && slot < FILTER_SLOTS && filter != null) {
      filter.setInventorySlotContents(slot, stack);
      updateFilterItem();
    }
  }

  private void updateFilterItem() {
    FilterRegister.writeFilterToStack(filter, filterItem);
    updateBlock();
  }

  @Override
  public RedstoneControlMode getRedstoneControlMode() {
    return redstoneControlMode;
  }

  @Override
  public void setRedstoneControlMode(RedstoneControlMode redstoneControlMode) {
    this.redstoneControlMode = redstoneControlMode;
    redstoneStateDirty = true;
    updateBlock();
  }

  @Override
  public void readCustomNBT(NBTTagCompound nbtRoot) {
    readContentsFromNBT(nbtRoot);
    redstoneCheckPassed = nbtRoot.getBoolean("redstoneCheckPassed");
  }

  public void readContentsFromNBT(NBTTagCompound nbtRoot) {
    NBTTagList itemList = (NBTTagList) nbtRoot.getTag("Items");
    if(itemList != null) {
      for (int i = 0; i < itemList.tagCount(); i++) {
        NBTTagCompound itemStack = itemList.getCompoundTagAt(i);
        byte slot = itemStack.getByte("Slot");
        if(slot >= 0 && slot < inv.length) {
          inv[slot] = ItemStack.loadItemStackFromNBT(itemStack);
        }
      }
    }
    if(nbtRoot.hasKey("range")) {
      range = limitRange(nbtRoot.getInteger("range"));
    } else {
      range = Config.vacuumChestRange;
    }
    if(nbtRoot.hasKey("filter")) {
      NBTTagCompound filterTag = (NBTTagCompound) nbtRoot.getTag("filter");
      filterItem = ItemStack.loadItemStackFromNBT(filterTag);
      IItemFilter flt = FilterRegister.getFilterForUpgrade(filterItem);
      if(flt instanceof ItemFilter) {
        filter = (ItemFilter)flt;
      } else {
        filterItem = null;
      }
    } else {
      filterItem = null;
      filter = null;
    }

    int rsContr = nbtRoot.getInteger("redstoneControlMode");
    if(rsContr < 0 || rsContr >= RedstoneControlMode.values().length) {
      rsContr = 0;
    }
    redstoneControlMode = RedstoneControlMode.values()[rsContr];
  }

  @Override
  public void writeCustomNBT(NBTTagCompound nbtRoot) {
    writeContentsToNBT(nbtRoot);
    nbtRoot.setBoolean("redstoneCheckPassed", redstoneCheckPassed);
  }

  public void writeContentsToNBT(NBTTagCompound nbtRoot) {
    NBTTagList itemList = new NBTTagList();
    for (int i = 0; i < inv.length; i++) {
      if(inv[i] != null) {
        NBTTagCompound itemStackNBT = new NBTTagCompound();
        itemStackNBT.setByte("Slot", (byte) i);
        inv[i].writeToNBT(itemStackNBT);
        itemList.appendTag(itemStackNBT);
      }
    }
    nbtRoot.setTag("Items", itemList);
    nbtRoot.setInteger("range", range);
    if(filterItem != null) {
      NBTTagCompound filterNBT = new NBTTagCompound();
      filterItem.writeToNBT(filterNBT);
      nbtRoot.setTag("filter", filterNBT);
    }
    nbtRoot.setInteger("redstoneControlMode", redstoneControlMode.ordinal());
  }

}
