package sourcecoded.quantum.tile;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import sourcecoded.quantum.api.energy.EnergyBehaviour;
import sourcecoded.quantum.api.energy.ITileRiftHandler;
import sourcecoded.quantum.api.energy.RiftEnergyStorage;
import sourcecoded.quantum.api.tileentity.IBindable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings("unchecked")
public class TileSync extends TileDyeable implements IBindable, ITileRiftHandler {
int lastHashcode;
int lastMeta;
Block lastBlock;
boolean blockChangedLastTick;
int boundX, boundY, boundZ;
RiftEnergyStorage rift;
public TileSync() {
rift = new RiftEnergyStorage(50000);
}
@Override
public void updateEntity() {
super.updateEntity();
doThing();
}
public void doThing() {
if (!worldObj.isRemote) {
TileEntity tile = getBoundTile();
if (tile != null) {
TileSync bound = (TileSync) tile;
boolean metaChanged = hasMetaChanged();
boolean blockChanged = hasBlockChanged();
boolean shouldSyncNBT = worldObj.getBlockMetadata(xCoord, yCoord, zCoord) == 1;
boolean nbtChanged = hasNBTChanged();
if (blockChanged && getRiftEnergy() >= 3000) {
bound.changeBlock(lastBlock, lastMeta);
takeRiftEnergy(5000);
} else if (blockChanged) {
if (!blockChangedLastTick) {
for (ItemStack stack : getBlockAbove().getDrops(worldObj, xCoord, yCoord + 1, zCoord, lastMeta, 0))
worldObj.spawnEntityInWorld(new EntityItem(worldObj, xCoord, yCoord + 1, zCoord, stack));
}
blockChangedLastTick = false;
worldObj.setBlockToAir(xCoord, yCoord + 1, zCoord);
bound.changeBlock(Blocks.air, 0);
}
if (metaChanged && getRiftEnergy() >= 2000) {
bound.changeBlock(lastBlock, lastMeta);
takeRiftEnergy(5000);
} else if (metaChanged) {
if (!blockChangedLastTick) {
for (ItemStack stack : getBlockAbove().getDrops(worldObj, xCoord, yCoord + 1, zCoord, lastMeta, 0))
worldObj.spawnEntityInWorld(new EntityItem(worldObj, xCoord, yCoord + 1, zCoord, stack));
}
blockChangedLastTick = false;
worldObj.setBlockToAir(xCoord, yCoord + 1, zCoord);
bound.changeBlock(Blocks.air, 0);
}
//TODO Fix tile invalidation
nbt:
if (shouldSyncNBT) {
if (nbtChanged && getRiftEnergy() >= 200) {
TileEntity tileBound = bound.getTileAbove();
TileEntity thisTile = getTileAbove();
if (tileBound == null || thisTile == null)
break nbt;
NBTTagCompound sendingCompound = new NBTTagCompound();
thisTile.writeToNBT(sendingCompound);
NBTTagCompound comparisonCompound = new NBTTagCompound();
tileBound.writeToNBT(comparisonCompound);
combineNBT(comparisonCompound, stripNBT(sendingCompound));
tileBound.readFromNBT(comparisonCompound);
takeRiftEnergy(200);
} else if (nbtChanged) {
for (ItemStack stack : getBlockAbove().getDrops(worldObj, xCoord, yCoord + 1, zCoord, lastMeta, 0))
worldObj.spawnEntityInWorld(new EntityItem(worldObj, xCoord, yCoord + 1, zCoord, stack));
worldObj.setBlockToAir(xCoord, yCoord + 1, zCoord);
bound.changeBlock(Blocks.air, 0);
}
}
}
}
}
public void onDestroy() {
TileSync tile = getBoundTile();
if (tile != null) {
tile.invalidate();
tile.worldObj.setBlockToAir(tile.xCoord, tile.yCoord + 1, tile.zCoord);
}
}
public NBTTagCompound stripNBT(NBTTagCompound compound) {
compound.removeTag("x");
compound.removeTag("y");
compound.removeTag("z");
compound.removeTag("id");
return compound;
}
public void combineNBT(NBTTagCompound original, NBTTagCompound compare) {
HashMap<String, NBTBase> compareTags = getNBTAsHashmap(compare);
for (Map.Entry<String, NBTBase> entry : compareTags.entrySet())
original.setTag(entry.getKey(), entry.getValue());
}
public HashMap<String, NBTBase> getNBTAsHashmap(NBTTagCompound compound) {
HashMap<String, NBTBase> map = null;
Class nbt = compound.getClass();
Field keys;
try {
keys = nbt.getDeclaredField("tagMap");
keys.setAccessible(true);
map = (HashMap<String, NBTBase>) keys.get(compound);
} catch (Exception e) {
}
return map;
}
public void onBlockChanged() {
updateEntity();
}
public void changeBlock(Block block, int meta) {
blockChangedLastTick = true;
if (block instanceof ITileEntityProvider && worldObj.getBlockMetadata(xCoord, yCoord, zCoord) == 0)
worldObj.setBlockToAir(xCoord, yCoord + 1, zCoord);
else
worldObj.setBlock(xCoord, yCoord + 1, zCoord, block, meta, 3);
}
public TileSync getBoundTile() {
TileEntity tile = worldObj.getTileEntity(boundX, boundY, boundZ);
if (tile != null && tile instanceof TileSync)
return (TileSync) tile;
return null;
}
public Block getBlockAbove() {
return worldObj.getBlock(xCoord, yCoord + 1, zCoord);
}
public TileEntity getTileAbove() {
return worldObj.getTileEntity(xCoord, yCoord + 1, zCoord);
}
public boolean hasMetaChanged() {
int newMeta = worldObj.getBlockMetadata(xCoord, yCoord + 1, zCoord);
if (newMeta != lastMeta) {
lastMeta = newMeta;
return true;
}
return false;
}
public boolean hasBlockChanged() {
Block block = worldObj.getBlock(xCoord, yCoord + 1, zCoord);
if (block != null) {
if (block != lastBlock) {
lastBlock = block;
return true;
}
}
return false;
}
public boolean hasNBTChanged() {
TileEntity tile = worldObj.getTileEntity(xCoord, yCoord + 1, zCoord);
if (tile != null) {
NBTTagCompound compound = new NBTTagCompound();
tile.writeToNBT(compound);
int hash = compound.hashCode();
if (hash != lastHashcode) {
lastHashcode = hash;
return true;
}
}
return false;
}
@Override
public void writeToNBT(NBTTagCompound compound) {
super.writeToNBT(compound);
compound.setInteger("boundX", boundX);
compound.setInteger("boundY", boundY);
compound.setInteger("boundZ", boundZ);
}
@Override
public void readFromNBT(NBTTagCompound compound) {
super.readFromNBT(compound);
boundX = compound.getInteger("boundX");
boundY = compound.getInteger("boundY");
boundZ = compound.getInteger("boundZ");
}
public void clearBinding() {
boundX = 0;
boundY = 0;
boundZ = 0;
}
@Override
public boolean tryBind(EntityPlayer player, int x, int y, int z, boolean silent) {
TileEntity tileOld = getBoundTile();
if (tileOld != null)
((TileSync) tileOld).clearBinding();
TileEntity tile = worldObj.getTileEntity(x, y, z);
if (tile != null && tile instanceof TileSync) {
boundX = x;
boundY = y;
boundZ = z;
if (!silent)
getBoundTile().tryBind(player, xCoord, yCoord, zCoord, true);
return true;
} else
return false;
}
@Override
public int takeRiftEnergy(int amount) {
update();
return rift.takeRiftEnergy(amount);
}
@Override
public int giveRiftEnergy(int amount) {
update();
return rift.giveRiftEnergy(amount);
}
@Override
public int getRiftEnergy() {
return rift.getRiftEnergy();
}
@Override
public int getMaxRiftEnergy() {
return rift.getMaxRiftEnergy();
}
@Override
public EnergyBehaviour getBehaviour() {
return EnergyBehaviour.DRAIN;
}
}