package slimeknights.tconstruct.smeltery.tileentity;
import com.google.common.collect.Lists;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.util.List;
import javax.annotation.Nonnull;
import slimeknights.tconstruct.common.TinkerNetwork;
import slimeknights.tconstruct.library.TinkerRegistry;
import slimeknights.tconstruct.library.utils.TagUtil;
import slimeknights.tconstruct.smeltery.TinkerSmeltery;
import slimeknights.tconstruct.smeltery.block.BlockSearedFurnaceController;
import slimeknights.tconstruct.smeltery.multiblock.MultiblockDetection;
import slimeknights.tconstruct.smeltery.network.HeatingStructureFuelUpdatePacket;
public abstract class TileHeatingStructureFuelTank<T extends MultiblockDetection> extends TileHeatingStructure<T> {
// NBT Tags
public static final String TAG_TANKS = "tanks";
public static final String TAG_FUEL_QUALITY = "fuelQuality";
public static final String TAG_CURRENT_FUEL = "currentFuel";
public static final String TAG_CURRENT_TANK = "currentTank";
// amount of fuel gotten from a single consumption of the fluid, used for GUI fuel percentage
public int fuelQuality;
// Fuel tank information
public List<BlockPos> tanks;
public BlockPos currentTank;
public FluidStack currentFuel; // the fuel that was last consumed
public TileHeatingStructureFuelTank(String name, int inventorySize, int maxStackSize) {
super(name, inventorySize, maxStackSize);
tanks = Lists.newLinkedList();
}
@Override
protected void consumeFuel() {
// no need to consume fuel
if(hasFuel()) {
return;
}
// get current tank
searchForFuel();
// got a tank?
if(currentTank != null) {
// consume fuel!
TileEntity te = getWorld().getTileEntity(currentTank);
if(te instanceof TileTank) {
IFluidTank tank = ((TileTank) te).getInternalTank();
FluidStack liquid = tank.getFluid();
if(liquid != null) {
FluidStack in = liquid.copy();
int bonusFuel = TinkerRegistry.consumeSmelteryFuel(in);
int amount = liquid.amount - in.amount;
FluidStack drained = tank.drain(amount, false);
// we can drain. actually drain and add the fuel
if(drained != null && drained.amount == amount) {
tank.drain(amount, true);
currentFuel = drained.copy();
fuelQuality = bonusFuel;
addFuel(bonusFuel, drained.getFluid().getTemperature(drained) - 300); // convert to degree celcius
// notify client of fuel/temperature changes
if(isServerWorld()) {
TinkerNetwork.sendToAll(new HeatingStructureFuelUpdatePacket(pos, currentTank, temperature, currentFuel));
}
return;
}
}
fuelQuality = 0;
}
}
}
/**
* Locates a tank containing fuel, if one exists
*
* @return true if successful
*/
private void searchForFuel() {
// is the current tank still up to date?
if(currentTank != null && hasTankWithFuel(currentTank, currentFuel)) {
return;
}
// nope, current tank is empty, check others for same fuel
for(BlockPos pos : tanks) {
if(hasTankWithFuel(pos, currentFuel)) {
currentTank = pos;
return;
}
}
// nothing found, try again with new fuel
for(BlockPos pos : tanks) {
if(hasTankWithFuel(pos, null)) {
currentTank = pos;
return;
}
}
currentTank = null;
}
// checks if the given location has a fluid tank that contains fuel
private boolean hasTankWithFuel(BlockPos pos, FluidStack preference) {
IFluidTank tank = getTankAt(pos);
if(tank != null && tank.getFluid() != null) {
if(tank.getFluidAmount() > 0 && TinkerRegistry.isSmelteryFuel(tank.getFluid())) {
// if we have a preference, only use that
if(preference != null && tank.getFluid().isFluidEqual(preference)) {
return true;
}
else if(preference == null) {
return true;
}
}
}
return false;
}
@Override
protected void updateStructureInfo(MultiblockDetection.MultiblockStructure structure) {
// find all tanks for input
tanks.clear();
for(BlockPos pos : structure.blocks) {
if(getWorld().getBlockState(pos).getBlock() == TinkerSmeltery.searedTank) {
tanks.add(pos);
}
}
int inventorySize = getUpdatedInventorySize(structure.xd, structure.yd, structure.zd);
// if the new multiblock is smaller we pop out all items that don't fit in anymore
if(this.getSizeInventory() > inventorySize) {
for(int i = inventorySize; i < getSizeInventory(); i++) {
if(getStackInSlot(i) != null) {
dropItem(getStackInSlot(i));
}
}
}
// adjust inventory sizes
this.resize(inventorySize);
}
/** When the multiblock forms the inventory size is readjusted. Return the inventory size from the (total) structure size */
protected abstract int getUpdatedInventorySize(int width, int height, int depth);
protected void dropItem(ItemStack stack) {
EnumFacing direction = getWorld().getBlockState(pos).getValue(BlockSearedFurnaceController.FACING);
BlockPos pos = this.getPos().offset(direction);
EntityItem entityitem = new EntityItem(getWorld(), pos.getX(), pos.getY(), pos.getZ(), stack);
getWorld().spawnEntity(entityitem);
}
/**
* Grabs the tank at the given location (if present)
*/
private IFluidTank getTankAt(BlockPos pos) {
TileEntity te = getWorld().getTileEntity(pos);
if(te instanceof TileTank) {
return ((TileTank) te).getInternalTank();
}
return null;
}
/* GUI */
public float getHeatingProgress(int index) {
if(index < 0 || index > getSizeInventory() - 1) {
return -1f;
}
if(!canHeat(index)) {
return -1f;
}
return getProgress(index);
}
/**
* Can be used by the GUI to determine fuel percentage
*/
@SideOnly(Side.CLIENT)
public float getFuelPercentage() {
return (float) fuel / (float) fuelQuality;
}
@SideOnly(Side.CLIENT)
public FuelInfo getFuelDisplay() {
FuelInfo info = new FuelInfo();
// we still have leftover fuel
if(hasFuel()) {
info.fluid = currentFuel.copy();
info.fluid.amount = 0;
info.heat = this.temperature;
info.maxCap = currentFuel.amount;
}
else if(currentTank != null) {
// we need to consume fuel, check the current tank
if(hasTankWithFuel(currentTank, currentFuel)) {
IFluidTank tank = getTankAt(currentTank);
info.fluid = tank.getFluid().copy();
info.heat = temperature;
info.maxCap = tank.getCapacity();
}
}
// check all other tanks (except the current one that we already checked) for more fuel
for(BlockPos pos : tanks) {
if(pos == currentTank) {
continue;
}
IFluidTank tank = getTankAt(pos);
// tank exists and has something in it
if(tank != null && tank.getFluidAmount() > 0) {
// we don't have fuel yet, use this
if(info.fluid == null) {
info.fluid = tank.getFluid().copy();
info.heat = info.fluid.getFluid().getTemperature(info.fluid);
info.maxCap = tank.getCapacity();
}
// otherwise add the same together
else if(tank.getFluid().isFluidEqual(info.fluid)) {
info.fluid.amount += tank.getFluidAmount();
info.maxCap += tank.getCapacity();
}
}
}
return info;
}
/* Networking and saving */
@SideOnly(Side.CLIENT)
public void updateFuelFromPacket(int index, int fuel) {
if(index == 0) {
this.fuel = fuel;
}
else if(index == 1) {
this.fuelQuality = fuel;
}
}
@Nonnull
@Override
public NBTTagCompound writeToNBT(NBTTagCompound compound) {
compound = super.writeToNBT(compound);
compound.setInteger(TAG_FUEL_QUALITY, fuelQuality);
compound.setTag(TAG_CURRENT_TANK, TagUtil.writePos(currentTank));
NBTTagList tankList = new NBTTagList();
for(BlockPos pos : tanks) {
tankList.appendTag(TagUtil.writePos(pos));
}
compound.setTag(TAG_TANKS, tankList);
NBTTagCompound fuelTag = new NBTTagCompound();
if(currentFuel != null) {
currentFuel.writeToNBT(fuelTag);
}
compound.setTag(TAG_CURRENT_FUEL, fuelTag);
return compound;
}
@Override
public void readFromNBT(NBTTagCompound compound) {
super.readFromNBT(compound);
fuelQuality = compound.getInteger(TAG_FUEL_QUALITY);
NBTTagList tankList = compound.getTagList(TAG_TANKS, 10);
tanks.clear();
for(int i = 0; i < tankList.tagCount(); i++) {
tanks.add(TagUtil.readPos(tankList.getCompoundTagAt(i)));
}
NBTTagCompound fuelTag = compound.getCompoundTag(TAG_CURRENT_FUEL);
currentFuel = FluidStack.loadFluidStackFromNBT(fuelTag);
}
public static class FuelInfo {
public int heat;
public int maxCap;
public FluidStack fluid;
}
}