public enum EpicRenameCommands {

	RENAME, LORE, EPICRENAME, SETLORELINE, 
	REMOVELORELINE, INSERTLORELINE, GLOW, REMOVEGLOW, ALIGN, EXPORT, IMPORT;
	
	
	public static String getStringName(EpicRenameCommands e) {
		switch (e) {
		case RENAME: {return "rename";}
		case LORE: {return "lore";}
		case EPICRENAME: {return "epicrename";}
		case SETLORELINE: {return "setloreline";}
		case REMOVELORELINE: {return "removeloreline";}
		case INSERTLORELINE: {return "insertloreline";}
		case GLOW: {return "glow";}
		case REMOVEGLOW: {return "removeglow";}
		case ALIGN: {return "align";}
		case EXPORT: {return "export";}
		case IMPORT: {return "import";}
		default:
			break;
		}
		return "Command not found";
	}
	
}
 public class Main extends JavaPlugin {

	public static boolean debug = false;
	public static String PLUGIN_VERISON = null;
	private static int BSTATS_PLUGIN_ID = 548;
	
	/**
	 * Default to the method for getting items in hand for MC version 1.9.x+
	 */
	public static boolean USE_NEW_GET_HAND = true; 
	
	/**
	 *  Version is set in checkServerVerison()
	 */
	public static MCVersion MC_VERSION;
	public static Main plugin;
	public static PluginFile messages = null;
	public static PluginFile statsFile = null;
	
	/**
	 * Vault economy.
	 */
	public static Economy econ = null;
	public static boolean USE_ECO = false;
	public static boolean USE_XP_COST = false;

	public static ConsoleCommandSender clogger = Bukkit.getServer().getConsoleSender();
	public static Logger log = Bukkit.getLogger();
	
	public static String prefix = Messager.color("&8[&bEpic&fRename&8] &f");

	@Override
	public void onDisable() {
		Messager.msgConsole("&cPlugin Disabled.");
		plugin = null; // Fix memory leak - maybe?
	}

	@Override
	public void onEnable() {
		plugin = this;
		
		// Save default yaml files.
		this.saveDefaultConfig();
		messages = new PluginFile(this, "messages.yml", "messages.yml");
		statsFile = new PluginFile(this, "stats.yml"); // Issue #162
		
		PLUGIN_VERISON = Main.getInstance().getDescription().getVersion();
		
		checkServerVerison();

		Messager.msgConsole("&bVersion: &c" + PLUGIN_VERISON + " &bMC Version: &c" + MC_VERSION.toString());
		Messager.msgConsole("&cThis plugin is Copyright (c) 2021 Justin \"JustBru00\" Brubaker. This plugin is licensed under the MPL v2.0. "
				+ "You can view a copy of it at: http://bit.ly/2eMknxx");

		Messager.msgConsole("&aStarting to enable plugin...");

		// ISSUE #113
		if (ConfigurationManager.doesConfigYmlNeedUpdated()) {
			Messager.msgConsole("&c[WARN] The config.yml file version is incorrect. EpicRename v" + PLUGIN_VERISON +
					" expects a config.yml version of " + ConfigurationManager.CONFIG_VERSION + 
					". Attempting to add missing values to the config file.");
			ConfigurationManager.updateConfigYml();
		}
		
		if (ConfigurationManager.doesMessagesYmlNeedUpdated()) {
			Messager.msgConsole("&c[WARN] The messages.yml file version is incorrect. EpicRename v" + PLUGIN_VERISON +
					" expects a messages.yml version of " + ConfigurationManager.MESSAGES_VERSION + 
					". Attempting to add missing values to the config file.");
			ConfigurationManager.updateMessagesYml();
		}		
		// END ISSUE #113

		if (Main.getInstance().getConfig().getBoolean("economy.use")) {
			USE_ECO = true;
			Messager.msgConsole("&aEconomy is enabled in the config.");
		}
		
		if (Main.getInstance().getConfig().getBoolean("xp.use")) {
			USE_XP_COST = true;
			Messager.msgConsole("&aExperience cost is enabled in the config.");
		}

		if (!setupEconomy()) {
			// Message now handled in setupEconomy()
			Messager.msgConsole("&cDisabling support for economy features.");
			USE_ECO = false;
		}

		// Register Listeners
		PluginManager pm = Bukkit.getServer().getPluginManager();
		pm.registerEvents(new OnJoin(), this);
		pm.registerEvents(new ExploitPreventionListener(), this);

		// Command Executors
		getCommand("rename").setExecutor(new Rename());
		getCommand("epicrename").setExecutor(new EpicRename());
		getCommand("lore").setExecutor(new Lore());		
		getCommand("setloreline").setExecutor(new SetLoreLine());
		getCommand("removeloreline").setExecutor(new RemoveLoreLine());
		getCommand("insertloreline").setExecutor(new InsertLoreLine());
		getCommand("glow").setExecutor(new Glow());
		getCommand("removeglow").setExecutor(new RemoveGlow());
		getCommand("import").setExecutor(new Import());;
		getCommand("export").setExecutor(new Export());

		// Issue #161 - Removed MC Stats
		
		// Start bstats
		BStats bstats = new BStats(this, BSTATS_PLUGIN_ID);
		
		bstats.addCustomChart(new BStats.SimplePie("economy_features", new Callable<String>() {
			
			@Override
	        public String call() throws Exception {
				return Boolean.toString(Main.USE_ECO);
	        }
			
		}));
		
		bstats.addCustomChart(new BStats.SimplePie("uses_epicrenameonline_features", new Callable<String>() {
			
			@Override
	        public String call() throws Exception {
				return Boolean.toString(Main.isEpicRenameOnlineFeaturesUsedBefore());
	        }
		
		}));	
		
		// ISSUE #152
		bstats.addCustomChart(new BStats.SimplePie("experience_cost_feature", new Callable<String>() {
			
			@Override
	        public String call() throws Exception {
				return Boolean.toString(Main.USE_XP_COST);
	        }
		
		}));
		
		// Prefix 
		if (Main.getInstance().getConfig().getString("prefix") != null) {
			prefix = Messager.color(Main.getInstance().getConfig().getString("prefix"));
		} else {
			Messager.msgConsole("&cThe prefix in the config is null. Keeping default instead.");
		}
		Messager.msgConsole("&aPrefix set to: '" + prefix + "&a'");

		Messager.msgConsole("&aPlugin Enabled!");
	}

	/**
	 * 
	 * @param path Path to the message in messages.yml
	 * @return The colored string from messages.yml
	 */
	public static String getMsgFromConfig(String path) {
		if (messages.getString(path) == null) {
			Debug.send("[Main#getMsgFromConfig()] A message from messages.yml was NULL. The path to the message is: " + path);
			return "[ERROR] Could not read value from messages.yml. Ask a server admin to enable /epicrename debug to find the broken value. [ERROR]";
		}
		return messages.getString(path); // Removed duplicate Messager.color(); Messager#msgXXXX colors the message.
	}
	
	public static boolean getBooleanFromConfig(String path) {
		return Main.getInstance().getConfig().getBoolean(path);
	}

	public static Main getInstance() {
		return plugin;
	}

	public static void reloadConfigs() {
		getInstance().reloadConfig();
		messages.reload();
		if (Main.getInstance().getConfig().getBoolean("economy.use")) {
			USE_ECO = true;
			Messager.msgConsole("&aEconomy is enabled in the config.");
		}
		
		if (Main.getInstance().getConfig().getBoolean("xp.use")) {
			USE_XP_COST = true;
			Messager.msgConsole("&aXp cost is enabled in the config.");
		}
		
		// ISSUE #125 - Prefix not correctly loaded by /epicrename reload
		if (Main.getInstance().getConfig().getString("prefix") != null) {
			prefix = Messager.color(Main.getInstance().getConfig().getString("prefix"));
			Messager.msgConsole("&aPrefix set to: '" + prefix + "&a'");	
		} else {
			Messager.msgConsole("&cThe prefix in the config is null. Keeping the prefix set as the default instead.");
		}			
		// END ISSUE #125
	}

	public static void checkServerVerison() {
		String version = Bukkit.getVersion();
		// Check Server Version
		if ((version.contains("1.7")) || (version.contains("1.8"))) {
			USE_NEW_GET_HAND = false;
			MC_VERSION = MCVersion.ONE_DOT_SEVEN_OR_NEWER;
			Debug.send("[Main#checkServerVersion()] Using methods for version 1.7 or 1.8");
			Messager.msgConsole("&c[CheckServerVersion] Server running 1.7 or 1.8. EpicRename will stop supporting these versions in the near future.");
		} else if ((version.contains("1.9")) || (version.contains("1.10"))
				|| (version.contains("1.11")) || version.contains("1.12")) {
			USE_NEW_GET_HAND = true;
			MC_VERSION = MCVersion.ONE_DOT_NINE_OR_NEWER;
			Messager.msgConsole("&c[CheckServerVersion] Server running 1.9-1.12. EpicRename will stop supporting these versions in the future.");
		} else if (version.contains("1.13") || version.contains("1.14") || version.contains("1.15")) {
			USE_NEW_GET_HAND = true;
			MC_VERSION = MCVersion.ONE_DOT_NINE_OR_NEWER;
			Messager.msgConsole("&c[CheckServerVersion] Server running 1.13-1.15. EpicRename may stop supporting these versions in the future.");
		} else {
			// Version 1.16 or newer
			// ISSUE #150 HEX COLOR CODES
			USE_NEW_GET_HAND = true;
			MC_VERSION = MCVersion.ONE_DOT_SIXTEEN_OR_NEWER;
			Messager.msgConsole("&a[CheckServerVersion] Server running 1.16 or newer. Hex color code support has been enabled.");
		} // End of Server Version Check
	}

	/**
	 * Sets up vault economy.
	 * 
	 * @return
	 */
	private boolean setupEconomy() {
		if (getServer().getPluginManager().getPlugin("Vault") == null) {
			Messager.msgConsole("&cVault not found! If you would like to use economy features download Vault at: "
							+ "http://dev.bukkit.org/bukkit-plugins/vault/");
			return false;
		}
		RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class);
		if (rsp == null) {
			Messager.msgConsole("&cFailed to get the economy details from Vault. Is there a Vault compatible economy plugin installed?");
			return false;
		}
		econ = rsp.getProvider();
		return econ != null;
	}

	/**
	 * Gets the stack trace as a String from a Throwable
	 * @param aThrowable
	 * @return
	 */
	public static String getStackTrace(Throwable aThrowable) {
	    Writer result = new StringWriter();
	    PrintWriter printWriter = new PrintWriter(result);
	    aThrowable.printStackTrace(printWriter);
	    return result.toString();
	  }
	
	public static PluginFile getMessagesYmlFile() {
		return messages;
	}
	
	/**
	 * Issue #162
	 * Saves the given value to the stats.yml file.
	 * Will only actually save the value to the file if it is different than the value already set in the file.
	 * This is to prevent any extra IO calls.
	 * @param The boolean value of EpicRenameOnline feature use.
	 */
	public static void setEpicRenameOnlineFeaturesUsedBefore(boolean value) {
		if (statsFile != null) {
			if (statsFile.isBoolean("epicrenameonline_features_used_before")) {
				if (statsFile.getBoolean("epicrenameonline_features_used_before") != value) {
					statsFile.set("epicrenameonline_features_used_before", value);
					statsFile.save();
				}
			}			
		} else {
			Debug.send("[Main#setEpicRenameOnlineFeaturesUsedBefore] stats.yml file is null. Are you sure it was able to be saved during onEnable?");
		}
	}
	
	/**
	 * Issue #162
	 * Reads the current boolean value of the key epicrenameonline_features_used_before from stats.yml
	 * @return The boolean value.
	 */
	public static boolean isEpicRenameOnlineFeaturesUsedBefore() {
		if (statsFile == null) {
			Debug.send("[Main#isEpicRenameOnlineFeaturesUsedBefore] stats.yml file is null. Are you sure it was able to be saved duriong onEnable?");
			return false;
		}
		
		if (statsFile.isBoolean("epicrenameonline_features_used_before")) {
			return statsFile.getBoolean("epicrenameonline_features_used_before");
		} else {
			statsFile.set("epicrenameonline_features_used_before", false);
			statsFile.save();
			return false;
		}
	}
}
 public class Blacklists {

	// VERSION 3
	
	/**
	 * Issue #81
	 * Checks if the name of the item is one of the ones from the config.
	 * Useful to keep players from renaming important names.
	 * @param p The Player that we are checking for.
	 * @return True if the item can be edited. False if the item contains a blacklisted name.
	 */
	public static boolean checkExistingName(Player p) {
		Debug.send("[Blacklists#checkExistingName(Player)] Method called");
		
			if (RenameUtil.getInHand(p).getType() == Material.AIR || RenameUtil.getInHand(p) == null) {
				Debug.send("[Blacklists#checkExistingName(Player)] Item was AIR or NULL");
				return true;
			}
			
			String itemName = RenameUtil.getInHand(p).getItemMeta().getDisplayName();
			itemName = ChatColor.stripColor(itemName);
			
			if (itemName == null) {
				Debug.send("[Blacklists#checkExistingName(Player)] Item existing name was NULL");
				return true;
			}
			
			for (String blacklistedString : Main.getInstance().getConfig().getStringList("blacklists.existingname")) {
				if (blacklistedString != null) {
					
					blacklistedString = ChatColor.stripColor(Messager.color(blacklistedString));
					
					if (itemName.toLowerCase().contains(blacklistedString.toLowerCase())) {			
						Debug.send("[Blacklists#checkExistingName(Player)] Name contained '" + blacklistedString + "'");
						
						if (p.hasPermission("epicrename.bypass.existingname")) {
							// Player has bypass permission
							Debug.send("[Blacklists#checkExistingName(Player)] Player had the epicrename.bypass.existingname permission.");
							if (!Main.getBooleanFromConfig("disable_bypass_messages")) { // Issue #107
								Messager.msgPlayer(Main.getMsgFromConfig("blacklists.existingname.bypass"), p);
							} else {
								Debug.send("Bypass messages are disabled.");
							}
							return true;	
						} 							
						
						return false;						
					} 
				}				
			}
			
		return true;
	}
	
	/**
	 * Issue #81
	 * Checks if the lore of the item is one of the ones from the config file.
	 * Useful to keep players from changing important lore.
	 * @param p The Player that we are checking for.
	 * @return True if the item can be edited. False if the item contains a blacklisted lore string.
	 */
	public static boolean checkExistingLore(Player p) {
		Debug.send("[Blacklists#checkExistingLore(Player)] Method called");
		
		if (RenameUtil.getInHand(p).getType() == Material.AIR || RenameUtil.getInHand(p) == null) {
			Debug.send("[Blacklists#checkExistingLore(Player)] Item was AIR or NULL");
			return true;
		}
		
			List<String> loreLines = RenameUtil.getInHand(p).getItemMeta().getLore();
			
			if (loreLines == null) {
				Debug.send("[Blacklists#checkExistingLore(Player)] Lore from existing item was NULL");
				return true;
			}
			
			for (String loreLine : loreLines) {
				loreLine = ChatColor.stripColor(loreLine);
				
				for (String blacklistedString : Main.getInstance().getConfig().getStringList("blacklists.existingloreline")) {
					if (blacklistedString != null) {
					
						blacklistedString = ChatColor.stripColor(Messager.color(blacklistedString));
					
						if (loreLine.toLowerCase().contains(blacklistedString.toLowerCase())) {
							Debug.send("[Blacklists#checkExistingLore(Player)] Lore Line: '"+ loreLine +  "' contained '" + blacklistedString + "'");
							if (p.hasPermission("epicrename.bypass.existinglore")) {
								// Player has bypass permission
								Debug.send("[Blacklists#checkExistingLore(Player)] Player had the epicrename.bypass.existinglore permission.");
								if (!Main.getBooleanFromConfig("disable_bypass_messages")) { // Issue #107
									Messager.msgPlayer(Main.getMsgFromConfig("blacklists.existinglore.bypass"), p);
								} else {
									Debug.send("Bypass messages are disabled.");
								}
								return true;
							} 								
								
							return false;
						}
					}				
				}
			}	
		
		return true;
	}
	
	/**
	 * 
	 * @param m The Material from the {@link CommandExecutor}. This will also check is the player has the bypass permission. It will message the player.
	 * @return True if NO blacklisted material found. False if a blacklisted material is FOUND.
	 */
	public static boolean checkMaterialBlacklist(Material m, Player p) {
		
		// Issue #74
		if (p.hasPermission("epicrename.bypass.materialblacklist")) {
			Debug.send("Player just bypassed the material blacklist.");			
			if (!Main.getBooleanFromConfig("disable_bypass_messages")) { // Issue #107
				Messager.msgPlayer(Main.getMsgFromConfig("blacklists.material.bypass"), p);
			} else {
				Debug.send("Bypass messages are disabled.");
			}
			return true;
		}
		// End issue #74
		
		for (String s : Main.getInstance().getConfig().getStringList("blacklists.material")) {
			if (s != null) {
				if (m == Material.getMaterial(s)) {
					Debug.send("Material blacklist detected blacklisted material. (" + m.toString() + ")");
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 
	 * @param args The arguments from the {@link CommandExecutor}. This will also check is the player has the bypass permission. It will message the player.
	 * @return True if NO blacklisted word found. False if a blacklisted word is FOUND.
	 */
	public static boolean checkTextBlacklist(String[] args, Player p) {
		
		// Issue #74
		if (p.hasPermission("epicrename.bypass.textblacklist")) {
			Debug.send("Player just bypassed the text blacklist.");
			if (!Main.getBooleanFromConfig("disable_bypass_messages")) { // Issue #107
				Messager.msgPlayer(Main.getMsgFromConfig("blacklists.text.bypass"), p);
			} else {
				Debug.send("Bypass messages are disabled.");
			}
			
			return true;
		}
		// End issue #74
		
		StringBuilder builder = new StringBuilder("");		
		String completeArgs = "";		
		
		for (String item : args) {
			builder.append(item + " ");
		}
		
		completeArgs = builder.toString().trim();
		if (Main.getInstance().getConfig().getBoolean("replace_underscores")) {
			completeArgs = completeArgs.replace("_", " ");
			Debug.send("Replaced the underscores.");
		}
		
		completeArgs = ChatColor.stripColor(Messager.color(completeArgs));
		
		for (String s : Main.getInstance().getConfig().getStringList("blacklists.text")) {
			if (s != null) {
				if (completeArgs.toLowerCase().contains(s.toLowerCase())) {
					Debug.send("Text blacklist has found blacklisted word. (" + s + ")");
					return false;
				}
			}
		}

		return true;
	}

}
 public class Debug {
	public static void send(String msg) {
		if (Main.debug) {
		Bukkit.broadcastMessage(Messager.color(Main.prefix + "&8[&cDebug&8] &c" + msg));
		}
	}
}
 public class MaterialPermManager {

	public static final String MATERIAL_PERM = "epicrename.{CMD}.material.{MATERIAL}";
	
	/**
	 * Checks if the player has permission for the material provided.
	 * @param erc The command that is being performed.
	 * @param toCheck The {@link ItemStack} to check the material of.
	 * @param p The player that we are checking permissions for.
	 * @return True if the player has permission. False if the player doesn't have permission.
	 */
	public static boolean checkPerms(EpicRenameCommands erc, ItemStack toCheck, Player p) {
		// New Permission Checks
		String newPerm = MATERIAL_PERM.replace("{CMD}", EpicRenameCommands.getStringName(erc)).replace("{MATERIAL}", toCheck.getType().toString());
		String allNewPerm = MATERIAL_PERM.replace("{CMD}", EpicRenameCommands.getStringName(erc)).replace("{MATERIAL}", "*");
		
		//Debug.send("[MaterialPermManager] NewPerm: " + newPerm + " allNewPerm: " + allNewPerm);
		
		if (p.hasPermission(allNewPerm)) {
			Debug.send("[MaterialPermManager] The player has permission. Perm: " + allNewPerm);
			return true;
		}
		
		if (p.hasPermission(newPerm)) {
			Debug.send("[MaterialPermManager] The player has permission. Perm: " + newPerm);
			return true;
		}

		Debug.send("[MaterialPermManager] The player doesn't have any of the correct material permissions.");
		return false;
	}
}
 public class Messager {	
	
	private static final Pattern RGB_PATTERN = Pattern.compile("(&)?&#([0-9a-fA-F]{6})");
		
	public static String color(String uncolored) {
		if (Main.MC_VERSION == null) {
			return ChatColor.translateAlternateColorCodes('&', uncolored);	
		}
		
		if (Main.MC_VERSION.equals(MCVersion.ONE_DOT_SIXTEEN_OR_NEWER)) {
			return ChatColor.translateAlternateColorCodes('&', convertHexColorCodes(uncolored));		
		}
		
		return ChatColor.translateAlternateColorCodes('&', uncolored);		
	}
	
	/**
	 * ISSUE #150
	 * Converts hex color codes.
	 * @param uncolored
	 * @return
	 */
	public static String convertHexColorCodes(String uncolored) {
		StringBuffer builder = new StringBuffer();
		
		Matcher matcher = RGB_PATTERN.matcher(uncolored);
		
		while(matcher.find()) {
			boolean escaped = (matcher.group(1) != null);
			
			if (!escaped) {
				try {
					String hexColorCode = matcher.group(2);
					matcher.appendReplacement(builder, parseHexColor(hexColorCode));
					continue;
				} catch (NumberFormatException e) {
					//Ignore
				}				
			}
			matcher.appendReplacement(builder, "&#$2");
		}
		matcher.appendTail(builder);
		
		return builder.toString();
	}
	
    /**
     * @throws NumberFormatException If the provided hex color code is incorrect or if the version less than 1.16.
     */
    public static String parseHexColor(String hexColor) throws NumberFormatException {
        if (Main.MC_VERSION != MCVersion.ONE_DOT_SIXTEEN_OR_NEWER) {
            throw new NumberFormatException("RGB Colors only supported in 1.16 or newer");
        }

        if (hexColor.startsWith("#")) {
            hexColor = hexColor.substring(1);
        }
        
        if (hexColor.length() != 6) {
            throw new NumberFormatException("Invalid Length");
        }
        
        Color.decode("#" + hexColor);
        
        StringBuilder assembledColorCode = new StringBuilder();
        
        assembledColorCode.append("\u00a7x");
        
        for (char curChar : hexColor.toCharArray()) {
            assembledColorCode.append("\u00a7").append(curChar);
        }
        
        return assembledColorCode.toString();
    }
	
	public static void msgConsole(String msg) {		
		msg = VariableReplacer.replace(msg);
		
		if (Main.clogger != null) {
		Main.clogger.sendMessage(Main.prefix + Messager.color(msg));		
		} else {
			Main.log.info(ChatColor.stripColor(Messager.color(msg)));
		}
	}
	
	public static void msgPlayer(String msg, Player player) {
		msg = VariableReplacer.replace(msg);
		player.sendMessage(Main.prefix + Messager.color(msg));
	}	
	
	/**
	 * Sends a message from the provided messages.yml path to the provided sender.
	 * @param msgPath
	 * @param sender
	 */
	public static void msgSenderWithConfigMsg(String msgPath, CommandSender sender) {
		String msg = Main.getMsgFromConfig(msgPath);
		msg = VariableReplacer.replace(msg);
		sender.sendMessage(Main.prefix + Messager.color(msg));
	}
	
	public static void msgSender(String msg, CommandSender sender) {
		msg = VariableReplacer.replace(msg);
		sender.sendMessage(Main.prefix + Messager.color(msg));
	}	
}
 public class RenameUtil {

	// VERSION 3

	/**
	 * 
	 * @param sender
	 *            The {@link Player} who sent the command.
	 * @param args
	 *            The arguments of the command sent to the {@link CommandExecutor}
	 * @param erc
	 *            The command that called this method.
	 */
	@SuppressWarnings("deprecation")
	public static void renameHandle(Player player, String[] args, EpicRenameCommands erc) {

		if (erc == EpicRenameCommands.RENAME) { // Command /rename handling START
			Debug.send("[RenameUtil] RenameUtil.rename() handling from /rename");

			ItemStack inHand = RenameUtil.getInHand(player); // Item in the players hand.

			if (Blacklists.checkTextBlacklist(args, player)) { // Check Text Blacklist
				Debug.send("[RenameUtil] Passed Text Blacklist");
				if (Blacklists.checkMaterialBlacklist(inHand.getType(), player)) { // Check Material Blacklist
					Debug.send("[RenameUtil] Passed Material Blacklist Check.");
					if (Blacklists.checkExistingName(player)) { // Check Existing Name Blacklist
						Debug.send("[RenameUtil] Passed Existing Name Blacklist Check.");
						if (Blacklists.checkExistingLore(player)) { // Check Existing Lore Blacklist
							Debug.send("[RenameUtil] Passed Existing Lore Blacklist Check.");
							if (CharLimit.checkCharLimit(args, player)) { // Check Character Limit
								Debug.send("[RenameUtil] Passed Character Limit Check.");
								if (FormattingPermManager.checkPerms(erc, args, player)) {
									Debug.send("[RenameUtil] Passed Format Permissions Check.");
									if (inHand.getType() != Material.AIR) { // Check != Air
										if (MaterialPermManager.checkPerms(erc, inHand, player)) { // Check for per material permissions
											
											StringBuilder builder = new StringBuilder("");
											String completeArgs = "";

											for (String item : args) {
												builder.append(item + " ");
											}
											
											completeArgs = builder.toString().trim();
											
											if (Main.getInstance().getConfig().getBoolean("replace_underscores")) {
												completeArgs = completeArgs.replace("_", " ");
												Debug.send("[RenameUtil] Replaced the underscores.");
											}
											
											// Issue #32
											if (!FormattingCodeCounter.checkMinColorCodes(player, completeArgs, erc, true)) {
												FormattingCodeCounter.sendMinNotReachedMsg(player, erc);
												return;
											}
											Debug.send("[RenameUtil] Passed FormattingCodeCounter Minimum Check.");
											
											if (!FormattingCodeCounter.checkMaxColorCodes(player, completeArgs, erc, true)) {
												FormattingCodeCounter.sendMaxReachedMsg(player, erc);
												return;
											}
											Debug.send("[RenameUtil] Passed FormattingCodeCounter Maximum Check.");
											// End Issue #32

											completeArgs = Messager.color(completeArgs);
											
											EcoMessage ecoStatus = EconomyManager.takeMoney(player,	EpicRenameCommands.RENAME);

											if (ecoStatus == EcoMessage.TRANSACTION_ERROR) {
												return;
											}
											
											// Add experience cost option #121
											XpMessage xpStatus = XpCostManager.takeXp(player, EpicRenameCommands.RENAME);
											
											if (xpStatus == XpMessage.TRANSACTION_ERROR) {
												return;
											}

											if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
												player.getInventory().setItemInMainHand(
														RenameUtil.renameItemStack(player, args, inHand));
												Messager.msgPlayer(Main.getMsgFromConfig("rename.success"), player);
												Messager.msgConsole(Main.getMsgFromConfig("rename.log")
														.replace("{player}", player.getName())
														.replace("{name}", completeArgs));
												return;
											} else { // Use older method.
												player.setItemInHand(RenameUtil.renameItemStack(player, args, inHand));
												Messager.msgPlayer(Main.getMsgFromConfig("rename.success"), player);
												Messager.msgConsole(Main.getMsgFromConfig("rename.log")
														.replace("{player}", player.getName())
														.replace("{name}", completeArgs));
												return;
											}
										} else {
											Messager.msgPlayer(
													Main.getMsgFromConfig("rename.no_permission_for_material"), player);
											return;
										}
									} else {
										Messager.msgPlayer(Main.getMsgFromConfig("rename.cannot_rename_air"), player);
										return;
									}
								} else {
									// Message handled by FormattingPermManager
									return;
								}
							} else {
								Messager.msgPlayer(Main.getMsgFromConfig("rename_character_limit.name_too_long"), player);
								return;
							}
						} else {
							Messager.msgPlayer(Main.getMsgFromConfig("rename.blacklisted_existing_lore_found"), player);
							return;
						}
					} else {
						Messager.msgPlayer(Main.getMsgFromConfig("rename.blacklisted_existing_name_found"), player);
						return;
					}
				} else {
					Messager.msgPlayer(Main.getMsgFromConfig("rename.blacklisted_material_found"), player);
					return;
				}
			} else {
				Messager.msgPlayer(Main.getMsgFromConfig("rename.blacklisted_word_found"), player);
				return;
			}
		} else {
			Debug.send(
					"What are you sending a non rename command to RenameUtil#rename() for? Either you failed at using this plugin as a API or JustBru00 failed big time.");
		}
	}

	/**
	 * 
	 * @param player
	 *            The {@link Player} who ran the command.
	 * @param args
	 *            The arguments of the command.
	 * @param toRename
	 *            The {@link ItemStack} that is being renamed.
	 * @return The renamed {@link ItemStack}.
	 */
	public static ItemStack renameItemStack(Player player, String[] args, ItemStack toRename) {

		StringBuilder builder = new StringBuilder("");
		String completeArgs = "";

		for (String item : args) {
			builder.append(item + " ");
		}

		completeArgs = builder.toString().trim();
		if (Main.getInstance().getConfig().getBoolean("replace_underscores")) {
			completeArgs = completeArgs.replace("_", " ");
			Debug.send("Replaced the underscores.");
		}
		
		// ISSUE #130
		if (Main.getInstance().getConfig().getBoolean("add_trailing_space_to_rename")) {
			completeArgs = completeArgs + " ";
			Debug.send("[RenameUtil] Added trailing space to rename arguments.");
		}
		// END ISSUE #130
		
		// ISSUE #137
		if (Main.getInstance().getConfig().getBoolean("add_leading_space_to_rename")) {
			completeArgs = " " + completeArgs;
			Debug.send("[RenameUtil] Added leading space to rename arguments.");
		}
		// END ISSUE #137

		Debug.send("The complete args result is: " + completeArgs);

		ItemMeta im = toRename.getItemMeta();
		im.setDisplayName(Messager.color(completeArgs));
		toRename.setItemMeta(im);

		return toRename;
	}

	/**
	 * This method gets the item in the players main hand. It will use the correct
	 * method for the server version it is running on.
	 * 
	 * @param player
	 *            The player to get the item from.
	 * @return The item stack in the players hand.
	 */
	@SuppressWarnings("deprecation")
	public static ItemStack getInHand(Player player) {
		ItemStack returning = null;

		if (Main.USE_NEW_GET_HAND) {
			returning = player.getInventory().getItemInMainHand();
			return returning;
		} else {

			try {
				returning = player.getItemInHand();
			} catch (Exception e) {
				Debug.send(
						"&cProblem while getting the ItemStack inHand. Failed at player.getItemInHand() Server version problem?");
				Messager.msgConsole(
						"&cProblem while getting the ItemStack inHand. Failed at player.getItemInHand() Server version problem?");
			}

			return returning;
		}
	}

}
 public class WorldChecker {

	/**
	 * Checks the config to see if the current world is enabled or not.
	 * @param p The player who is running the command.
	 * @return True if the world is okay false if the world is disabled.
	 */
	public static boolean checkWorld(Player p) {
		
		if (!Main.getInstance().getConfig().getBoolean("per_world")) {
			Debug.send("Per world is disabled.");			
			return true;
		}
		
		Location l = p.getLocation();
		
		List<String> worlds;
		worlds = Main.getInstance().getConfig().getStringList("enabled_worlds");
		
		for (String s : worlds) {
			if (l.getWorld().getName().equals(s)) {
				Debug.send("In an enabled world");
				return true;
			}
		}
		Debug.send("In a disabled world.");
		return false;
	}
	
}
 public class FormatItem {
	protected ItemStack item;
	
	/***
	 * Creates instance of FormatItem from an ItemStack
	 * @param item ItemStack you want to get the format of.
	 */
	public FormatItem(ItemStack item) {
		this.item = item;
	}
	
	/***
	 * Creates instance of FormatItem from a Material
	 * @param material Material you want to get the format of.
	 */
	public FormatItem(Material material) {
		this.item = new ItemStack(material);
	}
	
	/***
	 * Creates instance of FormatItem from a display name.
	 * @param displayName String you want to get the format of.
	 */
	public FormatItem(String displayName) {
		try {
			Material material = Material.valueOf(displayName.toUpperCase().replace(" ", "_"));
			this.item = new ItemStack(material);
		} catch (Exception ex) {
			this.item = new ItemStack(Material.AIR);
		}
	}
	
	/***
	 * Returns the ItemStack value of the formatter.
	 * @return ItemStack
	 */
	public ItemStack getItem() { return this.item; }
	
	/***
	 * Private instance for internal usage only.
	 * @return Format of the FormatItem instance
	 */
	private String format() {
		if(this.item == null)
			return null;
		
		ItemMeta im = this.item.getItemMeta();
		if(im != null && im.getDisplayName() != null && im.getDisplayName() != "")
			return im.getDisplayName();
		
		Material material = this.item.getType();
		
		//Format to proper label: "Stone Bricks" instead of STONE_BRICKS
		String lowerCaseDisplay = material.toString().toLowerCase();
		String formattedDisplay = "";
		for(String lcd : lowerCaseDisplay.split("_")) {
			String lcdf = lcd.substring(0, 1).toUpperCase() + lcd.substring(1);
			formattedDisplay += lcdf + " ";
		}
		
		//Remove ending space bar if it has it
		if(formattedDisplay.endsWith(" "))
			formattedDisplay = formattedDisplay.substring(0, formattedDisplay.length()-1);
		
		return formattedDisplay;
	}
	
	/***
	 * Grabs the display name of the ItemFormat instance.
	 * @return String of the display name.
	 */
	public String getName() {
		String format = this.format();
		if(format != null)
			return format;
		return "";
	}
} public class TableGenerator {

    private static String delimiter = " | ";
    private static List<Character> char7 = Arrays.asList('°', '~', '@');
    private static List<Character> char5 = Arrays.asList('"', '{', '}', '(', ')', '*', 'f', 'k', '<', '>');
    private static List<Character> char4 = Arrays.asList('I', 't', ' ', '[', ']', '');
    private static List<Character> char3 = Arrays.asList('l', '`', '³', '\'');
    private static List<Character> char2 = Arrays.asList(',', '.', '!', 'i', '´', ':', ';', '|');
    private static char char1 = '\u17f2';
    private static Pattern regex = Pattern.compile(char1+"(?:§r)?(\\s*)"
			+ "(?:§r§8)?"+char1+"(?:§r)?(\\s*)"
			+ "(?:§r§8)?"+char1+"(?:§r)?(\\s*)"
			+ "(?:§r§8)?"+char1);
    private static String colors = "[&§][0-9a-fA-Fk-oK-OrR]";
    private Alignment[] alignments;
    private List<Row> table = new ArrayList<>();
    private int columns;
   
    public TableGenerator(Alignment... alignments) {
    	if (alignments == null || alignments.length < 1)
    		throw new IllegalArgumentException("Must atleast provide 1 alignment.");
    	
        this.columns = alignments.length;
        this.alignments = alignments;
    }
   
    public List<String> generate(Receiver receiver, boolean ignoreColors,
            boolean coloredDistances) {
    	if (receiver == null) {
    		throw new IllegalArgumentException("Receiver must not be null.");
    	}
    	
        Integer[] columWidths = new Integer[columns];
       
        for (Row r : table) {
            for (int i = 0; i < columns; i++) {
                String text = r.texts.get(i);
                int length;
               
                if (ignoreColors)
                    length = getCustomLength(text.replaceAll(colors, ""),
                            receiver);
                else length = getCustomLength(text, receiver);
               
                if (columWidths[i] == null) {
                    columWidths[i] = length;
                }
               
                else if (length > columWidths[i]) {
                    columWidths[i] = length;
                }
            }
        }
       
        List<String> lines = new ArrayList<String>();
       
        for (Row r : table) {
            StringBuilder sb = new StringBuilder();
           
            if (r.empty) {
                lines.add("");
                continue;
            }
           
            for (int i = 0; i < columns; i++) {
                Alignment agn = alignments[i];
                String text = r.texts.get(i);
                int length;
               
                if (ignoreColors)
                    length = getCustomLength(text.replaceAll(colors, ""),
                            receiver);
                else length = getCustomLength(text,
                        receiver);
               
                int empty = columWidths[i] - length;
                int spacesAmount = empty;
                if (receiver == Receiver.CLIENT)
                    spacesAmount = (int) Math.floor(empty / 4d);
                int char1Amount = 0;
                if (receiver == Receiver.CLIENT)
                    char1Amount = empty - 4 * spacesAmount;
               
                String spaces = concatChars(' ', spacesAmount);
                String char1s = concatChars(char1, char1Amount);
               
                if (coloredDistances)
                    char1s = "§r§8" + char1s + "§r";
               
                if (agn == Alignment.LEFT) {
                    sb.append(text);
                    if (i < columns - 1)
                    	sb.append(char1s).append(spaces);
                }
                if (agn == Alignment.RIGHT) {
                    sb.append(spaces).append(char1s).append(text);
                }
                if (agn == Alignment.CENTER) {
                    int leftAmount = empty / 2;
                    int rightAmount = empty - leftAmount;
                   
                    int spacesLeftAmount = leftAmount;
                    int spacesRightAmount = rightAmount;
                    if (receiver == Receiver.CLIENT) {
                        spacesLeftAmount = (int) Math.floor(spacesLeftAmount / 4d);
                        spacesRightAmount = (int) Math.floor(spacesRightAmount / 4d);
                    }
                   
                    int char1LeftAmount = 0;
                    int char1RightAmount = 0;
                    if (receiver == Receiver.CLIENT) {
                        char1LeftAmount = leftAmount - 4 * spacesLeftAmount;
                        char1RightAmount = rightAmount - 4 * spacesRightAmount;
                    }
                   
                    String spacesLeft = concatChars(' ', spacesLeftAmount);
                    String spacesRight = concatChars(' ', spacesRightAmount);
                    String char1Left = concatChars(char1, char1LeftAmount);
                    String char1Right = concatChars(char1, char1RightAmount);
                   
                    if (coloredDistances) {
                        char1Left = "§r§8" + char1Left + "§r";
                        char1Right = "§r§8" + char1Right + "§r";
                    }

                    sb.append(spacesLeft).append(char1Left).append(text);
                    if (i < columns - 1)
                    	sb.append(char1Right).append(spacesRight);
                }
               
                if (i < columns - 1) sb.append("§r" + delimiter);
            }
           
            String line = sb.toString();
            if (receiver == Receiver.CLIENT) {
            	for (int i = 0; i < 2; i++) {
	            	Matcher matcher = regex.matcher(line);
	            	line = matcher.replaceAll("$1$2$3 ").replace("§r§8§r", "§r")
	            			.replaceAll("§r(\\s*)§r", "§r$1");
            	}
            }
            lines.add(line);
        }
        return lines;
    }
   
    protected static int getCustomLength(String text, Receiver receiver) {
    	if (text == null) {
    		throw new IllegalArgumentException("Text must not be null.");
    	}
    	if (receiver == null) {
    		throw new IllegalArgumentException("Receiver must not be null.");
    	}
        if (receiver == Receiver.CONSOLE) return text.length();

        int length = 0;
        for (char c : text.toCharArray())
            length += getCustomCharLength(c);

        return length;
    }
   
    protected static int getCustomCharLength(char c) {
        if (char1 == c) return 1;
        if (char2.contains(c)) return 2;
        if (char3.contains(c)) return 3;
        if (char4.contains(c)) return 4;
        if (char5.contains(c)) return 5;
        if (char7.contains(c)) return 7;
       
        return 6;
    }
   
    protected String concatChars(char c, int length) {
        String s = "";
        if (length < 1) return s;

        for (int i = 0; i < length; i++)
            s += Character.toString(c);
        return s;
    }
   
    public void addRow(String... texts) {
    	if (texts == null) {
    		throw new IllegalArgumentException("Texts must not be null.");
    	}
        if (texts != null && texts.length > columns) {
            throw new IllegalArgumentException("Too big for the table.");
        }
       
        Row r = new Row(texts);
       
        table.add(r);
    }
   
    private class Row {
       
        public List<String> texts = new ArrayList<>();
        public boolean empty = true;
       
        public Row(String... texts) {
            if (texts == null) {
                for (int i = 0; i < columns; i++)
                    this.texts.add("");
                return;
            }
           
            for (String text : texts) {
                if (text != null && !text.isEmpty()) empty = false;
               
                this.texts.add(text);
            }
           
            for (int i = 0; i < columns; i++) {
                if (i >= texts.length) this.texts.add("");
            }
        }
    }
   
    public enum Receiver {
       
        CONSOLE, CLIENT
    }
   
    public enum Alignment {
       
        CENTER, LEFT, RIGHT
    }
} public enum Alignment {
       
    CENTER, LEFT, RIGHT
}
 public enum Receiver {
       
    CONSOLE, CLIENT
}

import java.util.ArrayList;
import java.util.List;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import com.gmail.justbru00.epic.rename.enums.v3.EpicRenameCommands;
import com.gmail.justbru00.epic.rename.main.v3.Main;
import com.gmail.justbru00.epic.rename.utils.v3.Blacklists;
import com.gmail.justbru00.epic.rename.utils.v3.Debug;
import com.gmail.justbru00.epic.rename.utils.v3.MaterialPermManager;
import com.gmail.justbru00.epic.rename.utils.v3.Messager;
import com.gmail.justbru00.epic.rename.utils.v3.RenameUtil;
import com.gmail.justbru00.epic.rename.utils.v3.WorldChecker;
import com.gmail.justbru00.epic.rename.utils.v3.align.FormatItem;
import com.gmail.justbru00.epic.rename.utils.v3.align.TableGenerator;
import com.gmail.justbru00.epic.rename.utils.v3.align.TableGenerator.Alignment;
import com.gmail.justbru00.epic.rename.utils.v3.align.TableGenerator.Receiver;

package com.gmail.justbru00.epic.rename.commands.v3;




/**
 * 
 * @author Justin "JustBru00" Brubaker
 * This is licensed under the MPL Version 2.0. See license info in LICENSE.txt
 * Created for Issue #86
 *
 */
public class Align implements CommandExecutor {
	
	private static final int ALIGN_LEFT = 1;
	private static final int ALIGN_CENTER = 2;
	private static final int ALIGN_RIGHT = 3;
	
	public static void main(String[] args) {
		// Test the align stuff
		ArrayList<String> text = new ArrayList<String>();
		text.add("             Swords");
		text.add("");
		text.add("Click to see all custom");
		text.add("enchantments for swords!");
		for(String s : alignStrings(text, ALIGN_RIGHT)) {
			System.out.println(s);
		}
	}

	@SuppressWarnings("deprecation")
	@Override
	public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
		
		if (command.getName().equalsIgnoreCase("align")) {
			if (sender.hasPermission("epicrename.align")) {
				if (sender instanceof Player) {
					Player player = (Player) sender;
					
					if (WorldChecker.checkWorld(player)) {
						ItemStack inHand = RenameUtil.getInHand(player);
						Material m = inHand.getType();
						
						// Check Material Permissions
						if (!MaterialPermManager.checkPerms(EpicRenameCommands.ALIGN, inHand, player)) {
							Messager.msgSenderWithConfigMsg("align.no_permission_for_material", sender);
							return true;
						}
						
						// Check Material Blacklist | #76
						if (!Blacklists.checkMaterialBlacklist(m, player)) {
							Messager.msgSenderWithConfigMsg("align.blacklisted_material_found", sender);
							return true;
						}
						
						// Check Existing Name Blacklist | #81
						if (!Blacklists.checkExistingName(player)) {
							Messager.msgSenderWithConfigMsg("align.blacklisted_existing_name_found", player);
							return true;
						}
						
						// Check Existing Lore Blacklist | #81
						if (!Blacklists.checkExistingLore(player)) {
							Messager.msgSenderWithConfigMsg("align.blacklisted_existing_lore_found", player);
							return true;
						}
						
						// Make sure item is not AIR or null
						if (m == Material.AIR || m == null) {
							Messager.msgSenderWithConfigMsg("align.cannot_edit_air", sender);
							return true;
						}
						
						// Example Command Usage: /align name <left || center || right>
						// /align lore <left || center || right> <1,2,3,5>
						// /align lore <left || center || right> <all>
						// /align both <left || center || right> 						
						if (args.length == 0) {
							Messager.msgSenderWithConfigMsg("align.not_enough_args", sender);
							return true;
						}						
						
						if (args.length >= 2) {				
							ArrayList<String> textToAlign = new ArrayList<String>();
							
							if (inHand.getItemMeta().hasDisplayName()) {
								textToAlign.add((inHand.getItemMeta().getDisplayName()).trim().replace("៲", ""));
							} else {								
								textToAlign.add(Messager.color("&r" + new FormatItem(inHand).getName().trim()).replace("៲", ""));								
							}
								
							if (inHand.getItemMeta().hasLore()) {
								for (String s : inHand.getItemMeta().getLore()) {
									textToAlign.add(s.trim().replace("៲", ""));
								}
							} else {
								// Well nothing /shrug
							}
							
							
							
							if (args[0].equalsIgnoreCase("name")) {
								if (args[1].equalsIgnoreCase("left")) {
									ArrayList<String> aligned = alignStringsClient(textToAlign, ALIGN_LEFT);
									
									ItemMeta meta = inHand.getItemMeta();
									meta.setDisplayName(aligned.get(0));
									
									// Only Name
									/*if (aligned.size() > 1) {
										// Lore in this thing
										List<String> lore = new ArrayList<String>();
										for (int i = 1; i < aligned.size(); i++) {
											lore.add(aligned.get(i));
										}
										meta.setLore(lore);
									} */
									
									inHand.setItemMeta(meta);
									if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
										player.getInventory().setItemInMainHand(inHand);
									} else { // Use older method.
										player.setItemInHand(inHand);
									}
									Messager.msgSenderWithConfigMsg("align.name_aligned_left_success", sender);
									return true;
								} else if (args[1].equalsIgnoreCase("center")) {
									ArrayList<String> aligned = alignStringsClient(textToAlign, ALIGN_CENTER);
									
									ItemMeta meta = inHand.getItemMeta();
									meta.setDisplayName(aligned.get(0));
									
									// Only Name
									/*if (aligned.size() > 1) {
										// Lore in this thing
										List<String> lore = new ArrayList<String>();
										for (int i = 1; i < aligned.size(); i++) {
											lore.add(aligned.get(i));
										}
										meta.setLore(lore);
									}*/
									
									inHand.setItemMeta(meta);
									if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
										player.getInventory().setItemInMainHand(inHand);
									} else { // Use older method.
										player.setItemInHand(inHand);
									}
									Messager.msgSenderWithConfigMsg("align.name_aligned_center_success", sender);
									return true;
								} else if (args[1].equalsIgnoreCase("right")) {
									ArrayList<String> aligned = alignStringsClient(textToAlign, ALIGN_RIGHT);
									
									ItemMeta meta = inHand.getItemMeta();
									meta.setDisplayName(aligned.get(0));
									
									// Only Name
									/*if (aligned.size() > 1) {
										// Lore in this thing
										List<String> lore = new ArrayList<String>();
										for (int i = 1; i < aligned.size(); i++) {
											lore.add(aligned.get(i));
										}
										meta.setLore(lore);
									}*/
									
									inHand.setItemMeta(meta);
									if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
										player.getInventory().setItemInMainHand(inHand);
									} else { // Use older method.
										player.setItemInHand(inHand);
									}
									Messager.msgSenderWithConfigMsg("align.name_aligned_right_success", sender);
									return true;									
								} else {
									Messager.msgSenderWithConfigMsg("align.incorrect_name_args", sender);
									return true;
								}
							} else if (args[0].equalsIgnoreCase("lore")) {
								if (args[1].equalsIgnoreCase("left")) {
									ArrayList<String> aligned = alignStringsClient(textToAlign, ALIGN_LEFT);
									
									ItemMeta meta = inHand.getItemMeta();
									/*meta.setDisplayName(aligned.get(0));*/								
									
									if (aligned.size() > 1) {
										// Lore in this thing
										List<String> lore = new ArrayList<String>();
										for (int i = 1; i < aligned.size(); i++) {
											lore.add(aligned.get(i));
										}
										meta.setLore(lore);
									}
									
									inHand.setItemMeta(meta);
									if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
										player.getInventory().setItemInMainHand(inHand);
									} else { // Use older method.
										player.setItemInHand(inHand);
									}
									Messager.msgSenderWithConfigMsg("align.lore_aligned_left_success", sender);
									return true;
								} else if (args[1].equalsIgnoreCase("center")) {
									ArrayList<String> aligned = alignStringsClient(textToAlign, ALIGN_CENTER);
									
									ItemMeta meta = inHand.getItemMeta();
									/*meta.setDisplayName(aligned.get(0));*/								
									
									if (aligned.size() > 1) {
										// Lore in this thing
										List<String> lore = new ArrayList<String>();
										for (int i = 1; i < aligned.size(); i++) {
											lore.add(aligned.get(i));
										}
										meta.setLore(lore);
									}
									
									inHand.setItemMeta(meta);
									if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
										player.getInventory().setItemInMainHand(inHand);
									} else { // Use older method.
										player.setItemInHand(inHand);
									}
									Messager.msgSenderWithConfigMsg("align.lore_aligned_center_success", sender);
									return true;
								} else if (args[1].equalsIgnoreCase("right")) {
									ArrayList<String> aligned = alignStringsClient(textToAlign, ALIGN_RIGHT);
									
									ItemMeta meta = inHand.getItemMeta();
									/*meta.setDisplayName(aligned.get(0));*/								
									
									if (aligned.size() > 1) {
										// Lore in this thing
										List<String> lore = new ArrayList<String>();
										for (int i = 1; i < aligned.size(); i++) {
											lore.add(aligned.get(i));
										}
										meta.setLore(lore);
									}
									
									inHand.setItemMeta(meta);
									if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
										player.getInventory().setItemInMainHand(inHand);
									} else { // Use older method.
										player.setItemInHand(inHand);
									}
									Messager.msgSenderWithConfigMsg("align.lore_aligned_right_success", sender);
									return true;
								} else {
									Messager.msgSenderWithConfigMsg("align.incorrect_lore_args", sender);
									return true;
								}
							} else if (args[0].equalsIgnoreCase("both")) {
								if (args[1].equalsIgnoreCase("left")) {
									ArrayList<String> aligned = alignStringsClient(textToAlign, ALIGN_LEFT);
									
									ItemMeta meta = inHand.getItemMeta();
									meta.setDisplayName(aligned.get(0));							
									
									if (aligned.size() > 1) {
										// Lore in this thing
										List<String> lore = new ArrayList<String>();
										for (int i = 1; i < aligned.size(); i++) {
											lore.add(aligned.get(i));
										}
										meta.setLore(lore);
									}
									
									inHand.setItemMeta(meta);
									if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
										player.getInventory().setItemInMainHand(inHand);
									} else { // Use older method.
										player.setItemInHand(inHand);
									}
									Messager.msgSenderWithConfigMsg("align.both_aligned_left_success", sender);
									return true;
								} else if (args[1].equalsIgnoreCase("center")) {
									ArrayList<String> aligned = alignStringsClient(textToAlign, ALIGN_CENTER);
									
									ItemMeta meta = inHand.getItemMeta();
									meta.setDisplayName(aligned.get(0));							
									
									if (aligned.size() > 1) {
										// Lore in this thing
										List<String> lore = new ArrayList<String>();
										for (int i = 1; i < aligned.size(); i++) {
											lore.add(aligned.get(i));
										}
										meta.setLore(lore);
									}
									
									inHand.setItemMeta(meta);
									if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
										player.getInventory().setItemInMainHand(inHand);
									} else { // Use older method.
										player.setItemInHand(inHand);
									}
									Messager.msgSenderWithConfigMsg("align.both_aligned_center_success", sender);
									return true;
								} else if (args[1].equalsIgnoreCase("right")) {
									ArrayList<String> aligned = alignStringsClient(textToAlign, ALIGN_RIGHT);
									
									ItemMeta meta = inHand.getItemMeta();
									meta.setDisplayName(aligned.get(0));							
									
									if (aligned.size() > 1) {
										// Lore in this thing
										List<String> lore = new ArrayList<String>();
										for (int i = 1; i < aligned.size(); i++) {
											lore.add(aligned.get(i));
										}
										meta.setLore(lore);
									}
									
									inHand.setItemMeta(meta);
									if (Main.USE_NEW_GET_HAND) { // Use 1.9+ method
										player.getInventory().setItemInMainHand(inHand);
									} else { // Use older method.
										player.setItemInHand(inHand);
									}
									Messager.msgSenderWithConfigMsg("align.both_aligned_right_success", sender);
									return true;
								} else {
									Messager.msgSenderWithConfigMsg("align.incorrect_both_args", sender);
									return true;
								}
							} else {
								Messager.msgSenderWithConfigMsg("align.incorrect_args", sender);
								return true;
							}
						} else {
							Messager.msgSenderWithConfigMsg("align.not_enough_args", sender);
							return true;
						}
					} else {
						Messager.msgSenderWithConfigMsg("align.disabled_world", sender);
						return true;
					}
				} else {
					Messager.msgSenderWithConfigMsg("align.wrong_sender", sender);
					return true;
				}
			} else {
				Messager.msgSenderWithConfigMsg("align.no_permission", sender);
				return true;
			}
		}
		
		return false;
	
	}
	
	/**
	 * Attempts to align text for the minecraft client.
	 * @param itemTextList
	 * @param alignmentScheme
	 * @return
	 */
	public static ArrayList<String> alignStringsClient(ArrayList<String> itemTextList, int alignmentScheme) {
		ArrayList<String> toReturn = new ArrayList<String>();
		
		TableGenerator tg = null;
		
		if (alignmentScheme == ALIGN_LEFT) {

			tg = new TableGenerator(Alignment.LEFT);
