package crazypants.enderio.machine.soul;
import java.util.List;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidHandler;
import com.enderio.core.api.common.util.ITankAccess;
import com.enderio.core.common.util.FluidUtil;
import crazypants.enderio.ModObject;
import crazypants.enderio.config.Config;
import crazypants.enderio.machine.AbstractPoweredTaskEntity;
import crazypants.enderio.machine.IMachineRecipe;
import crazypants.enderio.machine.MachineRecipeInput;
import crazypants.enderio.machine.MachineRecipeRegistry;
import crazypants.enderio.machine.SlotDefinition;
import crazypants.enderio.network.PacketHandler;
import crazypants.enderio.power.BasicCapacitor;
import crazypants.enderio.power.Capacitors;
import crazypants.enderio.xp.ExperienceContainer;
import crazypants.enderio.xp.IHaveExperience;
import crazypants.enderio.xp.PacketExperianceContainer;
import crazypants.enderio.xp.XpUtil;
public class TileSoulBinder extends AbstractPoweredTaskEntity implements IHaveExperience, IFluidHandler, ITankAccess {
public static final int POWER_PER_TICK_ONE = Config.soulBinderLevelOnePowerPerTickRF;
private static final BasicCapacitor CAP_ONE = new BasicCapacitor(POWER_PER_TICK_ONE * 2,
Capacitors.BASIC_CAPACITOR.capacitor.getMaxEnergyStored(), POWER_PER_TICK_ONE);
public static final int POWER_PER_TICK_TWO = Config.soulBinderLevelTwoPowerPerTickRF;
private static final BasicCapacitor CAP_TWO = new BasicCapacitor(POWER_PER_TICK_TWO * 2,
Capacitors.ACTIVATED_CAPACITOR.capacitor.getMaxEnergyStored(), POWER_PER_TICK_TWO);
public static final int POWER_PER_TICK_THREE = Config.soulBinderLevelThreePowerPerTickRF;
private static final BasicCapacitor CAP_THREE = new BasicCapacitor(POWER_PER_TICK_THREE * 2,
Capacitors.ENDER_CAPACITOR.capacitor.getMaxEnergyStored(), POWER_PER_TICK_THREE);
private final ExperienceContainer xpCont = new ExperienceContainer(XpUtil.getExperienceForLevel(Config.soulBinderMaxXpLevel));
public TileSoulBinder() {
super(new SlotDefinition(2, 2, 1));
}
@Override
public ExperienceContainer getContainer() {
return xpCont;
}
@Override
public String getMachineName() {
return ModObject.blockSoulBinder.unlocalisedName;
}
@Override
public int getInventoryStackLimit() {
return 1;
}
@Override
protected boolean processTasks(boolean redstoneChecksPassed) {
if(xpCont.isDirty()) {
PacketHandler.sendToAllAround(new PacketExperianceContainer(this), this);
xpCont.setDirty(false);
}
return super.processTasks(redstoneChecksPassed);
}
@Override
protected IMachineRecipe canStartNextTask(float chance) {
IMachineRecipe recipe = super.canStartNextTask(chance);
if(recipe == null) {
return null;
}
int xpRequired = ((ISoulBinderRecipe)recipe).getExperienceRequired();
if(xpCont.getExperienceTotal() >= xpRequired) {
return recipe;
}
return null;
}
public boolean needsXP() {
return getXPRequired() > 0;
}
/**
* Computes the required amount of XP to start the current recipe.
* @return 0 if no XP is required, negative when more than required XP is stored.
*/
private int getXPRequired() {
if(currentTask != null) {
return 0;
}
IMachineRecipe nextRecipe = getNextRecipe();
if(! (nextRecipe instanceof ISoulBinderRecipe)) {
return 0;
}
return ((ISoulBinderRecipe)nextRecipe).getExperienceRequired() - getContainer().getExperienceTotal();
}
public int getCurrentlyRequiredLevel() {
if(currentTask != null) {
return -1;
}
IMachineRecipe nextRecipe = getNextRecipe();
if(! (nextRecipe instanceof ISoulBinderRecipe)) {
return -1;
}
return ((ISoulBinderRecipe)nextRecipe).getExperienceLevelsRequired();
}
@Override
protected boolean startNextTask(IMachineRecipe nextRecipe, float chance) {
int xpRequired = ((ISoulBinderRecipe)nextRecipe).getExperienceRequired();
if(xpCont.getExperienceTotal() < xpRequired) {
return false;
}
if(super.startNextTask(nextRecipe, chance)) {
xpCont.drain(ForgeDirection.UNKNOWN, XpUtil.experienceToLiquid(xpRequired), true);
return true;
}
return false;
}
@Override
protected boolean isMachineItemValidForSlot(int slot, ItemStack item) {
if(!slotDefinition.isInputSlot(slot)) {
return false;
}
MachineRecipeInput newInput = new MachineRecipeInput(slot, item);
int otherSlot = slot == 0 ? 1 : 0;
if(inventory[otherSlot] == null) {
List<IMachineRecipe> recipes = MachineRecipeRegistry.instance.getRecipesForInput(getMachineName(), newInput);
if(recipes.isEmpty()) {
return false;
}
for(IMachineRecipe rec : recipes) {
if(rec != null && rec.isValidInput(newInput)) {
return true;
}
}
} else {
MachineRecipeInput[] inputs = new MachineRecipeInput[] {
newInput,
new MachineRecipeInput(otherSlot, inventory[otherSlot])
};
return MachineRecipeRegistry.instance.getRecipeForInputs(getMachineName(), inputs) != null;
}
return false;
}
@Override
public void onCapacitorTypeChange() {
switch (getCapacitorType()) {
case BASIC_CAPACITOR:
setCapacitor(CAP_ONE);
break;
case ACTIVATED_CAPACITOR:
setCapacitor(CAP_TWO);
break;
case ENDER_CAPACITOR:
setCapacitor(CAP_THREE);
break;
}
}
@Override
protected boolean doPull(ForgeDirection dir) {
boolean res = super.doPull(dir);
int req = getXPRequired();
if(req > 0) {
FluidUtil.doPull(this, dir, Math.min(XpUtil.experienceToLiquid(req), Config.fluidConduitExtractRate));
}
return res;
}
@Override
protected boolean doPush(ForgeDirection dir) {
boolean res = super.doPush(dir);
int maxAmount = Math.min(XpUtil.experienceToLiquid(getExcessXP()), Config.fluidConduitExtractRate);
if (maxAmount > 0) {
FluidUtil.doPush(this, dir, maxAmount);
}
return res;
}
/**
* Determines how much stored XP can/should be removed because it is not
* needed for the next recipe.
*
* @return A number between 0 and the amount of stored XP
*/
private int getExcessXP() {
if (currentTask == null) {
IMachineRecipe nextRecipe = getNextRecipe();
if (nextRecipe instanceof ISoulBinderRecipe) {
return Math.max(0, getContainer().getExperienceTotal() - ((ISoulBinderRecipe) nextRecipe).getExperienceRequired());
}
}
return getContainer().getExperienceTotal();
}
@Override
public boolean canFill(ForgeDirection from, Fluid fluid) {
return xpCont.canFill(from, fluid);
}
@Override
public int fill(ForgeDirection from, FluidStack resource, boolean doFill) {
int max = XpUtil.experienceToLiquid(getXPRequired());
if (resource == null || max <= 0) {
return 0;
} else if (max < resource.amount) {
FluidStack copy = resource.copy();
copy.amount = max;
return xpCont.fill(from, copy, doFill);
} else {
return xpCont.fill(from, resource, doFill);
}
}
@Override
public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) {
int max = XpUtil.experienceToLiquid(getExcessXP());
if (resource != null && max < resource.amount) {
FluidStack copy = resource.copy();
copy.amount = max;
return xpCont.drain(from, copy, doDrain);
} else {
return xpCont.drain(from, resource, doDrain);
}
}
@Override
public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) {
return xpCont.drain(from, Math.min(XpUtil.experienceToLiquid(getExcessXP()), maxDrain), doDrain);
}
@Override
public boolean canDrain(ForgeDirection from, Fluid fluid) {
return xpCont.canDrain(from, fluid);
}
@Override
public FluidTankInfo[] getTankInfo(ForgeDirection from) {
return xpCont.getTankInfo(from);
}
@Override
public void readCommon(NBTTagCompound nbtRoot) {
super.readCommon(nbtRoot);
xpCont.readFromNBT(nbtRoot);
}
@Override
public void writeCommon(NBTTagCompound nbtRoot) {
super.writeCommon(nbtRoot);
xpCont.writeToNBT(nbtRoot);
}
@Override
public FluidTank getInputTank(FluidStack forFluidType) {
return xpCont;
}
@Override
public FluidTank[] getOutputTanks() {
return new FluidTank[] { xpCont };
}
@Override
public void setTanksDirty() {
xpCont.setDirty(true);
}
}