package pneumaticCraft.common.semiblock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidHandler;
import pneumaticCraft.common.item.Itemss;
import pneumaticCraft.common.network.GuiSynced;
import pneumaticCraft.common.util.IOHelper;
import pneumaticCraft.lib.Log;
import pneumaticCraft.lib.ModIds;
import pneumaticCraft.proxy.CommonProxy.EnumGuiId;
import appeng.api.AEApi;
import appeng.api.exceptions.FailedConnection;
import appeng.api.networking.GridFlags;
import appeng.api.networking.GridNotification;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridBlock;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.crafting.ICraftingGrid;
import appeng.api.networking.crafting.ICraftingPatternDetails;
import appeng.api.networking.crafting.ICraftingProvider;
import appeng.api.networking.crafting.ICraftingProviderHelper;
import appeng.api.networking.crafting.ICraftingWatcher;
import appeng.api.networking.crafting.ICraftingWatcherHost;
import appeng.api.networking.events.MENetworkCellArrayUpdate;
import appeng.api.networking.events.MENetworkCraftingPatternChange;
import appeng.api.networking.security.BaseActionSource;
import appeng.api.networking.storage.IStackWatcher;
import appeng.api.networking.storage.IStackWatcherHost;
import appeng.api.networking.ticking.IGridTickable;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.networking.ticking.TickingRequest;
import appeng.api.storage.ICellContainer;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.StorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IItemList;
import appeng.api.util.AECableType;
import appeng.api.util.AEColor;
import appeng.api.util.DimensionalCoord;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.Optional;
import cpw.mods.fml.common.Optional.Interface;
@Optional.InterfaceList({@Interface(iface = "appeng.api.networking.IGridHost", modid = ModIds.AE2), @Interface(iface = "appeng.api.networking.IGridBlock", modid = ModIds.AE2), @Interface(iface = "appeng.api.networking.crafting.ICraftingProvider", modid = ModIds.AE2), @Interface(iface = "appeng.api.networking.crafting.ICraftingWatcherHost", modid = ModIds.AE2), @Interface(iface = "appeng.api.networking.storage.IStackWatcherHost", modid = ModIds.AE2), @Interface(iface = "pneumaticCraft.common.semiblock.MEInventoryExtension", modid = ModIds.AE2), @Interface(iface = "appeng.api.storage.ICellContainer", modid = ModIds.AE2), @Interface(iface = "appeng.api.networking.ticking.IGridTickable", modid = ModIds.AE2)})
public class SemiBlockRequester extends SemiBlockLogistics implements ISpecificRequester, IProvidingInventoryListener,
IGridHost, IGridBlock, ICraftingProvider, ICraftingWatcherHost, IStackWatcherHost, ICellContainer,
IGridTickable{
public static final String ID = "logisticFrameRequester";
//AE2 integration
@GuiSynced
private boolean aeMode;
private Object gridNode;
private Object craftingGrid;
private Object stackWatcher;
private Object craftingWatcher;
private boolean needToCheckForInterface = true;
private final Map<TileEntity, Integer> providingInventories = new HashMap<TileEntity, Integer>();
@Override
public int getColor(){
return 0xFF0000FF;
}
@Override
public int amountRequested(ItemStack stack){
int totalRequestingAmount = getTotalRequestedAmount(stack);
if(totalRequestingAmount > 0) {
IInventory inv = IOHelper.getInventoryForTE(getTileEntity());
int count = 0;
if(inv != null) {
for(int i = 0; i < inv.getSizeInventory(); i++) {
ItemStack s = inv.getStackInSlot(i);
if(s != null && isItemEqual(s, stack)) {
count += s.stackSize;
}
}
count += getIncomingItems(stack);
int requested = Math.max(0, Math.min(stack.stackSize, totalRequestingAmount - count));
return requested;
}
}
return 0;
}
private int getTotalRequestedAmount(ItemStack stack){
int requesting = 0;
for(int i = 0; i < getFilters().getSizeInventory(); i++) {
ItemStack requestingStack = getFilters().getStackInSlot(i);
if(requestingStack != null && isItemEqual(stack, requestingStack)) {
requesting += requestingStack.stackSize;
}
}
return requesting;
}
@Override
public int amountRequested(FluidStack stack){
int totalRequestingAmount = getTotalRequestedAmount(stack);
if(totalRequestingAmount > 0) {
TileEntity te = getTileEntity();
if(te instanceof IFluidHandler) {
int count = 0;
for(ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) {
FluidTankInfo[] infos = ((IFluidHandler)te).getTankInfo(d);
if(infos != null) {
for(FluidTankInfo info : infos) {
if(info.fluid != null && info.fluid.getFluid() == stack.getFluid()) {
count += info.fluid.amount;
}
}
if(count > 0) break;
}
}
count += getIncomingFluid(stack.getFluid());
int requested = Math.max(0, Math.min(stack.amount, totalRequestingAmount - count));
return requested;
}
}
return 0;
}
private int getTotalRequestedAmount(FluidStack stack){
int requesting = 0;
for(int i = 0; i < 9; i++) {
FluidStack requestingStack = getTankFilter(i).getFluid();
if(requestingStack != null && requestingStack.getFluid() == stack.getFluid()) {
requesting += requestingStack.amount;
}
}
return requesting;
}
@Override
public int getPriority(){
return 3;
}
@Override
public EnumGuiId getGuiID(){
return EnumGuiId.LOGISTICS_REQUESTER;
}
@Override
public boolean canFilterStack(){
return true;
}
/*
****************************************** Applied Energistics 2 Integration ***************************************************************
*/
@Override
@Optional.Method(modid = ModIds.AE2)
public void update(){
super.update();
if(!world.isRemote) {
if(needToCheckForInterface) {
if(Loader.isModLoaded(ModIds.AE2) && !world.isRemote && aeMode && gridNode == null) {
needToCheckForInterface = checkForInterface();
} else {
needToCheckForInterface = false;
}
}
Iterator<Map.Entry<TileEntity, Integer>> iterator = providingInventories.entrySet().iterator();
while(iterator.hasNext()) {
Map.Entry<TileEntity, Integer> entry = iterator.next();
if(entry.getValue() == 0 || entry.getKey().isInvalid()) {
iterator.remove();
} else {
entry.setValue(entry.getValue() - 1);
}
}
}
}
@Override
@Optional.Method(modid = ModIds.AE2)
public void handleGUIButtonPress(int guiID, EntityPlayer player){
if(guiID == 1) {
aeMode = !aeMode;
needToCheckForInterface = aeMode;
if(!aeMode && gridNode != null) {
disconnectFromInterface();
}
}
super.handleGUIButtonPress(guiID, player);
}
public boolean isIntegrationEnabled(){
return aeMode;
}
@Override
public void writeToNBT(NBTTagCompound tag){
super.writeToNBT(tag);
tag.setBoolean("aeMode", aeMode);
}
@Override
public void readFromNBT(NBTTagCompound tag){
super.readFromNBT(tag);
aeMode = tag.getBoolean("aeMode");
}
@Override
@Optional.Method(modid = ModIds.AE2)
public void invalidate(){
super.invalidate();
if(gridNode != null) {
disconnectFromInterface();
}
}
@Optional.Method(modid = ModIds.AE2)
public boolean isPlacedOnInterface(){
return getTileEntity() != null && AEApi.instance().definitions().blocks().iface().maybeBlock().get() == getTileEntity().getBlockType();
}
@Optional.Method(modid = ModIds.AE2)
private boolean checkForInterface(){
if(isPlacedOnInterface()) {
TileEntity te = getTileEntity();
if(te instanceof IGridHost) {
if(((IGridHost)te).getGridNode(null) == null) return true;
if(getGridNode(null) == null) return true;
try {
AEApi.instance().createGridConnection(getGridNode(null), ((IGridHost)te).getGridNode(null));
} catch(FailedConnection e) {
Log.error("Couldn't connect to an ME Interface!");
e.printStackTrace();
}
}
}
return false;
}
@Optional.Method(modid = ModIds.AE2)
private void disconnectFromInterface(){
((IGridNode)gridNode).destroy();
gridNode = null;
}
//IGridHost
@Override
@Optional.Method(modid = ModIds.AE2)
public AECableType getCableConnectionType(ForgeDirection arg0){
return AECableType.NONE;
}
@Override
@Optional.Method(modid = ModIds.AE2)
public IGridNode getGridNode(ForgeDirection d){
if(gridNode == null) {
gridNode = AEApi.instance().createGridNode(this);
}
return (IGridNode)gridNode;
}
@Override
@Optional.Method(modid = ModIds.AE2)
public void securityBreak(){
drop();
}
//IGridBlock
@Override
@Optional.Method(modid = ModIds.AE2)
public EnumSet<ForgeDirection> getConnectableSides(){
return null;//Shouldn't be called as isWorldAccessible is false.
}
@Override
@Optional.Method(modid = ModIds.AE2)
public EnumSet<GridFlags> getFlags(){
return EnumSet.noneOf(GridFlags.class);
}
@Override
@Optional.Method(modid = ModIds.AE2)
public AEColor getGridColor(){
return AEColor.Transparent;
}
@Override
public double getIdlePowerUsage(){
return 1;
}
@Override
@Optional.Method(modid = ModIds.AE2)
public DimensionalCoord getLocation(){
return new DimensionalCoord(world, getX(), getY(), getZ());
}
@Override
@Optional.Method(modid = ModIds.AE2)
public IGridHost getMachine(){
return this;
}
@Override
public ItemStack getMachineRepresentation(){
return new ItemStack(Itemss.logisticsFrameRequester);
}
@Override
public void gridChanged(){}
@Override
public boolean isWorldAccessible(){
return false;
}
@Override
@Optional.Method(modid = ModIds.AE2)
public void onGridNotification(GridNotification arg0){}
@Override
@Optional.Method(modid = ModIds.AE2)
public void setNetworkStatus(IGrid arg0, int arg1){}
//ICraftingProvider
@Override
public boolean isBusy(){
return true;
}
@Override
@Optional.Method(modid = ModIds.AE2)
public boolean pushPattern(ICraftingPatternDetails details, InventoryCrafting inventoryCrafting){
return false;
}
@Override
@Optional.Method(modid = ModIds.AE2)
public void provideCrafting(ICraftingProviderHelper helper){
updateProvidingItems(helper);
}
//ICraftingWatcherHost
@Override
@Optional.Method(modid = ModIds.AE2)
public void onRequestChange(ICraftingGrid grid, IAEItemStack aeStack){
craftingGrid = grid;
ItemStack stack = aeStack.getItemStack().copy();
int freeSlot = -1;
for(int i = 0; i < getFilters().getSizeInventory(); i++) {
ItemStack s = getFilters().getStackInSlot(i);
if(s != null) {
if(stack.isItemEqual(s)) {
s.stackSize = stack.stackSize;
if(s.stackSize == 0) getFilters().setInventorySlotContents(i, null);
return;
}
} else if(freeSlot == -1) {
freeSlot = i;
}
}
if(freeSlot >= 0) {
getFilters().setInventorySlotContents(freeSlot, stack.copy());
}
}
@Override
@Optional.Method(modid = ModIds.AE2)
public void updateWatcher(ICraftingWatcher watcher){
craftingWatcher = watcher;
updateProvidingItems();
}
//IStackWatcherHost
@Override
@Optional.Method(modid = ModIds.AE2)
public void onStackChange(IItemList arg0, IAEStack arg1, IAEStack arg2, BaseActionSource arg3, StorageChannel arg4){
if(craftingGrid != null) {
ICraftingGrid grid = (ICraftingGrid)craftingGrid;
for(int i = 0; i < getFilters().getSizeInventory(); i++) {
ItemStack s = getFilters().getStackInSlot(i);
if(s != null) {
if(!grid.isRequesting(AEApi.instance().storage().createItemStack(s))) {
getFilters().setInventorySlotContents(i, null);
notifyNetworkOfCraftingChange();
}
}
}
}
}
@Override
@Optional.Method(modid = ModIds.AE2)
public void updateWatcher(IStackWatcher watcher){
stackWatcher = watcher;
updateProvidingItems();
}
@Optional.Method(modid = ModIds.AE2)
private void updateProvidingItems(){
updateProvidingItems(null);
}
@Optional.Method(modid = ModIds.AE2)
private void notifyNetworkOfCraftingChange(){
if(gridNode != null) {
IGrid grid = ((IGridNode)gridNode).getGrid();
if(grid != null) grid.postEvent(new MENetworkCraftingPatternChange(this, (IGridNode)gridNode));
}
}
@Optional.Method(modid = ModIds.AE2)
private void updateProvidingItems(ICraftingProviderHelper cHelper){
IStackWatcher sWatcher = (IStackWatcher)stackWatcher;
ICraftingWatcher cWatcher = (ICraftingWatcher)craftingWatcher;
if(sWatcher != null) sWatcher.clear();
if(cWatcher != null) cWatcher.clear();
for(IAEItemStack stack : getProvidingItems()) {
if(sWatcher != null) sWatcher.add(stack);
if(cWatcher != null) cWatcher.add(stack);
if(cHelper != null) cHelper.setEmitable(stack);
}
}
@Override
public void notify(TileEntity te){
if(gridNode != null) providingInventories.put(te, 40);
}
@Optional.Method(modid = ModIds.AE2)
public List<IAEItemStack> getProvidingItems(){
List<IAEItemStack> stacks = new ArrayList<IAEItemStack>();
for(TileEntity te : providingInventories.keySet()) {
IInventory inv = IOHelper.getInventoryForTE(te);
if(inv != null) {
for(int i = 0; i < inv.getSizeInventory(); i++) {
ItemStack stack = inv.getStackInSlot(i);
if(stack != null) stacks.add(AEApi.instance().storage().createItemStack(stack));
}
}
}
return stacks;
}
//ICellContainer
@Override
@Optional.Method(modid = ModIds.AE2)
public IGridNode getActionableNode(){
return getGridNode(null);
}
@Override
@Optional.Method(modid = ModIds.AE2)
public List<IMEInventoryHandler> getCellArray(StorageChannel channel){
if(channel == StorageChannel.ITEMS) {
return Arrays.asList((IMEInventoryHandler)this);
} else {
return new ArrayList<IMEInventoryHandler>();
}
}
@Override
@Optional.Method(modid = ModIds.AE2)
public void saveChanges(IMEInventory arg0){
}
@Override
public void blinkCell(int arg0){
}
//IGridTickable
@Override
@Optional.Method(modid = ModIds.AE2)
public TickingRequest getTickingRequest(IGridNode node){
return new TickingRequest(120, 120, false, false);
}
@Override
@Optional.Method(modid = ModIds.AE2)
public TickRateModulation tickingRequest(IGridNode arg0, int arg1){
notifyNetworkOfCraftingChange();
if(gridNode != null) {
getGridNode(null).getGrid().postEvent(new MENetworkCellArrayUpdate());//Doing it on interval, as doing it right after AEApi.instance().createGridConnection doesn't seem to work..
}
return TickRateModulation.SAME;
}
}