content: package thebetweenlands.common.tile;
  
  import java.util.List;
  
  import net.minecraft.block.Block;
  import net.minecraft.block.state.IBlockState;
  import net.minecraft.item.ItemStack;
  import net.minecraft.nbt.NBTTagCompound;
  import net.minecraft.network.NetworkManager;
  import net.minecraft.network.play.server.SPacketUpdateTileEntity;
  import net.minecraft.util.DamageSource;
  import net.minecraft.util.EnumFacing;
  import net.minecraft.util.ITickable;
  import net.minecraft.util.math.AxisAlignedBB;
  import net.minecraft.util.math.BlockPos;
  import net.minecraft.util.math.BlockPos.MutableBlockPos;
  import net.minecraft.world.World;
  import net.minecraftforge.fml.common.network.NetworkRegistry;
  import net.minecraftforge.fml.relauncher.Side;
  import net.minecraftforge.fml.relauncher.SideOnly;
  import thebetweenlands.api.recipes.IDruidAltarRecipe;
  import thebetweenlands.common.TheBetweenlands;
  import thebetweenlands.common.block.container.BlockDruidAltar;
  import thebetweenlands.common.block.structure.BlockDruidStone;
  import thebetweenlands.common.block.structure.BlockMobSpawnerBetweenlands;
  import thebetweenlands.common.entity.mobs.EntityDarkDruid;
  import thebetweenlands.common.network.clientbound.MessageDruidAltarProgress;
  import thebetweenlands.common.recipe.misc.DruidAltarRecipe;
  import thebetweenlands.common.tile.spawner.MobSpawnerLogicBetweenlands;
  
  public class TileEntityDruidAltar extends TileEntityBasicInventory implements ITickable {
  	public final static double FINAL_HEIGHT = 2.0D;
  	// 14.25 seconds crafting time
  	public static final int CRAFTING_TIME = 20 * 14 + 5;
  	private static final float ROTATION_SPEED = 2.0F;
  	public float rotation;
  	public float prevRotation;
  	public float renderYOffset;
  	public float prevRenderYOffset;
  	public int craftingProgress = 0;
  	private boolean circleShouldRevert = true;
  
  	public TileEntityDruidAltar() {
  		super(5, "container.bl.druid_altar");
  	}
  
  	@Override
  	public void update() {
  		if (!this.world.isRemote && this.circleShouldRevert) {
  			checkDruidCircleBlocks(this.world);
  			this.circleShouldRevert = false;
  		}
  		if (this.world.isRemote) {
  			this.prevRotation = this.rotation;
  			this.rotation += ROTATION_SPEED;
  			if (this.rotation >= 360.0F) {
  				this.rotation -= 360.0F;
  				this.prevRotation -= 360.0F;
  			}
  			if (this.craftingProgress != 0) {
  				this.craftingProgress++;
  			}
  			this.prevRenderYOffset = this.renderYOffset;
  			this.renderYOffset = (float) ((double) this.craftingProgress / (double) TileEntityDruidAltar.CRAFTING_TIME * (FINAL_HEIGHT - 0.2D) + 1.2D);
  		} else {
  			if (this.craftingProgress != 0) {
  				IDruidAltarRecipe recipe = DruidAltarRecipe.getDruidAltarRecipe(this.inventory.get(1), this.inventory.get(2), this.inventory.get(3), this.inventory.get(4));
  				// Sync clients every second
  				if (this.craftingProgress % 20 == 0 || this.craftingProgress == 1) {
  					sendCraftingProgressPacket();
  				}
  				this.craftingProgress++;
  				if (recipe == null || !this.inventory.get(0).isEmpty()) {
  					stopCraftingProcess();
  				}
  				if (recipe != null) {
  					recipe.onCrafting(this.world, this.pos, new ItemStack[]{this.inventory.get(1), this.inventory.get(2), this.inventory.get(3), this.inventory.get(4)});
  					if(this.craftingProgress >= CRAFTING_TIME) {
  						ItemStack stack = recipe.getOutput(new ItemStack[]{this.inventory.get(1), this.inventory.get(2), this.inventory.get(3), this.inventory.get(4)}).copy();
  						setInventorySlotContents(1, ItemStack.EMPTY);
  						setInventorySlotContents(2, ItemStack.EMPTY);
  						setInventorySlotContents(3, ItemStack.EMPTY);
  						setInventorySlotContents(4, ItemStack.EMPTY);
  						setInventorySlotContents(0, stack);
  						stopCraftingProcess();
  						recipe.onCrafted(this.world, this.pos, new ItemStack[]{this.inventory.get(1), this.inventory.get(2), this.inventory.get(3), this.inventory.get(4)}, stack);
  					}
  				}
  			}
  		}
  	}
  
  	@Override
  	public int getInventoryStackLimit() {
  		return 1;
  	}
  
  	@Override
  	public void setInventorySlotContents(int slot, ItemStack stack) {
  		this.inventory.set(slot, stack);
  		if (!stack.isEmpty() && stack.getCount() > getInventoryStackLimit()) {
  			stack.setCount( getInventoryStackLimit());
  		}
  		IDruidAltarRecipe recipe = DruidAltarRecipe.getDruidAltarRecipe(this.inventory.get(1), this.inventory.get(2), this.inventory.get(3), this.inventory.get(4));
  		if (!this.world.isRemote && recipe != null && !stack.isEmpty() && this.inventory.get(0).isEmpty() && this.craftingProgress == 0) {
  			recipe.onStartCrafting(world, pos, new ItemStack[] {this.inventory.get(1), this.inventory.get(2), this.inventory.get(3), this.inventory.get(4)});
  			startCraftingProcess();
  		}
  	}
  
  	private void startCraftingProcess() {
  		World world = this.world;
  		int dim = world.provider.getDimension();
  		this.world.setBlockState(this.pos, this.world.getBlockState(this.pos).withProperty(BlockDruidAltar.ACTIVE, true), 3);
  		this.craftingProgress = 1;
  		// Packet to start sound
  		TheBetweenlands.networkWrapper.sendToAllAround(new MessageDruidAltarProgress(this, -1), new NetworkRegistry.TargetPoint(dim, this.pos.getX() + 0.5D, this.pos.getY() + 0.5D, this.pos.getZ() + 0.5D, 64D));
  		// Sets client crafting progress to 1
  		TheBetweenlands.networkWrapper.sendToAllAround(new MessageDruidAltarProgress(this, 1), new NetworkRegistry.TargetPoint(dim, this.pos.getX() + 0.5D, this.pos.getY() + 0.5D, this.pos.getZ() + 0.5D, 64D));
  		// Does the metadata stuff for the circle animated textures
  		checkDruidCircleBlocks(world);
  
  		AxisAlignedBB aabb = new AxisAlignedBB(this.pos).grow(8, 6, 8);
  		List<EntityDarkDruid> druids = this.world.getEntitiesWithinAABB(EntityDarkDruid.class, aabb);
  		for(EntityDarkDruid druid : druids) {
  			druid.attackEntityFrom(DamageSource.GENERIC, druid.getHealth());
  		}
  
  		MobSpawnerLogicBetweenlands logic = BlockMobSpawnerBetweenlands.getLogic(this.world, this.pos.down());
  		if(logic != null) {
  			//Don't spawn druids while crafting
  			logic.setDelay(CRAFTING_TIME + 20);
  		}
  	}
  
  	private void stopCraftingProcess() {
  		World world = this.world;
  		int dim = world.provider.getDimension();
  		this.world.setBlockState(this.pos, this.world.getBlockState(this.pos).withProperty(BlockDruidAltar.ACTIVE, false), 3);
  		this.craftingProgress = 0;
  		// Packet to cancel sound
  		TheBetweenlands.networkWrapper.sendToAllAround(new MessageDruidAltarProgress(this, -2), new NetworkRegistry.TargetPoint(dim, this.pos.getX() + 0.5D, this.pos.getY() + 0.5D, this.pos.getZ() + 0.5D, 64D));
  		// Sets client crafting progress to 0
  		TheBetweenlands.networkWrapper.sendToAllAround(new MessageDruidAltarProgress(this, 0), new NetworkRegistry.TargetPoint(dim, this.pos.getX() + 0.5D, this.pos.getY() + 0.5D, this.pos.getZ() + 0.5D, 64D));
  		// Does the metadata stuff for the circle animated textures
  		checkDruidCircleBlocks(world);
  	}
  
  	public void sendCraftingProgressPacket() {
  		World world = this.world;
  		int dim = world.provider.getDimension();
  		TheBetweenlands.networkWrapper.sendToAllAround(new MessageDruidAltarProgress(this), new NetworkRegistry.TargetPoint(dim, this.pos.getX() + 0.5D, this.pos.getY() + 0.5D, this.pos.getZ() + 0.5D, 64D));
  	}
  
  	private void checkDruidCircleBlocks(World world) {
  		int baseRadius = 6;
  		MutableBlockPos pos = new MutableBlockPos();
  		int posX = this.pos.getX(), posY = this.pos.getY(), posZ = this.pos.getZ();
  		for (int y = 0; y < 6; y++) {
  			for (int x = -baseRadius; x <= baseRadius; x++) {
  				for (int z = -baseRadius; z <= baseRadius; z++) {
  					int dSq = x * x + z * z;
  					if (Math.round(Math.sqrt(dSq)) <= baseRadius) {
  						pos.setPos(posX + x, posY + y, posZ + z);
  						IBlockState state = world.getBlockState(pos);
  						Block block = state.getBlock();
  						if (block instanceof BlockDruidStone) {
  							if ((this.craftingProgress == 0 || this.circleShouldRevert) && state.getValue(BlockDruidStone.ACTIVE)) {
  								world.setBlockState(pos, state.withProperty(BlockDruidStone.ACTIVE, false), 3);
  							} else if (this.craftingProgress == 1 && !state.getValue(BlockDruidStone.ACTIVE)) {
  								world.setBlockState(pos, state.withProperty(BlockDruidStone.ACTIVE, true), 3);
  							}
  						}
  					}
  				}
  			}
  		}
  	}
  
  	@Override
  	@SideOnly(Side.CLIENT)
  	public AxisAlignedBB getRenderBoundingBox() {
  		return new AxisAlignedBB(this.pos.getX() - 1, this.pos.getY(), this.pos.getZ() - 1, this.pos.getX() + 2, this.pos.getY() + 3, this.pos.getZ() + 2);
  	}
  
  	@Override
  	public boolean shouldRefresh(World world, BlockPos pos, IBlockState oldState, IBlockState newState) {
  		return oldState.getBlock() != newState.getBlock();
  	}
  
  	@Override
  	public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
  		super.writeToNBT(nbt);
  		nbt.setInteger("craftingProgress", this.craftingProgress);
  		return nbt;
  	}
  
  	@Override
  	public void readFromNBT(NBTTagCompound nbt) {
  		super.readFromNBT(nbt);
  		this.craftingProgress = nbt.getInteger("craftingProgress");
  	}
  
  	@Override
  	public SPacketUpdateTileEntity getUpdatePacket() {
  		NBTTagCompound tag = new NBTTagCompound();
  		this.writeInventoryNBT(tag);
  		return new SPacketUpdateTileEntity(pos, 0, tag);
  	}
  
  	@Override
  	public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity packet) {
  		this.readInventoryNBT(packet.getNbtCompound());
  	}
  
  	@Override
  	public NBTTagCompound getUpdateTag() {
  		NBTTagCompound nbt = super.getUpdateTag();
  		this.writeInventoryNBT(nbt);
  		return nbt;
  	}
  
  	@Override
  	public void handleUpdateTag(NBTTagCompound nbt) {
  		super.handleUpdateTag(nbt);
  		this.readInventoryNBT(nbt);
  	}
  
  	@Override
  	public int[] getSlotsForFace(EnumFacing side) {
  		switch (side){
  
  			case DOWN:
  				return new int[]{0};
  			case UP:
  			case NORTH:
  			case SOUTH:
  			case WEST:
  			case EAST:
  				return new int[]{1, 2, 3, 4};
  		}
  		return super.getSlotsForFace(side);
  	}
  
  	@Override
  	public ItemStack decrStackSize(int index, int count) {
  		world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2);
  		return super.decrStackSize(index, count);
  	}
  
  	@Override
  	public ItemStack removeStackFromSlot(int index) {
  		world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 2);
  		return super.removeStackFromSlot(index);
  	}
  }
  