@Entity()
@Table(name="jb_rp_storage")
public class RPStorageData {
	
	public static RPStorageData removeDisc(Block block) {
		RPStorageData rpdata = RPStorageData.getOrCreateEntry(block);
		
		rpdata.setColor(0);
		rpdata.setAuthor("");
		rpdata.setTitle("");
		rpdata.setUrl("");
		
		JukeIt.getInstance().getDatabase().save(rpdata);
		
		return rpdata;
	}
	
	public static RPStorageData setDisc(Block block, ItemStack discIs) {
		RPStorageData rpdata = RPStorageData.getOrCreateEntry(block);
		
		rpdata.setColor(((BurnedDisc)(new SpoutItemStack(discIs)).getMaterial()).getColor().identifier());
		rpdata.setAuthor("");
		rpdata.setTitle(DiscUtil.getLabel(discIs));
		rpdata.setUrl(DiscUtil.decodeDisc(discIs));
		
		JukeIt.getInstance().getDatabase().save(rpdata);
		
		return rpdata;
	}
	
	public static void deleteEntries(Block block) {
		//delete ALL data associated to this location, just incase somehow multiples got into the database this will take care of that.
		List<RPStorageData> rpdall = JukeIt.getInstance().getDatabase().find(RPStorageData.class)
			.where()
				.eq("x", block.getX())
				.eq("y", block.getY())
				.eq("z", block.getZ())
				.ieq("world", block.getWorld().getName())
			.findList();
		if (!rpdall.isEmpty()) {
			JukeIt.getInstance().getDatabase().delete(rpdall);
		}
	}
	
	public static RPStorageData getOrCreateEntry(Block block) {
		
		RPStorageData entry = JukeIt.getInstance().getDatabase().find(RPStorageData.class)
			.where()
				.eq("x", block.getX())
				.eq("y", block.getY())
				.eq("z", block.getZ())
				.ieq("world", block.getWorld().getName())
			.findUnique();
		
		if (entry == null) {
			
			entry = new RPStorageData();
			entry.setAuthor("");
			entry.setTitle("");
			entry.setColor(0);
			entry.setNeedle(0);
			entry.setUrl("");
			
			entry.setWorld(block.getWorld().getName());
			entry.setX(block.getX());
			entry.setY(block.getY());
			entry.setZ(block.getZ());
		}
		
		return entry;
	}
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
	
	@NotEmpty
	private String world;
	
	@NotNull
	private int x;
	
	@NotNull
	private int y;

	@NotNull
	private int z;
	
	private String url;
	
	private String title;
	
	private String author;
	
	private int color;
	
	@NotNull
	private int needle;

	public boolean hasDisc() {
		if (color > 0) return true;
		return false;
	}
	
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getWorld() {
		return world;
	}

	public void setWorld(String world) {
		this.world = world;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getZ() {
		return z;
	}

	public void setZ(int z) {
		this.z = z;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public int getColor() {
		return color;
	}

	public void setColor(int color) {
		this.color = color;
	}

	public int getNeedle() {
		return needle;
	}

	public void setNeedle(int needle) {
		this.needle = needle;
	}

}
 @Entity()
@Table(name="jb_rc_data")
public class RepeaterChipData {
	
	public static RepeaterChipData getData(SpoutBlock block) {
		RepeaterChipData data = JukeIt.getInstance().getDatabase().find(RepeaterChipData.class)
				.where()
					.eq("x", block.getX())
					.eq("y", block.getY())
					.eq("z", block.getZ())
					.ieq("world", block.getWorld().getName())
				.findUnique();
		if (data == null) {
			data = new RepeaterChipData();
			data.setX(block.getX());
			data.setY(block.getY());
			data.setZ(block.getZ());
			data.setWorld(block.getWorld().getName());
			data.setTime(0);
		}
		
		return data;
	}
	
	public static void saveData(RepeaterChipData data) {
		JukeIt.getInstance().getDatabase().save(data);
	}
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
	
	@NotNull
	private int x;
	
	@NotNull
	private int y;
	
	@NotNull
	private int z;
	
	@NotEmpty
	private String world;
	
	@NotNull
	private long time;
	
	public void setId(int id) {
		this.id = id;
	}
	
	public int getId() {
		return id;
	}
	
	public int getX() {
		return x;
	}
	
	public void setX(int x) {
		this.x = x;
	}
	
	public int getY() {
		return y;
	}
	
	public void setY(int y) {
		this.y = y;
	}
	
	public int getZ() {
		return z;
	}
	
	public void setZ(int z) {
		this.z = z;
	}

	public String getWorld() {
		return world;
	}

	public void setWorld(String world) {
		this.world = world;
	}
	
	public long getTime() {
		return time;
	}
	
	public void setTime(long time) {
		this.time = time;
	}
} @Entity()
@Table(name="jb_url_data")
public class URLData {
	
	public static URLData getEntry(int id) {
		
		URLData entry = JukeIt.getInstance().getDatabase().find(URLData.class)
			.where()
				.eq("id", id)
			.findUnique();
		
		return entry;
	}
	
	public static URLData getEntry(String url) {
		URLData entry = JukeIt.getInstance().getDatabase().find(URLData.class)
			.where()
				.eq("url", url)
			.findUnique();
		
		return entry;
	}
	
	public static URLData saveEntry(String url) {
		URLData entry = getEntry(url);
		
		if (entry != null) {
			return entry;
		}
		
		entry = new URLData();
		entry.setUrl(url);
		
		JukeIt.getInstance().getDatabase().save(entry);
		
		return entry;
	}
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
	
	@NotEmpty
	private String url;
	
	public int getId() {
		return id;
	}
	
	public void setId(int id) {
		this.id = id;
	}
	
	public String getUrl() {
		return url;
	}
	
	public void setUrl(String url) {
		this.url = url;
	}
}
 public class GeneralListener implements Listener {
	
	/**
	 * This event will keep player from placing the wrong Repeater Chip, if a player pulls the repeater chip out of the creative inventory.
	 * This event must be handled here as opposed to in the spout block overrides as the block removal functions will
	 * not work in the spout block overrides, only at this level of the listener.
	 * @param event
	 */
	@EventHandler
	public void onRepeaterChipPlace(BlockPlaceEvent event) {
		if (((SpoutBlock)event.getBlock()).getCustomBlock() instanceof RepeaterChipBlock) {
			if (event.getPlayer() != null) {
				event.getPlayer().sendMessage("This block is not placeable. You must use the Repeater Chip Item on a Record Player.");
			}
			((SpoutBlock)event.getBlock()).setCustomBlock(null);
			((SpoutBlock)event.getBlock()).setType(Material.AIR);
		}
	}
	
	/**
	 * Refreshes a players record player gui on item pickup.
	 * @param event
	 */
	@EventHandler
	public void onPlayerPickupItem(PlayerPickupItemEvent event) {
		Debug.debug("Player Pickup Item");
		if (((SpoutPlayer)event.getPlayer()).getMainScreen().getActivePopup() instanceof RecordPlayerGUI) {
			((RecordPlayerGUI)((SpoutPlayer)event.getPlayer()).getMainScreen().getActivePopup()).setDirtySlots();
		}
	}
	
	@EventHandler
	public void onCraftItem(CraftItemEvent event) {
		Debug.debug("CraftItemEvent");
		SpoutItemStack craftedStack = new SpoutItemStack(event.getRecipe().getResult());
		
		if (craftedStack.getMaterial() != null && craftedStack.getMaterial() instanceof CraftPermissible) {
			Debug.debug("crafted item has permission attached");
			
			String permission = ((CraftPermissible)craftedStack.getMaterial()).getCraftPermission();
			
			if (!event.getWhoClicked().hasPermission(permission)) {
				event.setResult(Result.DENY);
				((Player)event.getWhoClicked()).sendMessage("You do not have permission to craft this item.");
				((Player)event.getWhoClicked()).sendMessage("("+permission+")");
				event.setCancelled(true);
				return;
			}
			
		}
	}
}
 public class MachineListener implements Listener {
	
	@EventHandler
	public void onPistonPull(BlockPistonRetractEvent event) {
		if (event.isCancelled() || !event.isSticky()) {
			return;
		}
		SpoutBlock block = (SpoutBlock)event.getBlock().getRelative(event.getDirection(), 2);
		
		if ( block.getCustomBlock() == null) {
			return;
		}
		if (block.getCustomBlock() instanceof MachineBlock) {
			Debug.debug("canceling piston event");
			event.setCancelled(true);
		}
		
	}
	
	@EventHandler
	public void onPistonPush(BlockPistonExtendEvent event) {
		if (event.isCancelled()) return;
		for(Block block : event.getBlocks()) {
			if ( ((SpoutBlock)block).getCustomBlock() != null) {
				if ( ((SpoutBlock)block).getCustomBlock() instanceof MachineBlock ) {
					event.setCancelled(true);
				}
			}
		}
	}
	
	@EventHandler
	public void onPlace(BlockPlaceEvent event) {
		if (
				((SpoutBlock)event.getBlock()).getCustomBlock() == null ||
				!(((SpoutBlock)event.getBlock()).getCustomBlock() instanceof MachineBlock)
				){
			return;
			
		}
		
		if (
				!event.getBlock().getRelative(BlockFace.UP).getType().equals(Material.AIR)
			) {
			
			((SpoutBlock)event.getBlock()).setCustomBlock(null);
			((SpoutBlock)event.getBlock()).setType(Material.AIR);
			
			Location loc = event.getBlock().getLocation();
			loc.setX(loc.getX()+0.5);
			loc.setZ(loc.getZ()+0.5);
			//loc.setY(loc.getY()+1.0);
			loc.getWorld().dropItem(loc, new SpoutItemStack(Blocks.machineBlock));
			return;
		}
		
		SpoutManager.getMaterialManager().overrideBlock(event.getBlock().getRelative(BlockFace.UP), Blocks.machineBlock, (byte)1);
	}
	
	@EventHandler
	public void onStart(MachineStartEvent event) {
		Debug.debug("LabelMachineStartEvent heard - starting up. ");
		SpoutManager.getMaterialManager().overrideBlock(event.getBlock().getRelative(BlockFace.UP), Blocks.machineBlock, (byte)2);
		
		new Sound(SoundEffect.MACHINE_PRESS, event.getBlock(), 8).play();
		
		Particle particle = new Particle(ParticleType.LARGESMOKE, event.getBlock().getLocation().add(0.5d, 1d, 0.5d), new Vector(0,0,0));
		particle.setAmount(50);
		particle.setMaxAge(20);
		particle.spawn();
		
		MachineRunnable r = new MachineRunnable(event.getBlock(), event.getPrimaryItem(), event.getAdditionItem(), event.getLabel()) {
			@Override
			public void run() {
				MachineProcessEvent pEvent = new MachineProcessEvent(getBlock(), getPrimaryItem(), getAdditionalItem(), getLabel());
				Bukkit.getServer().getPluginManager().callEvent(pEvent);
			}
		};
		r.runTaskLater(JukeIt.getInstance(), 10L);
	}
	
	@EventHandler
	public void onProcess(MachineProcessEvent event) {
		Debug.debug("LabelMachineProcessEvent heard - processing graphics. bzzrrt! whirr!! chjkchjk ");
		
		Particle particle = new Particle(ParticleType.SMOKE, event.getBlock().getLocation().add(0.5d, 1d, 0.5d), new Vector(0,0.2,0));
		particle.setMaxAge(10);
		particle.setAmount(5);
		new ParticleGeneratorRunnable(particle, 5000).runTaskTimer(JukeIt.getInstance(), 0L, 5L);
		
		MachineRunnable r = new MachineRunnable(event.getBlock(), event.getPrimaryItem(), event.getAdditionItem(), event.getLabel()) {
			@Override
			public void run() {
				MachineCompleteEvent pEvent = new MachineCompleteEvent(getBlock(), getPrimaryItem(), getAdditionalItem(), getLabel());
				Bukkit.getServer().getPluginManager().callEvent(pEvent);
			}
		};
		r.runTaskLater(JukeIt.getInstance(), 110L);
	}
	
	@EventHandler
	public void onDiscLabel(MachineDiscLabelEvent event) {
		Debug.sdebug("MachineDiscLabelEvent", event.getLabel());
		ItemStack newDisc = DiscUtil.setLabel(event.getDisc(), event.getLabel());
		event.setDisc(newDisc);
		
	}
	
	@EventHandler
	public void onComplete(MachineCompleteEvent event) {
		Debug.debug("LabelMachineCompleteEvent heard - its magic.");
		SpoutManager.getMaterialManager().overrideBlock(event.getBlock().getRelative(BlockFace.UP), Blocks.machineBlock, (byte)1);
		
		Particle particle = new Particle(ParticleType.CLOUD, event.getBlock().getLocation().add(0.5d, 1d, 0.5d), new Vector(0,0,0));
		particle.setGravity(0);
		particle.setScale(0.5F);
		particle.setAmount(100);
		particle.spawn();
		
		//if either item is air, transaction is a flop, just abort.
		Debug.sdebug("values -- ", event.getPrimaryItem(), event.getAdditionItem(), event.hasLabel());
		if (event.getPrimaryItem().getType().equals(Material.AIR) || event.getAdditionItem().getType().equals(Material.AIR)) {
			Debug.debug("Aborting Machine Transaction - Requires two items.");
			abortEject(event);
			return;
		}
		
		//checking to see if we had a labeling disc setup.
		ItemStack cPrimItem = event.getPrimaryItem();
		ItemStack cAddItem = event.getAdditionItem();
		SpoutItemStack sPrimItem = new SpoutItemStack(cPrimItem);
		SpoutItemStack sAddItem = new SpoutItemStack(cAddItem);
		
		if (
				cAddItem.getType().equals(Material.PAPER) &&
				sPrimItem.getMaterial() instanceof BurnedDisc
				) {
			//labeling event.
			if (!event.hasLabel()) {
				event.setLabel("Unlabeled Disc");
			}
			MachineDiscLabelEvent e = new MachineDiscLabelEvent(cPrimItem, event.getLabel());
			Bukkit.getServer().getPluginManager().callEvent(e);
			
			eject(event.getBlock(), e.getDisc());
			if (!lastOne(event.getAdditionItem())) eject(event.getBlock(), removeOne(event.getAdditionItem()));
			
			return;
		}
		
		//check if were cloning a disc.
		if ( 
				(sPrimItem.getMaterial() instanceof BlankDisc && sAddItem.getMaterial() instanceof BurnedDisc) ||
				(sPrimItem.getMaterial() instanceof BurnedDisc && sAddItem.getMaterial() instanceof BlankDisc)
				) {
			
			ItemStack master;
			ItemStack target;
			//which one are we making a copy of?
			if (sPrimItem.getMaterial() instanceof BurnedDisc) {
				master =  cPrimItem;
				target =  cAddItem;
			} else {
				master = cAddItem;
				target = cPrimItem;
			}
			
			
			ItemStack newDisc = DiscUtil.createDisc(
					((BlankDisc)(new SpoutItemStack(target)).getMaterial()).getColor(), 
					DiscUtil.decodeDisc(master), 
					DiscUtil.getLabel(master)
					);
			
			this.eject(event.getBlock(), newDisc);
			
			//eject leftovers. since were cloning the master disc, should not destroy it.
			if (sPrimItem.getMaterial() instanceof BurnedDisc) {
				eject(event.getBlock(), event.getPrimaryItem());
				if (!lastOne(event.getAdditionItem())) eject(event.getBlock(), removeOne(event.getAdditionItem()));
			} else {
				eject(event.getBlock(), event.getAdditionItem());
				if (!lastOne(event.getPrimaryItem())) eject(event.getBlock(), removeOne(event.getPrimaryItem()));
			}
			
			return;
		}
		
		//check if were coloring a blank disc
		if ( (sPrimItem.getMaterial() instanceof BlankDisc && sAddItem.getType().equals(Material.INK_SACK)) ) {
			Debug.debug("attempting to color blank disc");
			Debug.debug("Dye: ", sAddItem.getData().getData(), " : ", event.getAdditionItem().getData().getData());
			
			if (sPrimItem.getAmount() == sAddItem.getAmount()) {
				//will use all the dye, dont return any.
				//will use all the discs. dont return any.
				//return amount of new color discs.
				eject(event.getBlock(), getBlankDiscByDyeColor(sAddItem.getData().getData(), sPrimItem.getAmount()));
				return;
			}
			if (sPrimItem.getAmount() > sAddItem.getAmount()) {
				//will use all the die, dont return any.
				//will use a portion of the discs. return some.
				int amountLeft = sPrimItem.getAmount() - sAddItem.getAmount();
				sPrimItem.setAmount(amountLeft);
				eject(event.getBlock(), sPrimItem);
				//return equal number of disc in new color.
				eject(event.getBlock(), getBlankDiscByDyeColor(sAddItem.getData().getData(), sAddItem.getAmount()));
				return;
			}
			if (sAddItem.getAmount() > sPrimItem.getAmount()) {
				//will use a portion of the dye, return some.
				int amountLeft = sAddItem.getAmount() - sPrimItem.getAmount();
				sAddItem.setAmount(amountLeft);
				eject(event.getBlock(), sAddItem);
				//will us all the discs. dont return any.
				//return equal number of discs used.
				eject(event.getBlock(), getBlankDiscByDyeColor(sAddItem.getData().getData(), sPrimItem.getAmount()));
				return;
			}
			
			abortEject(event);
			return;
		}
		
		//check for coloring already burned discs.
		if ( (sPrimItem.getMaterial() instanceof BurnedDisc && sAddItem.getType().equals(Material.INK_SACK)) ) {
			Debug.debug("coloring burned disc");
			
			ItemStack newDisc = DiscUtil.createDisc(getDiscColor(sAddItem.getData().getData()), DiscUtil.decodeDisc(cPrimItem), DiscUtil.getLabel(cPrimItem));
			
			//eject left over dye.
			if (!lastOne(event.getAdditionItem())) eject(event.getBlock(), removeOne(event.getAdditionItem()));
			//eject disc (should automatically have new color)
			eject(event.getBlock(), newDisc);
			return;
		}
		
		//set if we have a recipe match
		ItemStack recipeResult = MachineRecipe.getRecipeMatch(sPrimItem, sAddItem);
		if (recipeResult != null) {
			eject(event.getBlock(), recipeResult);
			if (!lastOne(event.getAdditionItem())) eject(event.getBlock(), removeOne(event.getAdditionItem()));
			return;
		}
		
		
		//if were here, than there was nothing but unmatchable junk.
		abortEject(event);
	}
	
	@EventHandler
	public void onBreak(BlockBreakEvent event) {
		
		
		
		if (!(((SpoutBlock)event.getBlock()).getCustomBlock() instanceof MachineBlock)){
			return;
		}
		
		destroyOtherHalf(event.getBlock());
		
		((SpoutBlock)event.getBlock()).setCustomBlock(null);
		((SpoutBlock)event.getBlock()).setType(Material.AIR);
		
		if (!event.getPlayer().getGameMode().equals(GameMode.CREATIVE)) {
			Location loc = event.getBlock().getLocation();
			loc.setX(loc.getX() + 0.5);
			loc.setY(loc.getY() + 0.5);
			event.getBlock().getWorld().dropItem(loc, new SpoutItemStack(Blocks.machineBlock, 1));
		}
		
		event.setCancelled(true);
	}
	
	private void destroyOtherHalf(Block block) {
		//possibly broken top block need to destroy bottom block
		if (
				((SpoutBlock)block).getCustomBlockData() > 0 &&
				(
					((SpoutBlock)block.getRelative(BlockFace.DOWN)).getCustomBlock() instanceof MachineBlock &&
					((SpoutBlock)block.getRelative(BlockFace.DOWN)).getCustomBlockData() == 0
				)
			) {
				((SpoutBlock)block).getRelative(BlockFace.DOWN).setCustomBlock(null);
				((SpoutBlock)block).getRelative(BlockFace.DOWN).setType(Material.AIR);
		}
		
		//possibly broke bottom block need to destroy top block
		else if (
				((SpoutBlock)block).getCustomBlockData() == 0 &&
				(
					((SpoutBlock)block.getRelative(BlockFace.UP)).getCustomBlock() instanceof MachineBlock &&
					((SpoutBlock)block.getRelative(BlockFace.UP)).getCustomBlockData() > 0
				)
			) {
				((SpoutBlock)block).getRelative(BlockFace.UP).setCustomBlock(null);
				((SpoutBlock)block).getRelative(BlockFace.UP).setType(Material.AIR);
		}
	}
	
	private ItemStack removeOne(ItemStack item) {
		item.setAmount(item.getAmount()-1);
		return item;
	}
	
	private boolean lastOne(ItemStack item) {
		if (item.getAmount() == 1) return true;
		return false;
	}
	
	private boolean notAir(ItemStack item) {
		if (item.getType().equals(Material.AIR)) return false;
		return true;
	}
	
	private void eject(Block block, ItemStack item) {
		Location loc = block.getLocation();
		//loc.setX(loc.getX()+0.5);
		//loc.setZ(loc.getZ()+0.5);
		loc.setY(loc.getY()+1.0);
		block.getWorld().dropItem(loc, item);
	}
	
	private void abortEject(MachineEvent event) {
		if (notAir(event.getPrimaryItem())) {
			eject(event.getBlock(), event.getPrimaryItem());
		}
		if (notAir(event.getAdditionItem())) {
			eject(event.getBlock(), event.getAdditionItem());
		}
	}
	 /*
	private Location getParticleLocation(Block block) {
		Location loc = block.getLocation();
		loc.setX(loc.getX()+0.5);
		loc.setY(loc.getY()+1.0);
		loc.setZ(loc.getZ()+0.5);
		
		return loc;
	}
	*/
	
	/**
	 * Yes. I know. this is absolutely horrible. shame on me. bad!
	 */
	private ItemStack getBlankDiscByDyeColor(byte data, int amountOfDiscs) {
		Debug.debug("MachineListener:getBlankDiscByDyeColr: ", data, amountOfDiscs);
		
		if (data == 0) return new SpoutItemStack(Items.blankDiscBlack, amountOfDiscs);
		if (data == 4) return new SpoutItemStack(Items.blankDiscBlue, amountOfDiscs);
		if (data == 3) return new SpoutItemStack(Items.blankDiscBrown, amountOfDiscs);
		if (data == 6) return new SpoutItemStack(Items.blankDiscCyan, amountOfDiscs);
		if (data == 8) return new SpoutItemStack(Items.blankDiscGray, amountOfDiscs);
		if (data == 2) return new SpoutItemStack(Items.blankDiscGreen, amountOfDiscs);
		if (data == 12) return new SpoutItemStack(Items.blankDiscLightBlue, amountOfDiscs);
		if (data == 10) return new SpoutItemStack(Items.blankDiscLime, amountOfDiscs);
		if (data == 13) return new SpoutItemStack(Items.blankDiscMagenta, amountOfDiscs);
		if (data == 14) return new SpoutItemStack(Items.blankDiscOrange, amountOfDiscs);
		if (data == 9) return new SpoutItemStack(Items.blankDiscPink, amountOfDiscs);
		if (data == 5) return new SpoutItemStack(Items.blankDiscPurple, amountOfDiscs);
		if (data == 1) return new SpoutItemStack(Items.blankDiscRed, amountOfDiscs);
		if (data == 7) return new SpoutItemStack(Items.blankDiscLightGray, amountOfDiscs);
		if (data == 11) return new SpoutItemStack(Items.blankDiscYellow, amountOfDiscs);
		return new SpoutItemStack(Items.blankDiscWhite, amountOfDiscs);
	}
	
	/**
	 * Yes. also horrible. but im feelin really lazy right now
	 */
	private DiscColor getDiscColor(byte data ) {
		
		if (data ==  0) return DiscColor.BLACK;
		if (data ==  4) return DiscColor.BLUE;
		if (data ==  3) return DiscColor.BROWN;
		if (data ==  6) return DiscColor.CYAN;
		if (data ==  8) return DiscColor.GRAY;
		if (data ==  2) return DiscColor.GREEN;
		if (data ==  12) return DiscColor.LIGHTBLUE;
		if (data ==  10) return DiscColor.LIME;
		if (data ==  13) return DiscColor.MAGENTA;
		if (data ==  14) return DiscColor.ORANGE;
		if (data ==  9) return DiscColor.PINK;
		if (data ==  5) return DiscColor.PURPLE;
		if (data ==  1) return DiscColor.RED;
		if (data ==  7) return DiscColor.LIGHTGRAY;
		if (data ==  11) return DiscColor.YELLOW;
		return DiscColor.WHITE;
	}
}
 public class SpeakerWireListener implements Listener {
	
	public HashMap<BlockFace, SpoutBlock> getAvailableWires(SpoutBlock block) {
		List<BlockFace> faceList = Arrays.asList(BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST,  BlockFace.UP, BlockFace.DOWN);
		
		HashMap<BlockFace, SpoutBlock> wires = new HashMap<BlockFace, SpoutBlock>();
		
		//collect a list of surrounding blocks who are also speaker wire, and have atleast one open side.
		for (BlockFace face : faceList) {
			
			SpoutBlock relBlock = (SpoutBlock)block.getRelative(face);
			
			if (relBlock.getCustomBlock() != null && relBlock.getCustomBlock() instanceof SpeakerWireBlock) {
				Debug.debug("Block at ", face, " is speakerwireblock-new");
				if ( ((SpeakerWireBlock)relBlock.getCustomBlock()).hasOpenEnd(relBlock) )
				{
					//if there is a maximum of 2, we dont need anymore, so go ahead and skip
					if (wires.size() < 2) {
						wires.put(face, relBlock);
					}
				}
			}
			/*
			if (relBlock.getCustomBlock() != null && relBlock.getCustomBlock() instanceof SpeakerWireBlock) {
				Debug.debug("Block at ", face, " is speakerwireblock");
				
				if ( ((SpeakerWireBlock)relBlock.getCustomBlock()).hasOpenEnd(relBlock) )
				{
					//if there is a maximum of 2, we dont need anymore, so go ahead and skip
					if (wires.size() < 2) {
						wires.put(face, relBlock);
					}
				}
			}
			*/
			
		}
		
		return wires;
		
	}
	
	public BlockFace getConnectedFace(SpoutBlock block, BlockFace ignoreFace) {
		
		List<BlockFace> faceList = Arrays.asList(BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST, BlockFace.UP, BlockFace.DOWN);
		
		//collect a list of surrounding blocks who are also speaker wire, and have atleast one open side.
		for (BlockFace face : faceList) {
			
			if (!face.equals(ignoreFace)) {
				
				if ( ((SpeakerWireBlock)block.getCustomBlock()).isFaceConnected(block, face) ) return face;
				
			}
		}
		
		return null;
		
	}
	
	public void setBlockType(SpoutBlock wireBlock, HashMap<BlockFace, SpoutBlock> availableWires) {
		
		switch(availableWires.size()) {
		case 0:
			//do nothing;
			break;
		case 1:
			//only connect to one;
			if (
					availableWires.containsKey(BlockFace.EAST) ||
					availableWires.containsKey(BlockFace.WEST)
					) {
				
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockEastWest);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("EASTtoWEST").getTypeId());
			} else if (
					availableWires.containsKey(BlockFace.NORTH) ||
					availableWires.containsKey(BlockFace.SOUTH)
					) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockNorthSouth);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("NORTHtoSOUTH").getTypeId());
			}
			else if (
					availableWires.containsKey(BlockFace.UP) ||
					availableWires.containsKey(BlockFace.DOWN)
					) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockUpDown);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("UPtoDOWN").getTypeId());
			}
			break;
		case 2:
			//connecting to 2 wires
			if (availableWires.containsKey(BlockFace.EAST) && availableWires.containsKey(BlockFace.WEST)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockEastWest);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("EASTtoWEST").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.NORTH) && availableWires.containsKey(BlockFace.SOUTH)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockNorthSouth);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("NORTHtoSOUTH").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.NORTH) && availableWires.containsKey(BlockFace.EAST)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockNorthEast);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("NORTHtoEAST").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.EAST) && availableWires.containsKey(BlockFace.SOUTH)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockEastSouth);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("EASTtoSOUTH").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.SOUTH) && availableWires.containsKey(BlockFace.WEST)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockSouthWest);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("SOUTHtoWEST").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.WEST) && availableWires.containsKey(BlockFace.NORTH)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockWestNorth);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("WESTtoNORTH").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.UP) && availableWires.containsKey(BlockFace.DOWN)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockUpDown);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("UPtoDOWN").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.EAST) && availableWires.containsKey(BlockFace.UP)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockEastUp);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("EASTtoUP").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.WEST) && availableWires.containsKey(BlockFace.UP)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockWestUp);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("WESTtoUP").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.NORTH) && availableWires.containsKey(BlockFace.UP)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockNorthUp);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("NORTHtoUP").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.SOUTH) && availableWires.containsKey(BlockFace.UP)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockSouthUp);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("SOUTHtoUP").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.EAST) && availableWires.containsKey(BlockFace.DOWN)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockEastDown);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("EASTtoDOWN").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.WEST) && availableWires.containsKey(BlockFace.DOWN)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockWestDown);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("WESTtoDOWN").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.NORTH) && availableWires.containsKey(BlockFace.DOWN)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockNorthDown);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("NORTHtoDOWN").getTypeId());
				
			} else if (availableWires.containsKey(BlockFace.SOUTH) && availableWires.containsKey(BlockFace.DOWN)) {
				//wireBlock.setCustomBlock(Blocks.speakerWireBlockSouthDown);
				SpoutManager.getMaterialManager().overrideBlock(wireBlock, Blocks.speakerWireBlock, (byte)WireData.valueOf("SOUTHtoDOWN").getTypeId());
				
			}
			break;
		}
		
	}
	
	@EventHandler
	public void onPlace(SpeakerWirePlaceEvent event) {
		Debug.debug(event.getPlayer(), "Placing Speaker Wire");
		
		HashMap<BlockFace, SpoutBlock> wires = getAvailableWires(event.getBlock());
		
		Debug.debug(event.getPlayer(), "Surrounding Wires with open ends: ", wires.size());
		Debug.debug(event.getPlayer(), "Placing Wire");
		
		event.getBlock().setCustomBlock(Blocks.speakerWireBlock);
		setBlockType((SpoutBlock)event.getBlock(), wires);
		
		//do the same for the wipres we connected to.
		//we know that every wire in this list has ATLEAST one available connection
		for ( Entry<BlockFace, SpoutBlock> item : wires.entrySet() ) {
			
			HashMap<BlockFace, SpoutBlock> connectTo = new HashMap<BlockFace, SpoutBlock>();
			//add our block.
			connectTo.put(item.getKey().getOppositeFace(), event.getBlock());
			
			BlockFace existingFace = getConnectedFace(item.getValue(), item.getKey().getOppositeFace());
			
			if (existingFace != null) {
				connectTo.put(existingFace, item.getValue());
			}
			
			setBlockType(item.getValue(), connectTo);
			
		}
		
	}
}
 public class Blocks {
	
	public static RecordPlayer recordPlayer;
	public static JukeboxBasic jukeboxBasic;
	public static JukeboxLowRange jukeboxLowRange;
	public static JukeboxMidRange jukeboxMidRange;
	public static JukeboxLongRange jukeboxLongRange;
	public static JukeboxMaxRange jukeboxMaxRange;
	public static JukeboxWorldRange jukeboxWorldRange;
	
	public static MachineBlock machineBlock;
	
	public static DiscBurner discBurner;
	
	public static Speaker speaker;
	
	public static SpeakerWireBlock speakerWireBlock;
	
	public static RepeaterChipBlock repeaterChipBlock;
	
	public Blocks() {
		
		//speaker must load before recordPlayer
		speaker = new Speaker();
		
		discBurner = new DiscBurner();
		
		recordPlayer = new RecordPlayer();
		
		machineBlock = new MachineBlock();
		
		//the must be initialized in order
		jukeboxBasic = new JukeboxBasic();
		jukeboxLowRange = new JukeboxLowRange();
		jukeboxMidRange = new JukeboxMidRange();
		jukeboxLongRange = new JukeboxLongRange();
		jukeboxMaxRange = new JukeboxMaxRange();
		jukeboxWorldRange = new JukeboxWorldRange();
		
		speakerWireBlock = new SpeakerWireBlock();
		
		repeaterChipBlock = new RepeaterChipBlock();
	}
	
}
 public class Items {

	public static MachineTop machineTop;
	public static MachineBottom machineBottom;
	
	public static WoodFlintNeedle woodflintNeedle;
	public static BlazeFlintNeedle blazeflintNeedle;
	
	public static BlankDisc blankDiscBlack;
	public static BlankDisc blankDiscBlue;
	public static BlankDisc blankDiscBrown;
	public static BlankDisc blankDiscCyan;
	public static BlankDisc blankDiscGray;
	public static BlankDisc blankDiscGreen;
	public static BlankDisc blankDiscLightBlue;
	public static BlankDisc blankDiscLightGray;
	public static BlankDisc blankDiscLime;
	public static BlankDisc blankDiscMagenta;
	public static BlankDisc blankDiscOrange;
	public static BlankDisc blankDiscPink;
	public static BlankDisc blankDiscPurple;
	public static BlankDisc blankDiscRed;
	public static BlankDisc blankDiscWhite;
	public static BlankDisc blankDiscYellow;
	
	public static BurnedDisc burnedDiscBlack;
	public static BurnedDisc burnedDiscBlue;
	public static BurnedDisc burnedDiscBrown;
	public static BurnedDisc burnedDiscCyan;
	public static BurnedDisc burnedDiscGray;
	public static BurnedDisc burnedDiscGreen;
	public static BurnedDisc burnedDiscLightBlue;
	public static BurnedDisc burnedDiscLightGray;
	public static BurnedDisc burnedDiscLime;
	public static BurnedDisc burnedDiscMagenta;
	public static BurnedDisc burnedDiscOrange;
	public static BurnedDisc burnedDiscPink;
	public static BurnedDisc burnedDiscPurple;
	public static BurnedDisc burnedDiscRed;
	public static BurnedDisc burnedDiscWhite;
	public static BurnedDisc burnedDiscYellow;
	
	public static BurnedDisc burnedDisc;
	public static HashMap<String, BurnedDisc> burnedDiscs;
	
	public static SpeakerWire speakerWire;
	
	public static DiscOnAStick discOnAStick;
	
	public static RepeaterChipItem repeaterChipItem;
	
	public enum Disc {
		BLACK(		blankDiscBlack, burnedDiscBlack, DiscColor.BLACK),
		RED(		blankDiscRed, burnedDiscRed, DiscColor.RED),
		GREEN(		blankDiscGreen, burnedDiscGreen, DiscColor.GREEN),
		BROWN(		blankDiscBrown, burnedDiscBrown, DiscColor.BROWN),
		BLUE(		blankDiscBlue, burnedDiscBlue, DiscColor.BLUE),
		PURPLE(		blankDiscPurple, burnedDiscPurple, DiscColor.PURPLE),
		CYAN(		blankDiscCyan, burnedDiscCyan, DiscColor.CYAN),
		LIGHTGRAY(	blankDiscLightGray, burnedDiscLightGray, DiscColor.LIGHTGRAY),
		GRAY(		blankDiscGray, burnedDiscGray, DiscColor.GRAY),
		PINK(		blankDiscPink, burnedDiscPink, DiscColor.PINK),
		LIME(		blankDiscLime, burnedDiscLime, DiscColor.LIME),
		YELLOW(		blankDiscYellow, burnedDiscYellow, DiscColor.YELLOW),
		LIGHTBLUE(	blankDiscLightBlue, burnedDiscLightBlue, DiscColor.LIGHTBLUE),
		MAGENTA(	blankDiscMagenta, burnedDiscMagenta, DiscColor.MAGENTA),
		ORANGE(		blankDiscOrange, burnedDiscOrange, DiscColor.ORANGE),
		WHITE(		blankDiscWhite, burnedDiscWhite, DiscColor.WHITE);
		
		private BlankDisc blank;
		private BurnedDisc burned;
		private DiscColor color;
		
		private Disc(BlankDisc blank, BurnedDisc burned, DiscColor color) {
			this.blank = blank;
			this.burned = burned;
			this.color = color;
		}
		
		public BlankDisc blank() {
			return blank;
		}
		
		public BurnedDisc burned() {
			return burned;
		}
		
		public DiscColor color() {
			return color;
		}
		
		public static Disc getDiscByColor(DiscColor color) {
			for(Disc disc : Disc.values()) {
				if (disc.color.equals(color)) {
					return disc;
				}
			}
			return null;
		}
	}
	
	public Items() {
		
		machineTop = new MachineTop();
		machineBottom = new MachineBottom();
		
		//Init custom items.
		woodflintNeedle = new WoodFlintNeedle();
		blazeflintNeedle = new BlazeFlintNeedle();
		
		speakerWire = new SpeakerWire();
		
		blankDiscBlack = new BlankDisc("Blank Black Obsidyisc", DiscColor.BLACK);
		blankDiscBlue = new BlankDisc("Blank Blue Obsidyisc", DiscColor.BLUE);
		blankDiscBrown = new BlankDisc("Blank Brown Obsidyisc", DiscColor.BROWN);
		blankDiscCyan = new BlankDisc("Blank Cyan Obsidyisc", DiscColor.CYAN);
		blankDiscGray = new BlankDisc("Blank Gray Obsidyisc", DiscColor.GRAY);
		blankDiscGreen = new BlankDisc("Blank Green Obsidyisc", DiscColor.GREEN);
		blankDiscLightBlue = new BlankDisc("Blank Light Blue Obsidyisc", DiscColor.LIGHTBLUE);
		blankDiscLightGray = new BlankDisc("Blank Light Gray Obsidyisc", DiscColor.LIGHTGRAY);
		blankDiscLime = new BlankDisc("Blank Lime Obsidyisc", DiscColor.LIME);
		blankDiscMagenta = new BlankDisc("Blank Magenta Obsidyisc", DiscColor.MAGENTA);
		blankDiscOrange = new BlankDisc("Blank Orange Obsidyisc", DiscColor.ORANGE);
		blankDiscPink = new BlankDisc("Blank Pink Obsidyisc", DiscColor.PINK);
		blankDiscPurple = new BlankDisc("Blank Purple Obsidyisc", DiscColor.PURPLE);
		blankDiscRed = new BlankDisc("Blank Red Obsidyisc", DiscColor.RED);
		blankDiscWhite = new BlankDisc("Blank White Obsidyisc", DiscColor.WHITE);
		blankDiscYellow = new BlankDisc("Blank Yellow Obsidyisc", DiscColor.YELLOW);
		
		burnedDiscBlack = new BurnedDisc(DiscColor.BLACK);
		burnedDiscBlue = new BurnedDisc(DiscColor.BLUE);
		burnedDiscBrown = new BurnedDisc(DiscColor.BROWN);
		burnedDiscCyan = new BurnedDisc(DiscColor.CYAN);
		burnedDiscGray = new BurnedDisc(DiscColor.GRAY);
		burnedDiscGreen = new BurnedDisc(DiscColor.GREEN);
		burnedDiscLightBlue = new BurnedDisc(DiscColor.LIGHTBLUE);
		burnedDiscLightGray = new BurnedDisc(DiscColor.LIGHTGRAY);
		burnedDiscLime = new BurnedDisc(DiscColor.LIME);
		burnedDiscMagenta = new BurnedDisc(DiscColor.MAGENTA);
		burnedDiscOrange = new BurnedDisc(DiscColor.ORANGE);
		burnedDiscPink = new BurnedDisc(DiscColor.PINK);
		burnedDiscPurple = new BurnedDisc(DiscColor.PURPLE);
		burnedDiscRed = new BurnedDisc(DiscColor.RED);
		burnedDiscWhite = new BurnedDisc(DiscColor.WHITE);
		burnedDiscYellow = new BurnedDisc(DiscColor.YELLOW);
		
		discOnAStick = new DiscOnAStick();
		
		repeaterChipItem = new RepeaterChipItem();
		
	}
}
 public class MusicHandler extends ResourceHandler {

	public MusicHandler() {
		this.setDirectoriesListed(false);
		this.setResourceBase(new File(JukeIt.getInstance().getDataFolder(), "").getAbsolutePath());
	}
	
	@Override
	public void handle(
				String target,
				Request baseRequest, 
				HttpServletRequest request,
				HttpServletResponse response
			) throws IOException, ServletException {
		
		Debug.debug("Attempting to handle a music uri");
		
		if (target.startsWith("/music/")) {
			
			Debug.debug("URI is a music URI");
			super.handle(target, baseRequest, request, response);
		} else {
			baseRequest.setHandled(false);
			return;
		}
		
	}
	
} public class ServerHandler extends AbstractHandler {

	public Configuration cfg;
	
	public ServerHandler() {
		cfg = new Configuration();
		try {
			cfg.setDirectoryForTemplateLoading( new File(JukeIt.getInstance().getDataFolder(), "web") );
			cfg.setObjectWrapper(new DefaultObjectWrapper());  
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	public void handle(
				String target,
				Request baseRequest, 
				HttpServletRequest request,
				HttpServletResponse response
			) throws IOException, ServletException {
		
		Debug.debug("Handling Web Request: ", target);
		
		if ( target.equalsIgnoreCase("/") ) {
			
			response.setContentType("text/html;charset=utf-8");
			response.setStatus(HttpServletResponse.SC_OK);
			baseRequest.setHandled(true);
			
			Template template = cfg.getTemplate("index.html");
			
			Map<String, Object> dataRoot = new HashMap<String, Object>();
			dataRoot.put("serverName", JukeIt.getInstance().getConfig().getString("serverName"));
			dataRoot.put("allowUpload", JukeIt.getInstance().getConfig().getBoolean("allowWebServerUploads"));
			
			dataRoot.put("files", JukeIt.getServerFileList());
			
			try {
				template.process(dataRoot, response.getWriter());
			} catch (TemplateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			return;
		}
		
		if (target.equalsIgnoreCase("/robots.txt")) {
			response.setContentType("text/plain;charset=utf-8");
			response.setStatus(HttpServletResponse.SC_OK);
			baseRequest.setHandled(true);
			
			response.getWriter().println("User-agent: *");
			response.getWriter().println("Disallow: /");
			
			return;
		}
		
		if ( target.equalsIgnoreCase("/upload") ) {
			
			if (!JukeIt.getInstance().getConfig().getBoolean("allowWebServerUploads")) {
				return;
			}
			
			response.setContentType("text/html;charset=utf-8");
			response.setStatus(HttpServletResponse.SC_OK);
			baseRequest.setHandled(true);
			
			if (!ServletFileUpload.isMultipartContent(request))
			{
				response.getWriter().println("No File Upload Detected");
				return;
			}
			
			
			/* Straight from commons.io docs */
			
			// Create a factory for disk-based file items
			FileItemFactory factory = new DiskFileItemFactory();
			
			// Create a new file upload handler
			ServletFileUpload upload = new ServletFileUpload(factory);
			
			// Parse the request
			try {
				
				List<FileItem> items = castList(FileItem.class, upload.parseRequest(request));
				
				// Process the uploaded items
				Iterator<FileItem> iter = items.iterator();
				while (iter.hasNext()) {
				    FileItem item = (FileItem) iter.next();

				    if (item.isFormField()) {
				        //processFormField(item);
				    } else {
				        //processUploadedFile(item);
				    	//String fieldName = item.getFieldName();
				        //String fileName = item.getName();
				        //String contentType = item.getContentType();
				        //boolean isInMemory = item.isInMemory();
				        //long sizeInBytes = item.getSize();
				        
				    	if (!item.getName().endsWith(".ogg") && !item.getName().endsWith(".wav") && !item.getName().endsWith(".mp3")) {
				    		response.getWriter().println("File must be a .ogg or .wave");
				    		return;
				    	}
				    	
				    	String name = item.getName().replace(" ", "_");
				    	File uploadedFile = new File(JukeIt.getInstance().getDataFolder(), "music/"+name);
				        item.write(uploadedFile);
				        
				        response.getWriter().println("1");
				        return;
				    }
				}
				
			} catch (FileUploadException e) {
				response.getWriter().println(e.getMessage());
				return;
			} catch (Exception e) {
				response.getWriter().println(e.getMessage());
				return;
			}
	        
	        return;
		}
		
	}
	
	public static <T> List<T> castList(Class<? extends T> clazz, Collection<?> c) {
	    List<T> r = new ArrayList<T>(c.size());
	    for(Object o: c)
	      r.add(clazz.cast(o));
	    return r;
	}
} public class Debug {
	
	public static final String tag = "JukeIt";
	
	public static void debug(String debugText) {
		if (JukeIt.getInstance().getConfig().getBoolean("debug")) {
			Bukkit.getServer().getConsoleSender().sendMessage(ChatColor.GOLD +"["+tag+"] " + debugText);
		}
	}
	
	public static void debug(Object... debugTexts) {
		String allText = "";
		for (Object debugText : debugTexts) {
			allText = allText + debugText.toString();
		}
		debug(allText);
	}
	
	public static void sdebug(Object... debugTexts) {
		String allText = "";
		for (Object debugText : debugTexts) {
			allText = allText + debugText.toString() + " :: ";
		}
		debug(allText);
	}
	
	public static void debug(Player player, String debugText) {
		if (JukeIt.getInstance().getConfig().getBoolean("debug")) {
			player.sendMessage(ChatColor.DARK_GRAY + "["+ ChatColor.GRAY + tag + ChatColor.DARK_GRAY + "] " + ChatColor.GRAY +debugText);
			Bukkit.getServer().getConsoleSender().sendMessage(ChatColor.GOLD +"["+tag+"] " + player.getName() + ": " + debugText);
		}
	}
	
	public static void debug(Player player, Object... debugTexts) {
		String allText = "";
		for (Object debugText : debugTexts) {
			allText = allText + debugText.toString();
		}
		debug(player, allText);
	}
	
}
 public class Recipies {
	
	public static final char getWoodMatRefLetter(Material mat) {
		if (mat.equals(MaterialData.wood)) return '!';
		if (mat.equals(MaterialData.jungleWood)) return '@';
		if (mat.equals(MaterialData.birchWood)) return '#';
		if (mat.equals(MaterialData.spruceWood)) return '$';
		return '!';
	}
	
	public static final String buildWoodRefString( Object mat1, Object mat2, Object mat3 ) {
		String bstr = "";
		
		if ( mat1 instanceof Character ) bstr = bstr + (Character)mat1;
		else bstr = bstr + getWoodMatRefLetter((Material)mat1);
		
		if ( mat2 instanceof Character ) bstr = bstr + (Character)mat2;
		else bstr = bstr + getWoodMatRefLetter((Material)mat2);
		
		if ( mat3 instanceof Character ) bstr = bstr + (Character)mat3;
		else bstr = bstr + getWoodMatRefLetter((Material)mat3);
		
		return bstr;
	}
	
	public static final List<Material> getWoodUniqueMats(Material... mats) {
		List<Material> l = new ArrayList<Material>();
		
		for (Material mat : mats) {
			if(!l.contains(mat)) l.add(mat);
		}
		return l;
	}
	
	public static final List<Material> getWoods() {
		List<Material> woods = new ArrayList<Material>();
		woods.add(MaterialData.wood);
		woods.add(MaterialData.birchWood);
		woods.add(MaterialData.jungleWood);
		woods.add(MaterialData.spruceWood);
		
		return woods;
	}
	
	public static void load() {
		
		//////////////////
		// Speaker Wire //
		//////////////////
		SpoutManager.getMaterialManager().registerSpoutRecipe(
		new SpoutShapedRecipe( new SpoutItemStack(Items.speakerWire, 3) )
			.shape("sss", "rrr")
			.setIngredient('s', MaterialData.string)
			.setIngredient('r', MaterialData.redstone)
			);
		
		//////////////////////////////
		// Wood-Flint Record Needle //
		//////////////////////////////
		SpoutManager.getMaterialManager().registerSpoutRecipe(
			new SpoutShapedRecipe( new SpoutItemStack(Items.woodflintNeedle, 1) )
			.shape("ttt", "sss", "  f")
			.setIngredient('t', Items.speakerWire)
			.setIngredient('s', MaterialData.stick)
			.setIngredient('f', MaterialData.flint)
			);
		
		//////////////////////////////
		// Blaze-Flint Record Needle //
		//////////////////////////////
		SpoutManager.getMaterialManager().registerSpoutRecipe(
			new SpoutShapedRecipe( new SpoutItemStack(Items.blazeflintNeedle, 1) )
			.shape("ttt", "sss", "  f")
			.setIngredient('t', Items.speakerWire)
			.setIngredient('s', MaterialData.blazeRod)
			.setIngredient('f', MaterialData.flint)
			);
		
		//////////////////////
		// Disc Burner //
		//////////////////////
		SpoutManager.getMaterialManager().registerSpoutRecipe(
			new SpoutShapedRecipe( new SpoutItemStack(Blocks.discBurner, 1) )
			.shape("jf")
			.setIngredient('j', MaterialData.jukebox)
			.setIngredient('f', MaterialData.furnace)
			);
		
		
		/////////////////////
		// Blank Obsidyisc //
		/////////////////////
		SpoutManager.getMaterialManager().registerSpoutRecipe(
			new SpoutShapedRecipe( new SpoutItemStack(Items.blankDiscWhite, 1) )
			.shape(" o ", "oRo", " o ")
			.setIngredient('o', MaterialData.obsidian)
			.setIngredient('R', MaterialData.redstone)
			);
		
		
	}
	
}
 public class ResourceManager {
	
	public static final List<String> music = Arrays.asList(
			"battle_jesus_vs_cyborg_hitlers.ogg"
			);
	
	public static final List<String> web = Arrays.asList(
			"index.html",
			"favicon.png",
			
			"css/bootstrap.css",
			"css/bootstrap.min.css",
			"css/bootstrap-responsive.css",
			"css/bootstrap-responsive.min.css",
			"css/uploadify.css",
			
			"img/glyphicons-halflings.png",
			"img/glyphicons-halflings-white.png",
			"img/uploadify-cancel.png",
			"img/jb32logo.png",
			
			"js/bootstrap.js",
			"js/bootstrap.min.js",
			"js/bootstrap-collapse.js",
			"js/bootstrap-tooltip.js",
			"js/bootstrap-popover.js",
			"js/jquery.js",
			"js/jquery.uploadify.min.js",
			"js/uploadify.swf"
			);
	
	public static void copyResources() {
		for (TextureFile texture : TextureFile.values()) {
			doCopy(texture.getFile(), "textures/default");
		}
		
		for (SoundEffect sound : SoundEffect.values()) {
			doCopy(sound.getSoundFileName(), "sounds");
		}
		
		for (String m : music) {
			doCopy(m, "music");
		}
		
		for (String w : web) {
			doCopy(w, "web");
		}
	}
	
	private static void doCopy(String filename, String pathInJar) {
		
		File dir = new File(JukeIt.getInstance().getDataFolder(), pathInJar);
		
		if (!dir.exists()) dir.mkdirs();
		if (!dir.canWrite()) Bukkit.getLogger().log(Level.WARNING, "The path "+ dir.getPath() +" is not writable");
		if (!dir.isDirectory()) Bukkit.getLogger().log(Level.WARNING, "The path "+ dir.getPath() +" is not a directory");
		
		String fileCopyRelPath = new File(pathInJar, filename).getPath();
		
		JukeIt.getInstance().saveResource(fileCopyRelPath, true);
	}
	
	public static void preLoginCache() {
		for (TextureFile texture : TextureFile.values()) {
			String pack = JukeIt.getInstance().getConfig().getString("texturePack", "default");
			File toCache = new File(JukeIt.getInstance().getDataFolder(), new File("textures/"+pack, texture.getFile()).getPath());
			if (toCache.exists()) {
				SpoutManager.getFileManager().addToPreLoginCache(JukeIt.getInstance(), toCache);
			} else {
				SpoutManager.getFileManager().addToPreLoginCache(JukeIt.getInstance(), new File(JukeIt.getInstance().getDataFolder(), new File("textures/default", texture.getFile()).getPath()));
			}
		}
		for (SoundEffect sound : SoundEffect.values()) {
			SpoutManager.getFileManager().addToPreLoginCache(JukeIt.getInstance(), new File(JukeIt.getInstance().getDataFolder(), new File("sounds", sound.getSoundFileName()).getPath()));
		}
	}
	
	public static void clearCache() {
		List<String> textures = new ArrayList<String>();
		for (TextureFile texture : TextureFile.values()) {
			textures.add(texture.getFile());
		}
		SpoutManager.getFileManager().removeFromCache(JukeIt.getInstance(), textures);
	}
	
	public static void addCache() {
		for (TextureFile texture : TextureFile.values()) {
			String pack = JukeIt.getInstance().getConfig().getString("texturePack", "default");
			File toCache = new File(JukeIt.getInstance().getDataFolder(), new File("textures/"+pack, texture.getFile()).getPath());
			if (toCache.exists()) {
				SpoutManager.getFileManager().addToCache(JukeIt.getInstance(), toCache);
			} else {
				SpoutManager.getFileManager().addToCache(JukeIt.getInstance(), new File(JukeIt.getInstance().getDataFolder(), new File("textures/default", texture.getFile()).getPath()));
			}
		}
		
		for (SoundEffect sound : SoundEffect.values()) {
			SpoutManager.getFileManager().addToCache(JukeIt.getInstance(), new File(JukeIt.getInstance().getDataFolder(), new File("sounds", sound.getSoundFileName()).getPath()));
		}
		
	}
	
	public static void resetCache() {
		clearCache();
		addCache();
	}
}

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import javax.persistence.PersistenceException;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.mcstats.Metrics;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.ddl.DdlGenerator;
import com.chrischurchwell.jukeit.database.RPStorageData;
import com.chrischurchwell.jukeit.database.RepeaterChipData;
import com.chrischurchwell.jukeit.database.URLData;
import com.chrischurchwell.jukeit.listener.GeneralListener;
import com.chrischurchwell.jukeit.listener.MachineListener;
import com.chrischurchwell.jukeit.listener.SpeakerWireListener;
import com.chrischurchwell.jukeit.material.Blocks;
import com.chrischurchwell.jukeit.material.Items;
import com.chrischurchwell.jukeit.server.MusicHandler;
import com.chrischurchwell.jukeit.server.ServerHandler;
import com.chrischurchwell.jukeit.util.Debug;
import com.chrischurchwell.jukeit.util.Recipies;
import com.chrischurchwell.jukeit.util.ResourceManager;

/**
 * This file is part of JukeIt
 *
 * Copyright (C) 2011-2013  Chris Churchwell
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.chrischurchwell.jukeit;





/**
 * The main class for the JukeIt Plugin
 * @author Chris Churchwell
 */
public class JukeIt extends JavaPlugin {
	
	private static JukeIt instance;
	public Server HTTPserver;
	SelectChannelConnector HTTPconnector;
	
	Blocks blocks;
	Items items;
	
	public static JukeIt getInstance() {
		return instance;
	}
	
	public static void info(String message) {
		log(Level.INFO, message);
	}
	
	public static void warn(String message) {
		log(Level.WARNING, message);
	}
	
	public static void severe(String message) {
		log(Level.SEVERE, message);
	}
	
	public static void log(Level level, String message) {
		Bukkit.getLogger().log(level, "["+getInstance().getDescription().getName()+"] " + message);
	}
	
	public JukeIt() {
		instance = this;
		
		//load the web server
		HTTPserver = new Server();
		HTTPconnector = new SelectChannelConnector();
	}
	
	public void onEnable()
	{	
		//load mc-stats
		try {
			Metrics metrics = new Metrics(this);
			metrics.start();
		} catch (IOException e1) {
			Debug.debug("Unable to submit mcstats");
		}
		
		//copy the config
		this.getConfig().options().copyDefaults(true);
		this.saveConfig();
		
		//double check for spout.
		if (!Bukkit.getPluginManager().isPluginEnabled("Spout")) {
			severe("Could not start: SpoutPlugin not found. SpoutPlugin is required for JukeIt to operate.");
			setEnabled(false);
			return;
		}
		
		ResourceManager.copyResources();
		ResourceManager.preLoginCache();
		
		setupDatabase();
		
		items = new Items();
		blocks = new Blocks();
		
		Recipies.load();
		
		//load listeners

		this.getServer().getPluginManager().registerEvents(new GeneralListener(), this);
