com.kaijin.AdvPowerMan.tileentities.TEChargingBench.java Source code

Java tutorial

Introduction

Here is the source code for com.kaijin.AdvPowerMan.tileentities.TEChargingBench.java

Source

/*******************************************************************************
 * Copyright (c) 2012-2013 Yancarlo Ramsey and CJ Bowman
 * Licensed as open source with restrictions. Please see attached LICENSE.txt.
 ******************************************************************************/
package com.kaijin.AdvPowerMan.tileentities;

import ic2.api.Direction;
import ic2.api.item.ElectricItem;
import ic2.api.item.IElectricItem;
import ic2.api.tile.IEnergyStorage;
import ic2.api.energy.event.EnergyTileLoadEvent;
import ic2.api.energy.tile.IEnergySink;
import ic2.core.IC2;
import ic2.core.network.NetworkManager;
import io.netty.buffer.ByteBuf;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import com.kaijin.AdvPowerMan.AdvancedPowerManagement;
import com.kaijin.AdvPowerMan.Info;
import com.kaijin.AdvPowerMan.MovingAverage;
import com.kaijin.AdvPowerMan.Utils;

import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

public class TEChargingBench extends TECommonBench
        implements IEnergySink, IEnergyStorage, IInventory, ISidedInventory {
    // Base values
    public int baseMaxInput;
    public int baseStorage;

    // Adjustable values that need communicating via container
    public int adjustedMaxInput;
    public int adjustedStorage;

    public int currentEnergy;
    // For outside texture display
    public int chargeLevel;

    public float drainFactor;
    public float chargeFactor;

    protected int energyReceived = 0;
    public MovingAverage inputTracker = new MovingAverage(12);

    public int ticksRequired = 0;
    public int energyRequired = 0;

    private static final int[] ChargingBenchSideInput = { Info.CB_SLOT_INPUT };
    private static final int[] ChargingBenchSideOutput = { Info.CB_SLOT_OUTPUT };
    private static final int[] ChargingBenchSideInOut = { Info.CB_SLOT_INPUT, Info.CB_SLOT_OUTPUT };
    private static final int[] ChargingBenchSidePower = { Info.CB_SLOT_POWER_SOURCE };

    public TEChargingBench() // Default constructor used only when loading tile
    // entity from world save
    {
        super();
        // Do nothing else; Creating the inventory array and loading previous
        // values will be handled in NBT read method momentarily.
    }

    public TEChargingBench(int i) // Constructor used when placing a new tile
    // entity, to set up correct parameters
    {
        super();
        contents = new ItemStack[19];

        // base tier = what we're passed, so 1, 2 or 3
        baseTier = i;
        initializeBaseValues();

        // setup Adjusted variables to = defaults, we'll be adjusting them in
        // entityUpdate
        adjustedMaxInput = baseMaxInput;
        adjustedStorage = baseStorage;

        powerTier = baseTier;

        drainFactor = 1.0F;
        chargeFactor = 1.0F;
    }

    protected void initializeBaseValues() {
        // if (ChargingBench.isDebugging)
        // System.out.println("Initializing - BaseTier: " + baseTier);

        // Max Input math = 32 for tier 1, 128 for tier 2, 512 for tier 3
        baseMaxInput = (int) Math.pow(2.0D, (double) (2 * baseTier + 3));
        // if (ChargingBench.isDebugging) System.out.println("BaseMaxInput: " +
        // baseMaxInput);

        switch (baseTier) {
        case 1:
            baseStorage = 40000;
            break;
        case 2:
            baseStorage = 600000;
            break;
        case 3:
            baseStorage = 10000000;
            break;
        default:
            baseStorage = 0;
        }
        // if (ChargingBench.isDebugging) System.out.println("BaseStorage: " +
        // baseStorage);
    }

    /**
     * Called to upgrade (or downgrade) a charging bench to a certain tier.
     * 
     * @param newTier
     *            The tier to replace the charging bench with, based on the
     *            component item used
     * @return the original tier of the charging bench, for creating the correct
     *         component item
     */
    public int swapBenchComponents(int newTier) {
        int oldTier = baseTier;
        baseTier = newTier;
        worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, Info.CB_META + newTier - 1, 3);
        initializeBaseValues();
        doUpgradeEffects();
        chargeLevel = gaugeEnergyScaled(12);
        worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
        return oldTier;
    }

    // IC2 API stuff

    // IEnergySink

    @Override
    public void setStored(int energy) {
        // What uses this?
    }

    @Override
    public int addEnergy(int amount) {
        // Returning our current energy value always, we do not implement this
        // function
        return currentEnergy;
    }

    @Override
    public int getSinkTier() {
        return adjustedMaxInput;
    }

    // IEnergyStorage

    /**
     * Get the amount of energy currently stored in the block.
     * 
     * @return Energy stored in the block
     */
    @Override
    public int getStored() {
        return currentEnergy;
    }

    /**
     * Get the maximum amount of energy the block can store.
     * 
     * @return Maximum energy stored
     */
    @Override
    public int getCapacity() {
        return adjustedStorage;
    }

    /**
     * Get the block's energy output.
     * 
     * @return Energy output in EU/t
     */
    @Override
    public int getOutput() {
        return 0;
    }

    // End IC2 API

    @Override
    public int getGuiID() {
        return Info.GUI_ID_CHARGING_BENCH;
    }

    /**
     * This will cause the block to drop anything inside it, create a new item
     * in the world of its type, invalidate the tile entity, remove itself from
     * the IC2 EnergyNet and clear the block space (set it to air)
     */
    @Override
    protected void selfDestroy() {
        dropContents();
        ItemStack stack = new ItemStack(AdvancedPowerManagement.blockAdvPwrMan, 1, Info.CB_META + baseTier - 1);
        dropItem(stack);
        worldObj.setBlockToAir(xCoord, yCoord, zCoord);
        this.invalidate();
    }

    public void doUpgradeEffects() {
        // Count our upgrades
        ItemStack stack;
        int ocCount = 0;
        int tfCount = 0;
        int esCount = 0;
        for (int i = Info.CB_SLOT_UPGRADE; i < Info.CB_SLOT_UPGRADE + 4; ++i) {
            stack = contents[i];
            if (stack != null) {
                if (stack.isItemEqual(Info.ic2overclockerUpg)) {
                    ocCount += stack.stackSize;
                } else if (stack.isItemEqual(Info.ic2storageUpg)) {
                    esCount += stack.stackSize;
                } else if (stack.isItemEqual(Info.ic2transformerUpg)) {
                    tfCount += stack.stackSize;
                }
            }
        }

        // Cap upgrades at sane quantities that won't result in negative energy
        // storage from integer overflows and such.
        if (ocCount > 20)
            ocCount = 20;
        if (esCount > 64)
            esCount = 64;
        if (tfCount > 3)
            tfCount = 3;

        // Overclockers:
        chargeFactor = (float) Math.pow(1.3F, ocCount); // 30% more power
        // transferred to an
        // item per overclocker,
        // exponential.
        drainFactor = (float) Math.pow(1.5F, ocCount); // 50% more power drained
        // per overclocker,
        // exponential. Yes, you
        // waste power, that's
        // how OCs work.

        // Transformers:
        powerTier = baseTier + tfCount; // Allows better energy storage items to
        // be plugged into the battery slot of
        // lower tier benches.
        if (powerTier > 3)
            powerTier = 3;

        adjustedMaxInput = (int) Math.pow(2.0D, (double) (2 * (baseTier + tfCount) + 3));
        if (adjustedMaxInput > 2048)
            adjustedMaxInput = 2048; // You can feed EV in with 1-4 TF upgrades,
        // if you so desire.

        // Energy Storage:
        switch (baseTier) {
        case 1:
            adjustedStorage = baseStorage + esCount * 10000; // LV: 25%
            // additional
            // storage per
            // upgrade
            // (10,000).
            break;
        case 2:
            adjustedStorage = baseStorage + esCount * 60000; // MV: 10%
            // additional
            // storage per
            // upgrade
            // (60,000).
            break;
        case 3:
            adjustedStorage = baseStorage + esCount * 500000; // HV: 5%
            // additional
            // storage per
            // upgrade
            // (500,000).
            break;
        default:
            adjustedStorage = baseStorage; // This shouldn't ever happen, but
            // just in case, it shouldn't crash
            // it - storage upgrades just won't
            // work.
        }
        if (currentEnergy > adjustedStorage)
            currentEnergy = adjustedStorage; // If storage has decreased, lose
        // any excess energy.
    }

    public boolean isItemValid(int slot, ItemStack stack) {
        // Decide if the item is a valid IC2 electrical item
        if (stack != null && stack.getItem() instanceof IElectricItem) {
            IElectricItem item = (IElectricItem) (stack.getItem());
            // Is the item appropriate for this slot?
            if (slot == Info.CB_SLOT_POWER_SOURCE && item.canProvideEnergy(stack)
                    && item.getTier(stack) <= powerTier)
                return true;
            if (slot >= Info.CB_SLOT_CHARGING && slot < Info.CB_SLOT_CHARGING + 12
                    && item.getTier(stack) <= baseTier)
                return true;
            if (slot >= Info.CB_SLOT_UPGRADE && slot < Info.CB_SLOT_UPGRADE + 4
                    && (stack.isItemEqual(Info.ic2overclockerUpg) || stack.isItemEqual(Info.ic2transformerUpg)
                            || stack.isItemEqual(Info.ic2storageUpg)))
                return true;
            if (slot == Info.CB_SLOT_INPUT && item.getTier(stack) <= baseTier)
                return true;
            if (slot == Info.CB_SLOT_OUTPUT)
                return true; // GUI won't allow placement of items here, but if
                             // the bench or an external machine does, it
                             // should at least let it sit there as long as
                             // it's an electrical item.
        }
        return false;
    }

    /**
     * Reads a tile entity from NBT.
     */
    @Override
    public void readFromNBT(NBTTagCompound nbttagcompound) {
        super.readFromNBT(nbttagcompound);

        if (Info.isDebugging)
            System.out.println("CB ID: " + nbttagcompound.getString("id"));

        baseTier = nbttagcompound.getInteger("baseTier");
        currentEnergy = nbttagcompound.getInteger("currentEnergy");
        // if (ChargingBench.isDebugging)
        // System.out.println("ReadNBT.CurrentEergy: " + currentEnergy);

        // Our inventory
        contents = new ItemStack[Info.CB_INVENTORY_SIZE];
        NBTTagList nbttaglist = nbttagcompound.getTagList("Items", Constants.NBT.TAG_COMPOUND);
        for (int i = 0; i < nbttaglist.tagCount(); ++i) {
            NBTTagCompound nbttagcompound1 = (NBTTagCompound) nbttaglist.getCompoundTagAt(i);
            int j = nbttagcompound1.getByte("Slot") & 255;

            if (j >= 0 && j < contents.length) {
                contents[j] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
            }
        }

        // We can calculate these, no need to save/load them.
        initializeBaseValues();
        doUpgradeEffects();
    }

    /**
     * Writes a tile entity to NBT.
     */
    @Override
    public void writeToNBT(NBTTagCompound nbttagcompound) {
        super.writeToNBT(nbttagcompound);

        nbttagcompound.setInteger("baseTier", baseTier);
        nbttagcompound.setInteger("currentEnergy", currentEnergy);
        // if (ChargingBench.isDebugging)
        // System.out.println("WriteNBT.CurrentEergy: " + currentEnergy);

        // Our inventory
        NBTTagList nbttaglist = new NBTTagList();
        for (int i = 0; i < contents.length; ++i) {
            if (contents[i] != null) {
                // if (ChargingBench.isDebugging)
                // System.out.println("WriteNBT contents[" + i + "] stack tag: "
                // + contents[i].stackTagCompound);
                NBTTagCompound nbttagcompound1 = new NBTTagCompound();
                nbttagcompound1.setByte("Slot", (byte) i);
                contents[i].writeToNBT(nbttagcompound1);
                nbttaglist.appendTag(nbttagcompound1);
            }
        }
        nbttagcompound.setTag("Items", nbttaglist);
    }

    @Override
    public void updateEntity() // TODO Marked for easy access
    {
        if (AdvancedPowerManagement.proxy.isClient()) {
            return;
        }

        if (!initialized && worldObj != null) {
            EnergyTileLoadEvent loadEvent = new EnergyTileLoadEvent(this);
            MinecraftForge.EVENT_BUS.post(loadEvent);
            // EnergyNet.getForWorld(worldObj).addTileEntity(this);
            initialized = true;
        }

        inputTracker.tick(energyReceived);
        energyReceived = 0;
        ticksRequired = 0;
        energyRequired = 0;

        boolean lastWorkState = doingWork;
        doingWork = false;

        // Work done every tick
        drainPowerSource();
        chargeItems();
        moveOutputItems();
        acceptInputItems();

        // Determine if and how completion time will be affected by lack of
        // energy and input rate
        if (energyRequired > currentEnergy) {
            final int deficit = energyRequired - currentEnergy;
            final float avg = inputTracker.getAverage();
            if (avg >= 1.0F) {
                final int time = (int) Math.ceil(((float) deficit) / avg);
                if (time > ticksRequired)
                    ticksRequired = time;
            } else
                ticksRequired = -1;
        }

        // Trigger this only when charge level passes where it would need to
        // update the client texture
        int oldChargeLevel = chargeLevel;
        chargeLevel = gaugeEnergyScaled(12);
        if (oldChargeLevel != chargeLevel || lastWorkState != doingWork) {
            // if (ChargingBench.isDebugging)
            // System.out.println("TE oldChargeLevel: " + oldChargeLevel +
            // " chargeLevel: " + chargeLevel);
            worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
        }
    }

    /**
     * Looks in the power item slot to see if it can pull in EU from a valid
     * item in that slot. If so, pull in as much EU as the item allows to be
     * transferred per tick up to the maximum energy transfer rate based on our
     * tier, limited also by the maximum energy storage capacity. ie. do not
     * pull in more than we have room for
     * 
     * @return
     */
    private void drainPowerSource() {
        double chargeReturned = 0;

        ItemStack stack = getStackInSlot(Info.CB_SLOT_POWER_SOURCE);
        if (stack != null && stack.getItem() instanceof IElectricItem && currentEnergy < adjustedStorage) {
            IElectricItem powerSource = (IElectricItem) (stack.getItem());

            Item emptyItem = powerSource.getEmptyItem(stack);
            int chargedItemID = Item.getIdFromItem(powerSource.getChargedItem(stack));

            if (Item.getIdFromItem(stack.getItem()) == chargedItemID) {
                if (powerSource.getTier(stack) <= powerTier && powerSource.canProvideEnergy(stack)) {
                    double itemTransferLimit = powerSource.getTransferLimit(stack);
                    double energyNeeded = adjustedStorage - currentEnergy;

                    // Test if the amount of energy we have room for is greater
                    // than what the item can transfer per tick.
                    if (energyNeeded > itemTransferLimit) {
                        // If so, request the max it can transfer per tick.
                        energyNeeded = itemTransferLimit;
                        // If we need less than it can transfer per tick,
                        // request only what we have room for so we don't waste
                        // power.
                    }

                    if (energyNeeded > 0) {
                        chargeReturned = ElectricItem.manager.discharge(stack, energyNeeded, powerTier, false,
                                false, false);
                        // Add the energy we received to our current energy
                        // level,
                        currentEnergy += chargeReturned;
                        if (chargeReturned > 0)
                            doingWork = true;
                        // and make sure that we didn't go over. If we somehow
                        // did, drop the excess.
                        if (currentEnergy > adjustedStorage)
                            currentEnergy = adjustedStorage;
                    }
                }

                // Workaround for buggy IC2 API .discharge that automatically
                // switches stack to emptyItemID but leaves a stackTagCompound
                // on it, so it can't be stacked with never-used empties
                if (chargedItemID != Item.getIdFromItem(emptyItem)
                        && ElectricItem.manager.discharge(stack, 1, powerTier, false, true, true) == 0) {
                    // if (ChargingBench.isDebugging)
                    // System.out.println("Switching to emptyItemID: " +
                    // emptyItemID + " from stack.itemID: " + stack.itemID +
                    // " - chargedItemID: " + chargedItemID);
                    setInventorySlotContents(Info.CB_SLOT_POWER_SOURCE, new ItemStack(emptyItem, 1, 0));
                    // ItemStack newStack = new ItemStack(emptyItemID, 1, 0);
                    // contents[ChargingBench.slotPowerSource] = newStack;
                }
            }
        }
    }

    /**
     * Look through all of the items in our main inventory and determine the
     * current charge level, maximum charge level and maximum base charge rate
     * for each item. Increase maximum charge rate for each item based on
     * overclockers as appropriate, then, starting with the first slot in the
     * main inventory, transfer one tick worth of energy from our internal
     * storage to the item. Continue doing this for all items in the inventory
     * until we reach the end of the main inventory or run out of internal EU
     * storage.
     */
    private void chargeItems() {
        for (int i = Info.CB_SLOT_CHARGING; i < Info.CB_SLOT_CHARGING + 12; i++) {
            ItemStack stack = contents[i];
            if (stack != null && stack.getItem() instanceof IElectricItem && stack.stackSize == 1) {
                IElectricItem item = (IElectricItem) (stack.getItem());
                if (item.getTier(stack) <= baseTier) {
                    double itemTransferLimit = item.getTransferLimit(stack);
                    if (itemTransferLimit == 0)
                        itemTransferLimit = baseMaxInput;
                    int adjustedTransferLimit = (int) Math.ceil(chargeFactor * itemTransferLimit);
                    double amountNeeded;
                    double missing;
                    int consumption;
                    if (Item.getIdFromItem(item.getChargedItem(stack)) != Item
                            .getIdFromItem(item.getEmptyItem(stack)) || stack.isStackable()) {
                        // Running stack.copy() on every item every tick would
                        // be a horrible thing for performance, but the
                        // workaround is needed
                        // for ElectricItem.charge adding stackTagCompounds for
                        // charge level to EmptyItemID batteries even when run
                        // in simulate mode.
                        // Limiting its use by what is hopefully a broad enough
                        // test to catch all cases where it's necessary in order
                        // to avoid problems.
                        // Using it for any item types listed as stackable and
                        // for any items where the charged and empty item IDs
                        // differ.
                        final ItemStack stackCopy = stack.copy();
                        amountNeeded = ElectricItem.manager.charge(stackCopy, adjustedTransferLimit, baseTier, true,
                                true);
                        if (amountNeeded == adjustedTransferLimit) {
                            missing = ElectricItem.manager.charge(stackCopy, item.getMaxCharge(stackCopy), baseTier,
                                    true, true);
                        } else
                            missing = amountNeeded;
                    } else {
                        amountNeeded = ElectricItem.manager.charge(stack, adjustedTransferLimit, baseTier, true,
                                true);
                        if (amountNeeded == adjustedTransferLimit) {
                            missing = ElectricItem.manager.charge(stack, item.getMaxCharge(stack), baseTier, true,
                                    true);
                        } else
                            missing = amountNeeded;
                    }

                    // How long will this item take and how much will it drain?
                    final int eta = (int) Math.ceil(((float) missing) / ((float) adjustedTransferLimit));
                    if (ticksRequired < eta)
                        ticksRequired = eta;
                    energyRequired += (int) Math.ceil((drainFactor / chargeFactor) * missing);

                    int adjustedEnergyUse = (int) Math.ceil((drainFactor / chargeFactor) * amountNeeded);
                    if (adjustedEnergyUse > 0 && currentEnergy > 0) {
                        if (adjustedEnergyUse > currentEnergy) {
                            // Allow that last trickle of energy to be
                            // transferred out of the bench
                            adjustedTransferLimit = (adjustedTransferLimit * currentEnergy) / adjustedEnergyUse;
                            adjustedEnergyUse = currentEnergy;
                        }
                        // We don't need to do this with the current API, it's
                        // switching the ItemID for us. Just make sure we don't
                        // try to charge stacked batteries, as mentioned above!
                        // int chargedItemID = item.getChargedItemId();
                        // if (stack.itemID != chargedItemID)
                        // {
                        // setInventorySlotContents(i, new
                        // ItemStack(chargedItemID, 1, 0));
                        // }
                        ElectricItem.manager.charge(contents[i], adjustedTransferLimit, baseTier, true, false);
                        currentEnergy -= adjustedEnergyUse;
                        if (currentEnergy < 0)
                            currentEnergy = 0;
                        doingWork = true;
                    }
                }
            }
        }
    }

    /**
     * First, check the output slot to see if it's empty. If so, look to see if
     * there are any fully charged items in the main inventory. Move the first
     * fully charged item to the output slot.
     */
    private void moveOutputItems() {
        ItemStack stack = contents[Info.CB_SLOT_OUTPUT];
        if (stack == null) {
            // Output slot is empty. Try to find a fully charged item to move
            // there.
            for (int slot = Info.CB_SLOT_CHARGING; slot < Info.CB_SLOT_CHARGING + 12; ++slot) {
                ItemStack currentStack = contents[slot];
                if (currentStack != null && currentStack.getItem() instanceof IElectricItem) {
                    // Test if the item is fully charged (cannot accept any more
                    // power).
                    if (ElectricItem.manager.charge(currentStack.copy(), 1, baseTier, false, true) == 0) {
                        contents[Info.CB_SLOT_OUTPUT] = currentStack;
                        contents[slot] = null;
                        this.markDirty();
                        break;
                    }
                }
            }
        }
    }

    /**
     * Check to see if there are any items in the input slot. If so, check to
     * see if there are any free charging slots. If so, move one from the input
     * slot to a free charging slot. Do not move more than one, if the stack
     * contains more.
     */
    private void acceptInputItems() {
        ItemStack stack = contents[Info.CB_SLOT_INPUT];
        if (stack != null && stack.getItem() instanceof IElectricItem) {
            // Input slot contains something electrical. If possible, move one
            // of it into the charging area.
            IElectricItem item = (IElectricItem) (stack.getItem());
            for (int slot = Info.CB_SLOT_CHARGING; slot < Info.CB_SLOT_CHARGING + 12; ++slot) {
                if (contents[slot] == null) {
                    // Grab one unit from input and move it to the selected
                    // slot.
                    contents[slot] = decrStackSize(Info.CB_SLOT_INPUT, 1);
                    break;
                }
            }
        }
    }

    public int gaugeEnergyScaled(int gaugeSize) {
        if (currentEnergy <= 0) {
            return 0;
        }

        int result = currentEnergy * gaugeSize / adjustedStorage;
        if (result > gaugeSize)
            result = gaugeSize;

        return result;
    }

    // Networking stuff

    @SideOnly(Side.CLIENT)
    @Override
    public void receiveDescriptionData(int packetID, ByteBuf stream) {
        final int a;
        final boolean b;
        // try
        // {
        a = stream.readInt();
        b = stream.readBoolean();
        /*
         * } catch (IOException e) { logDescPacketError(e); return; }
         */
        chargeLevel = a;
        doingWork = b;
        worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
    }

    @Override
    public Packet getDescriptionPacket() {
        return createDescPacket();
    }

    @Override
    protected void addUniqueDescriptionData(ByteBuf data) throws IOException {
        data.writeInt(chargeLevel);
        data.writeBoolean(doingWork);
    }

    // ISidedInventory

    /*
     * @Override public int getStartInventorySide(ForgeDirection side) { switch
     * (side) { case UP: return Info.CB_SLOT_INPUT; case DOWN: return
     * Info.CB_SLOT_OUTPUT; default: return Info.CB_SLOT_POWER_SOURCE; } }
     * 
     * @Override public int getSizeInventorySide(ForgeDirection side) { // Each
     * side accesses a single slot return 1; }
     */

    @Override
    public int[] getAccessibleSlotsFromSide(int side) {
        switch (side) {
        // Correct values for top and bottom sides: 0 = bottom, 1 = top
        case 0:
            // return ChargingBenchSideOutput;
        case 1:
            // return ChargingBenchSideInput;
            return ChargingBenchSideInOut;
        default:
            return ChargingBenchSidePower;
        }
    }

    @Override
    public boolean isItemValidForSlot(int i, ItemStack stack) {
        // Decide if the item is a valid IC2 electrical item
        if (i == Info.CB_SLOT_POWER_SOURCE)
            return Utils.isItemDrainable(stack, powerTier);
        if (i == Info.CB_SLOT_INPUT)
            return Utils.isItemChargeable(stack, powerTier);
        // Info.CB_SLOT_OUTPUT ?
        return false;
    }

    // Returns true if automation can insert the given item in the given slot
    // from the given side. Args: Slot, item, side
    @Override
    public boolean canInsertItem(int i, ItemStack itemstack, int j) // canInsertItem
    {
        if (i == Info.CB_SLOT_INPUT || i == Info.CB_SLOT_POWER_SOURCE)
            return true;
        return false;
    }

    // Returns true if automation can extract the given item in the given slot
    // from the given side. Args: Slot, item, side
    @Override
    public boolean canExtractItem(int i, ItemStack itemstack, int j) // canExtractItem
    {
        if (i == Info.CB_SLOT_OUTPUT || i == Info.CB_SLOT_POWER_SOURCE)
            return true;
        return false;
    }

    // IInventory

    @Override
    public int getSizeInventory() {
        // Only input/output slots are accessible to machines
        return 3;
    }

    @Override
    public String getInventoryName() {
        switch (baseTier) {
        case 1:
            return Info.KEY_BLOCK_NAMES[0] + Info.KEY_NAME_SUFFIX;
        case 3:
            return Info.KEY_BLOCK_NAMES[1] + Info.KEY_NAME_SUFFIX;
        case 4:
            return Info.KEY_BLOCK_NAMES[2] + Info.KEY_NAME_SUFFIX;
        }
        return "";
    }

    @Override
    public void markDirty(int slot) {
        if (slot == Info.CB_SLOT_INPUT || slot == Info.CB_SLOT_OUTPUT) {
            // Move item from input to output if not valid. (Wrong tier or not
            // electric item.)
            if (contents[Info.CB_SLOT_INPUT] != null && contents[Info.CB_SLOT_OUTPUT] == null) {
                if (!isItemValid(Info.CB_SLOT_INPUT, contents[Info.CB_SLOT_INPUT])) {
                    contents[Info.CB_SLOT_OUTPUT] = contents[Info.CB_SLOT_INPUT];
                    contents[Info.CB_SLOT_INPUT] = null;
                }
            }
        } else if (slot >= Info.CB_SLOT_UPGRADE && slot < Info.CB_SLOT_UPGRADE + 4) {
            // One of the upgrade slots was touched, so we need to recalculate.
            doUpgradeEffects();
        } else if (slot >= Info.CB_SLOT_CHARGING && slot < Info.CB_SLOT_CHARGING + 12) {
            // Make sure it's not fully charged already? Not sure, full items
            // will be output in updateEntity

        } else if (slot == Info.CB_SLOT_POWER_SOURCE) {
            // Perhaps eject the item if it's not valid? No, just leave it
            // alone.
            // If machinery added it the player can figure out the problem by
            // trying to remove and replace it and realizing it won't fit.
        }
        super.markDirty();
    }

    @Override
    public void markDirty() {
        // We're not sure what called this or what slot was altered, so make
        // sure the upgrade effects are correct just in case and then pass the
        // call on.
        doUpgradeEffects();
        super.markDirty();
    }

    @Override
    public boolean acceptsEnergyFrom(TileEntity emitter, ForgeDirection direction) {
        return true;
    }

    @Override
    public double getOutputEnergyUnitsPerTick() {
        return 0;
    }

    @Override
    public boolean isTeleporterCompatible(ForgeDirection side) {
        return false;
    }

    @Override
    public double getDemandedEnergy() {
        // return (currentEnergy < adjustedStorage &&
        // !receivingRedstoneSignal());
        if (!receivingRedstoneSignal()) {
            return adjustedStorage - currentEnergy;
        }
        return 0;
    }

    @Override
    public double injectEnergy(ForgeDirection directionFrom, double amount, double voltage) {
        int surplus = 0;
        if (AdvancedPowerManagement.proxy.isServer()) {
            // if supply is greater than the max we can take per tick
            if (amount > adjustedMaxInput) {
                // If the supplied EU is over the baseMaxInput, we're getting
                // supplied higher than acceptable current. Pop ourselves off
                // into the world and return all but 1 EU, or if the supply
                // somehow was 1EU, return zero to keep IC2 from spitting out
                // massive errors in the log
                selfDestroy();
                if (amount <= 1)
                    return 0;
                else
                    return amount - 1;
            } else {
                if (currentEnergy > adjustedStorage)
                    currentEnergy = adjustedStorage;
                currentEnergy += amount;
                energyReceived += amount;
                // check if our current energy level is now over the max energy
                // level
                if (currentEnergy > adjustedStorage) {
                    // if so, our surplus to return is equal to that amount over
                    surplus = currentEnergy - adjustedStorage;
                    // and set our current energy level TO our max energy level
                    currentEnergy = adjustedStorage;
                    energyReceived -= surplus;
                }
                // surplus may be zero or greater here
            }
        }
        return surplus;
    }
}