public class SurvivalGames extends JavaPlugin {
	
	public static SurvivalGames instance;
	public static FileConfiguration messages, database, signs, reset, chestloot, scoreboard, kits;
	
	public static ArenaManager arenaManager;
	public static GameManager gameManager;
	public static ChestManager chestManager;
	public static UserManager userManger;
	public static SignManager signManager;
	public static ScoreBoardManager scoreBoardManager;
	
	public static Economy econ;
	
	public static String version = "SurvivalGames - Version ";
	
	private static PluginManager pm = Bukkit.getPluginManager();
	
	public void onDisable() {
		if(gameManager != null) {
			for(Game game : gameManager.getGames()) {
				game.kickall();
			}
		}
		DatabaseManager.close();
	}
	
	public void onEnable() {
		if(!Bukkit.getVersion().toLowerCase().contains("spigot")) {
			System.err.println("[SurvivalGames] ###################################################################");
			System.err.println("[SurvivalGames] #########       THIS PLUGIN REQUIRES SPIGOT TO RUN       ##########");
			System.err.println("[SurvivalGames] #########      Please use it instead of craftbukkit!     ##########");
			System.err.println("[SurvivalGames] ######### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##########");
			System.err.println("[SurvivalGames] #########       Download: http://www.spigotmc.org/       ##########");
			System.err.println("[SurvivalGames] ###################################################################");
			Bukkit.getPluginManager().disablePlugin(this);
			return;
		}
		
		if(!Bukkit.getPluginManager().isPluginEnabled("WorldEdit")) {
			System.err.println("[SurvivalGames] ##########################################################");
			System.err.println("[SurvivalGames] ######### NO WORLDEDIT FOUND! DISABLE PLUGIN... ##########");
			System.err.println("[SurvivalGames] ##########################################################");
			Bukkit.getPluginManager().disablePlugin(this);
			return;
		}
		
		instance = this;
		version += getDescription().getVersion();
		
		new ConfigLoader().load();
		DatabaseManager.open();
		DatabaseManager.load();
		
		startUpdateChecker();
		
		PermissionHandler.reinitializeDatabase();
		Game.reinitializeDatabase();
		MessageHandler.reload();
		
		if(setupEconomy())
			System.out.println("[SurvivalGames] Vault found!");
		
		// TEMPORARY
		Util.checkForOutdatedArenaSaveFiles();

		chestManager = new ChestManager();
		scoreBoardManager = new ScoreBoardManager();
		arenaManager = new ArenaManager();
		gameManager = new GameManager();
		userManger = new UserManager();
		signManager = new SignManager();
		
		getCommand("sg").setExecutor(new CommandSG());
		
		pm.registerEvents(new SelectionListener(), this);
		pm.registerEvents(new PlayerListener(), this);
		pm.registerEvents(new ChestListener(), this);
		pm.registerEvents(new SignListener(), this);
		pm.registerEvents(new ResetListener(), this);
		pm.registerEvents(new UpdateListener(), this);
		pm.registerEvents(new SpectatorListener(), this);
		pm.registerEvents(new ChatListener(), this);
		
		try {
			new Metrics(this).start();
		} catch (IOException e) {
			System.err.println("[SurvivalGames] Cannot load metrics: " + e.getMessage());
		}
		
		if(getWorldEdit() != null) {
			System.out.println("[SurvivalGames] Plugin enabled. WorldEdit found!");
		} else {
			System.out.println("[SurvivalGames] Plugin enabled.");
		}
		
		signManager.updateSigns();
	}
	
	// UPDATE CHECKING

	public void startUpdateChecker() {
		Bukkit.getScheduler().runTaskTimer(this, new Runnable() {
			public void run() {
				new UpdateCheck(SurvivalGames.instance, 61788);
			}
		}, 0L, 216000);
	}
	
	// VAULT
	
	private boolean setupEconomy() {
		if(Bukkit.getPluginManager().isPluginEnabled("Vault")) {
			RegisteredServiceProvider<Economy> economyProvider = getServer().getServicesManager().getRegistration(Economy.class);
	        if (economyProvider != null) {
	            econ = economyProvider.getProvider();
	        }
		}

        return (econ != null);
	}
	
	// FILECONFIGURATION SAVE
	
	public static void saveMessages() {
		try {
			messages.save("plugins/SurvivalGames/messages.yml");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void saveDataBase() {
		try {
			database.save("plugins/SurvivalGames/database.yml");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void saveSigns() {
		try {
			signs.save("plugins/SurvivalGames/signs.yml");
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void saveReset() {
		try {
			reset.save("plugins/SurvivalGames/reset.yml");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void saveChests() {
		try {
			chestloot.save("plugins/SurvivalGames/chestloot.yml");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void saveScoreboard() {
		try {
			scoreboard.save("plugins/SurvivalGames/scoreboard.yml");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void saveKits() {
		try {
			kits.save("plugins/SurvivalGames/kits.yml");
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	// WORLDEDIT
	
	public static WorldEditPlugin getWorldEdit() {
		if(!pm.isPluginEnabled("WorldEdit")) {
			return null;
		} else {
			return (WorldEditPlugin) pm.getPlugin("WorldEdit");
		}
	}
	
	// API
	
	public static GameManager getGameManager() {
		return gameManager;
	}
	
	public static ArenaManager getArenaManager() {
		return arenaManager;
	}
	
	public static ChestManager getChestManager() {
		return chestManager;
	}
	
	public static UserManager getUserManager() {
		return userManger;
	}
	
	public static SignManager getSignManager() {
		return signManager;
	}
	
	public static ScoreBoardManager getScoreboardManager() {
		return scoreBoardManager;
	}
	
	public static SurvivalGames getInstance() {
		return instance;
	}

}
 public class Util {
	
	// ITEMSTACK
	public static boolean debug = false;
	private static Random random = new Random();
	
	@SuppressWarnings("deprecation")
	public static ItemStack parseItemStack(String s) {
		try {
			String[] gSplit = s.split(" ");
			ItemStack is = null;
			
			// ITEM ID / MATERIAL / SUBID
			String[] idsSplit = gSplit[0].split(":");
			try {
				is = new ItemStack(Integer.parseInt(idsSplit[0]));
			} catch(NumberFormatException e) {
				is = new ItemStack(Material.valueOf(idsSplit[0]));
			}
			
			if(idsSplit.length > 1)
				is.setDurability(Short.parseShort(idsSplit[1]));
			
			if(gSplit.length > 1) {
				int metaStart = 2;
				
				try {
					is.setAmount(Integer.parseInt(gSplit[1]));
				} catch(NumberFormatException e) {
					metaStart = 1;
				}
				ItemMeta im = is.getItemMeta();
				for(int meta = metaStart; meta < gSplit.length; meta++) {
					String rawKey = gSplit[meta];
					String[] split = rawKey.split(":");
					String key = split[0];
					
					if(key.equalsIgnoreCase("name")) {
						im.setDisplayName(ChatColor.translateAlternateColorCodes('&', split[1]).replace("_", " "));
					} else if(key.equalsIgnoreCase("lore")) {
						List<String> lore = new ArrayList<>();
						for(String line : split[1].split("//")) {
							lore.add(ChatColor.translateAlternateColorCodes('&', line).replace("_", " "));
						}
						im.setLore(lore);
					} else if(key.equalsIgnoreCase("color") && im instanceof LeatherArmorMeta) {
						LeatherArmorMeta lam = (LeatherArmorMeta) im;
						String[] csplit = split[1].split(",");
						Color color = Color.fromBGR(Integer.parseInt(csplit[0]), Integer.parseInt(csplit[1]), Integer.parseInt(csplit[2]));
						lam.setColor(color);
					} else if(key.equalsIgnoreCase("effect") && im instanceof PotionMeta) {
						PotionMeta pm = (PotionMeta) im;
						String[] psplit = split[1].split(",");
						pm.addCustomEffect(new PotionEffect(PotionEffectType.getByName(psplit[0]), Integer.parseInt(psplit[1]) * 20, Integer.parseInt(psplit[2])), true);
					} else if(key.equalsIgnoreCase("player") && im instanceof SkullMeta) {
						((SkullMeta)im).setOwner(split[1]);
					} else if(key.equalsIgnoreCase("enchant")) {
						String[] esplit = split[1].split(",");
						im.addEnchant(getEnchantment(esplit[0]), Integer.parseInt(esplit[1]), true);
					}
					
				}
				is.setItemMeta(im);
			}
			
			return is;
		} catch(Exception e) {
			System.err.println("[SurvivalGames] Cannot parse ItemStack: " + s + " - Mabye this is the reason: " + e.toString());
			return null;
		}
	}
	
	public static Vector calculateVector(Location from, Location to) {
        Location a = from, b = to;
        
        double dX = a.getX() - b.getX();
        double dY = a.getY() - b.getY();
        double dZ = a.getZ() - b.getZ();
        double yaw = Math.atan2(dZ, dX);
        double pitch = Math.atan2(Math.sqrt(dZ * dZ + dX * dX), dY) + Math.PI;
        double x = Math.sin(pitch) * Math.cos(yaw);
        double y = Math.sin(pitch) * Math.sin(yaw);
        double z = Math.cos(pitch);
        
        Vector vector = new Vector(x, z, y);
        
        return vector;
    }
	
	public static void shootRandomFirework(Location loc, int height) {
		Firework f = (Firework) loc.getWorld().spawnEntity(loc, EntityType.FIREWORK);
		FireworkMeta fm = f.getFireworkMeta();
		fm.setPower(height);
		int effectAmount = random.nextInt(3) + 1;
		for(int i = 0; i < effectAmount; i++) {
			Builder b = FireworkEffect.builder();
			int colorAmount = random.nextInt(3) + 1;
			for(int ii = 0; ii < colorAmount; ii++) {
				b.withColor(Color.fromBGR(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
			}
			b.with(Type.values()[random.nextInt(Type.values().length)]);
			b.flicker(random.nextInt(2) == 0 ? false : true);
			b.trail(random.nextInt(2) == 0 ? false : true);
			fm.addEffect(b.build());
		}
		f.setFireworkMeta(fm);
	}
	
	// EXP PERCENT
	
	public static float getExpPercent(float value, float max) {
		if(value == 0)
			return 0;
		return value / max;
	}
	
	// ENCHANTMENT
	public static Enchantment getEnchantment(String enc) {
		enc = enc.toUpperCase();
		Enchantment en = Enchantment.getByName(enc);
		
		if(en == null) {
			switch (enc) {
			case "PROTECTION":
				en = Enchantment.PROTECTION_ENVIRONMENTAL;
				break;
			case "FIRE_PROTECTION":
				en = Enchantment.PROTECTION_FIRE;
				break;
			case "FEATHER_FALLING":
				en = Enchantment.PROTECTION_FALL;
				break;
			case "BLAST_PROTECTION":
				en = Enchantment.PROTECTION_EXPLOSIONS;
				break;
			case "PROJECTILE_PROTCETION":
				en = Enchantment.PROTECTION_PROJECTILE;
				break;
			case "RESPIRATION":
				en = Enchantment.OXYGEN;
				break;
			case "AQUA_AFFINITY":
				en = Enchantment.WATER_WORKER;
				break;
			case "SHARPNESS":
				en = Enchantment.DAMAGE_ALL;
				break;
			case "SMITE":
				en = Enchantment.DAMAGE_UNDEAD;
				break;
			case "BANE_OF_ARTHROPODS":
				en = Enchantment.DAMAGE_ARTHROPODS;
				break;
			case "LOOTING":
				en = Enchantment.LOOT_BONUS_MOBS;
				break;
			case "EFFICIENCY":
				en = Enchantment.DIG_SPEED;
				break;
			case "UNBREAKING":
				en = Enchantment.DURABILITY;
				break;
			case "FORTUNE":
				en = Enchantment.LOOT_BONUS_BLOCKS;
				break;
			case "POWER":
				en = Enchantment.ARROW_DAMAGE;
				break;
			case "PUNCH":
				en = Enchantment.ARROW_KNOCKBACK;
				break;
			case "FLAME":
				en = Enchantment.ARROW_FIRE;
				break;
			case "INFINITY":
				en = Enchantment.ARROW_INFINITE;
				break;
			case "LUCK_OF_THE_SEA":
				en = Enchantment.LUCK;
				break;
			}
		}
		
		return en;
	}
	// TIME
	
	public static String getFormatedTime(int seconds) {
		int minutes = seconds / 60;
		int hours = minutes / 60;
		int days = hours / 24;
		
		seconds -= minutes * 60;
		minutes -= hours * 60;
		hours -= days * 24;
		
		String s = "";
		if(days > 0)
			s += days + "d";
		if(hours > 0)
			s += hours + "h";
		if(minutes > 0)
			s += minutes + "m";
		if(seconds > 0) 
			s += seconds + "s";
	
		return s;
	}
	
	// LOCATION
	
	public static Location parseLocation(String s) {
		String[] split = s.split(",");
		Location loc = null;
		
		
		try {
			World world = Bukkit.getWorld(split[0]);
			if(split.length == 6) {
				double x = Double.parseDouble(split[1]);
				double y = Double.parseDouble(split[2]);
				double z = Double.parseDouble(split[3]);
				
				float yaw = Float.parseFloat(split[4]);
				float pitch = Float.parseFloat(split[5]);
				loc = new Location(world, x, y, z, yaw, pitch);
			} else if(split.length == 4) {
				int x = Integer.parseInt(split[1]);
				int y = Integer.parseInt(split[2]);
				int z = Integer.parseInt(split[3]);
				
				loc = new Location(world, x, y, z);
			}
		} catch(NumberFormatException | ArrayIndexOutOfBoundsException e) {
			System.err.println("[SurvivalGames] Cannot parse location from string: " + s);
		}
		
		return loc;
	}
	
	public static String serializeLocation(Location l, boolean exact) {
		if(l != null) {
			String key = l.getWorld().getName() + ",";
			if(exact) {
				key += l.getX() + "," + l.getY() + "," + l.getZ() + "," + l.getYaw() + "," + l.getPitch();
			} else {
				key += l.getBlockX() + "," + l.getBlockY() + "," + l.getBlockZ();
			}
			
			return key;
		}
		return null;
	}
	
	public static void debug(Object object) {
		if(debug) {
			System.out.println("[SurvivalGames] [Debug] " + object.toString());
			for(Player p : Bukkit.getOnlinePlayers()) {
				if(p.isOp()) 
					p.sendMessage("§7[Debug] " + object.toString());
			}
		}
	}
	
	// TEMPORARY UPDATE METHODS
	
	public static void checkForOutdatedArenaSaveFiles() {
		File f = new File("plugins/SurvivalGames/reset/");
		List<String> outdated = new ArrayList<>();
		if(f.exists()) {
			for(String key : f.list()) {
				if(!key.endsWith(".map"))
					continue;
				File file = new File("plugins/SurvivalGames/reset/" + key);
				SchematicFormat sf = SchematicFormat.getFormat(file);
				if(sf == null) {
					outdated.add(key);
					
				}
			}
		}
		String s = null;
		if(!outdated.isEmpty()) {
			s = MessageHandler.getMessage("prefix") + "§cThe format of " + outdated.size() + " map saves is outdated§7: §e";
			for(int i = 0; i < outdated.size(); i++) {
				s+= outdated.get(i);
				if(i != outdated.size() - 1) {
					s+= "§7, §e";
				} else {
					s+= " §c! ";
				}
				

			}
			s+= "Select all the arenas with §l/sg arena select §cand type §c§l/sg arena save§c! In the old format, the arenas will not reset!";
		}
		UpdateListener.setOutdatedMaps(s);
	}

}
 public class Arena implements Cloneable {

	private Location min, max;
	private List<Location> spawns;
	private int graceperiod;
	private String name;
	private String game;
	private List<Integer> allowedBlocks;
	private double moneyKill, moneyWin;
	
	private Material chesttype;
	private int chestdata;
	
	private boolean deathmatch, refill;
	private List<Location> deathmatchSpawns;
	
	private int autodeathmatch, playerdeathmatch;
	
	private int votes = 0;
	
	public Arena(Location min, Location max, List<Location> spawns, Material chesttype, int chestdata, int graceperiod, String name, String game, boolean deathmatch, List<Location> deathmatchspawns, List<Integer> allowedBlocks, int autodeathmatch, int playerdeathmatch, double moneyKill, double moneyWin, boolean chestrefill, Location domeMiddle, int domeRadius) {
		this.min = min;
		this.max = max;
		this.spawns = spawns;
		this.graceperiod = graceperiod;
		this.name = name;
		this.game = game;
		this.allowedBlocks = allowedBlocks;
		this.chesttype = chesttype;
		this.chestdata = chestdata;
		this.moneyKill = moneyKill;
		this.moneyWin = moneyWin;
		this.refill = chestrefill;
		
		this.deathmatch = deathmatch;
		this.deathmatchSpawns = deathmatchspawns;
		
		if(deathmatchSpawns.isEmpty())
			this.deathmatch = false;
		
		
		this.autodeathmatch = autodeathmatch;
		this.playerdeathmatch = playerdeathmatch;
		this.domeRadius = domeRadius;
		if(domeRadius > 0) {
			this.domeMiddle = domeMiddle;
			this.domeMiddle.setY(0);
		}
	}
	
	public int getAutomaticlyDeathmatchTime() {
		return autodeathmatch;
	}
	
	public int getPlayerDeathmatchAmount() {
		return playerdeathmatch;
	}
	
	public boolean isDeathmatchEnabled() {
		return deathmatch;
	}
	
	public List<Location> getDeathmatchSpawns() {
		return deathmatchSpawns;
	}
	
	public void setVotes(int votes) {
		this.votes = votes;
	}
	
	public int getVotes() {
		return votes;
	}
	
	public Material getChestType() {
		return chesttype;
	}
	
	public int getChestData() {
		return chestdata;
	}
	
	public List<Integer> getAllowedMaterials() {
		return allowedBlocks;
	}
	
	public double getMoneyOnKill() {
		return moneyKill;
	}
	
	public double getMoneyOnWin() {
		return moneyWin;
	}
	
	public boolean chestRefill() {
		return refill;
	}
	
	public boolean containsBlock(Location loc) {
		double x = loc.getX();
		double y = loc.getY();
		double z = loc.getZ();
		return (loc.getWorld().equals(min.getWorld()) && x >= min.getX() && x <= max.getX() && y >= min.getY() && y <= max.getY() && z >= min.getZ() && z <= max.getZ());
	}
	
	public Location getMinimumLocation() {
		return min;
	}
	
	public Location getMaximumLocation() {
		return max;
	}
	
	public List<Location> getSpawns() {
		return spawns;
	}
	
	public int getGracePeriod() {
		return graceperiod;
	}
	
	public String getName() {
		return name;
	}
	
	public String getGame() {
		return game;
	}
	
	// ADJUSTABLE DOME
	private Location domeMiddle;
	private int domeRadius;
	
	public Location getDomeMiddle() {
		return domeMiddle;
	}
	
	public int getDomeRadius() {
		return domeRadius;
	}
	
	public double domeDistance(Location loc) {
		Location bloc = loc.clone();
		bloc.setY(0);
		return domeMiddle.distance(bloc);
	}
	
	public boolean isDomeEnabled() {
		return domeMiddle != null;
	}
	
}
 public class ArenaArgument {
	
	private CommandSender sender;
	private String[] args;
	
	public ArenaArgument(CommandSender sender, String[] args) {
		this.sender = sender;
		this.args = args;
	}
	
	@SuppressWarnings("deprecation")
	public boolean execute() {
		
		if(!(sender instanceof Player)) {
			sender.sendMessage("§cThe arena argument can only execute as a Player!");
			return true;
		}
		
		Player p = (Player)sender;
		
		if(!PermissionHandler.hasPermission(p, Permission.ARENA)) {
			p.sendMessage(MessageHandler.getMessage("no-permission"));
			return true;
		}
		
		if(args.length == 1) {
			Helper.showArenaHelpsite(p);
			return true;
		} else {
			if(args[1].equalsIgnoreCase("delete")) {
				SurvivalGames.arenaManager.delete(p);
				return true;
			} else if(args[1].equalsIgnoreCase("save")) {
				SurvivalGames.arenaManager.save(p);
				return true;
			} else if(args[1].equalsIgnoreCase("create")) {
				
				if(args.length < 4) {
					p.sendMessage(MessageHandler.getMessage("cmd-error").replace("%0%", "You must specify a arenaname and a gamename: /sg arena create <LOBBYNAME> <ARENA NAME>"));
					return true;
				}
				
				String arenaname = getArgs(3);
				String gamename = args[2];
				
				SurvivalGames.arenaManager.createArena(p, arenaname, gamename);
				return true;
				
			} else if(args[1].equalsIgnoreCase("select")) {
				
				if(args.length < 4) {
					p.sendMessage(MessageHandler.getMessage("cmd-error").replace("%0%", "You must specify a arenaname and a gamename: /sg arena select <LOBBYNAME> <ARENA NAME>"));
					return true;
				}
				
				String arenaname = getArgs(3);
				String gamename = args[2];
				
				SurvivalGames.arenaManager.selectArena(p, arenaname, gamename);
				return true;
				
			} else if(args[1].equalsIgnoreCase("check")) {
				SurvivalGames.arenaManager.check(p);
				return true;
				
			} else if(args[1].equalsIgnoreCase("addspawn")) {
				SurvivalGames.arenaManager.addSpawn(p, "Spawns");
				return true;
				
			} else if(args[1].equalsIgnoreCase("removespawn")) {
				if(args.length == 2) {
					p.sendMessage(MessageHandler.getMessage("cmd-error").replace("%0%", "You must specify a number: /sg arena removespawn <ID>"));
					return true;
				}
				
				int id = 0;
				
				try {
					id = Integer.parseInt(args[2]);
				} catch(NumberFormatException e) {
					p.sendMessage(MessageHandler.getMessage("cmd-error").replace("%0%", args[2] + " isn't a valid number!"));
					return true;
				}
				
				SurvivalGames.arenaManager.removeSpawn(p, id, "Spawns");
				return true;
				
			} else if(args[1].equalsIgnoreCase("deathmatch")) {
				
				if(args.length == 2) {
					SurvivalGames.arenaManager.changeDeathmatch(p);
					return true;
				}
				
				if(args[2].equalsIgnoreCase("add")) {
					SurvivalGames.arenaManager.addSpawn(p, "Deathmatch-Spawns");
					return true;
					
				} else if(args[2].equalsIgnoreCase("remove")) {
					
					if(args.length == 3) {
						p.sendMessage(MessageHandler.getMessage("cmd-error").replace("%0%", "You must specify a number: /sg arena deathmatch remove <ID>"));
						return true;
					}
					
					int id = 0;
					
					try {
						id = Integer.parseInt(args[3]);
					} catch(NumberFormatException e) {
						p.sendMessage(MessageHandler.getMessage("cmd-error").replace("%0%", args[3] + " isn't a valid number!"));
						return true;
					}
					
					SurvivalGames.arenaManager.removeSpawn(p, id, "Deathmatch-Spawns");
					return true;
					
				} else if(args[2].equalsIgnoreCase("domemiddle")) {
					SurvivalGames.arenaManager.setDeathmatchDomeMiddle(p, args.length > 3);
					return true;
				} else if(args[2].equalsIgnoreCase("domeradius")) {
					if(args.length == 3) {
						SurvivalGames.arenaManager.setDeathmatchDomeRadius(p, 0, true);
					} else {
						int radius = 0;
						try {
							radius = Integer.parseInt(args[3]);
						} catch(NumberFormatException e) {
							p.sendMessage(MessageHandler.getMessage("prefix") + args[3] + " isn't a valid radius!");
						}
						SurvivalGames.arenaManager.setDeathmatchDomeRadius(p, radius, false);
					}
					
				}
				
				return true;
				
			} else if(args[1].equalsIgnoreCase("finish")) {
				SurvivalGames.arenaManager.finishSetup(p);
				return true;
				
			} else if(args[1].equalsIgnoreCase("tools")) {
				
				WorldEditPlugin we = SurvivalGames.getWorldEdit();
				if(we == null) {
					ItemStack is = new ItemStack(Material.CARROT_STICK);
					ItemMeta im = is.getItemMeta();
					im.setDisplayName("SurvivalGames Selection Tool");
					is.setItemMeta(im);
					p.getInventory().addItem(is);
					p.sendMessage(MessageHandler.getMessage("arena-tools"));
				} else {
					p.getInventory().addItem(new ItemStack(we.getConfig().getInt("wand-item")));
					p.sendMessage(MessageHandler.getMessage("arena-tools-worldedit"));
				}
				return true;
				
			}
			
		}
		
		
		p.sendMessage(MessageHandler.getMessage("prefix") + "§cCommand not found! Type /sg arena for help!");
		return true;
		
	}
	
	private String getArgs(int i) {
		String s = "";
		for(int a = i; i < args.length; a++) {
			try {
				s += args[a] + " ";
			} catch(ArrayIndexOutOfBoundsException e) {
				break;
			}
		}
		return s.substring(0, s.length() - 1);
	}

}
 public class ConfigArgument {
	
	private CommandSender sender;
	private String[] args;
	
	public ConfigArgument(CommandSender sender, String[] args) {
		this.sender = sender;
		this.args = args;
	}
	
	public boolean execute() {
		if(!(sender instanceof Player)) {
			sender.sendMessage("§cThe config argument can only execute as a Player!");
			return true;
		}
		
		Player p = (Player)sender;
		
		if(!PermissionHandler.hasPermission(p, Permission.CONFIG)) {
			p.sendMessage(MessageHandler.getMessage("no-permission"));
			return true;
		}
		
		if(args.length == 1) {
			Helper.showConfigHelpsite(p);
		} else {
			if(args[1].equalsIgnoreCase("reload")) {
				if(args.length == 2) {
					ConfigReloader.reloadMessage();
					ConfigReloader.reloadConfig();
					ConfigReloader.reloadDatabase();
					ConfigReloader.reloadSigns();
					ConfigReloader.reloadChestloot();
					ConfigReloader.reloadScoreboard();
					p.sendMessage(MessageHandler.getMessage("prefix") + "You've reloaded all configuration files successfully!");
					return true;
				}
				String con = args[2];
				if(con.equalsIgnoreCase("messages")) {
					ConfigReloader.reloadMessage();
					p.sendMessage(MessageHandler.getMessage("prefix") + "You've reloaded the messages.yml successfully!");
			
				} else if(con.equalsIgnoreCase("signs")) {
					ConfigReloader.reloadSigns();
					p.sendMessage(MessageHandler.getMessage("prefix") + "You've reloaded the signs.yml successfully!");
					
				} else if(con.equalsIgnoreCase("database")) {
					ConfigReloader.reloadDatabase();
					p.sendMessage(MessageHandler.getMessage("prefix") + "The settings are applied to a lobby after a lobby-reload or the end of a survival game.");
				} else if(con.equalsIgnoreCase("config")) {
					ConfigReloader.reloadConfig();
					p.sendMessage(MessageHandler.getMessage("prefix") + "You've reloaded the config.yml successfully!");
				} else if(con.equalsIgnoreCase("chestloot")) {
					ConfigReloader.reloadChestloot();
					p.sendMessage(MessageHandler.getMessage("prefix") + "You've reloaded the chestloot.yml successfully!");
				} else if(con.equalsIgnoreCase("scoreboard")) {
					ConfigReloader.reloadScoreboard();
					p.sendMessage(MessageHandler.getMessage("prefix") + "You've reloaded the scoreboard.yml successfully!");
				} else {
					p.sendMessage(MessageHandler.getMessage("config-error-name").replace("%0%", "/sg config reload [MESSAGES/SIGNS/DATABASE/CONFIG/CHESTLOOT/SCOREBOARD]"));
					return true;
				}
				
			}
		}
		return true;
	}

}
 public class LobbyArgument {
	
	private CommandSender sender;
	private String[] args;
	
	public LobbyArgument(CommandSender sender, String[] args) {
		this.sender = sender;
		this.args = args;
	}
	
	public boolean execute() {
		
		if(!(sender instanceof Player)) {
			sender.sendMessage("§cThe lobby argument can only execute as a Player!");
			return true;
		}
		
		Player p = (Player)sender;
		
		if(!PermissionHandler.hasPermission(p, Permission.GAME) && !PermissionHandler.hasPermission(p, Permission.LOBBY)) {
			p.sendMessage(MessageHandler.getMessage("no-permission"));
			return true;
		}
		
		if(args.length == 1) {
			Helper.showLobbyHelpsite(p);
		} else {
			if(args[1].equalsIgnoreCase("delete")) {
				if(args.length == 2) {
					p.sendMessage(MessageHandler.getMessage("game-must-enter").replace("%0%", "/sg lobby create <NAME>"));
					return true;
				}
				
				if(SurvivalGames.gameManager.getGame(args[2]) != null) {
					p.sendMessage(MessageHandler.getMessage("prefix") + "§cYou must unload the lobby first! /sg lobby unload " + args[2]);
					return true;
				}
				
				if(!SurvivalGames.database.contains("Games." + args[2])) {
					p.sendMessage(MessageHandler.getMessage("prefix") + "§cLobby " + args[2] + " does not exist!");
					return true;
				}
				
				SurvivalGames.database.set("Games." + args[2], null);
				SurvivalGames.saveDataBase();
				p.sendMessage(MessageHandler.getMessage("prefix") + "You've removed lobby " + args[2] + " successfully!");
				return true;
			} else if(args[1].equalsIgnoreCase("create")) {
				if(args.length == 2) {
					p.sendMessage(MessageHandler.getMessage("game-must-enter").replace("%0%", "/sg lobby create <NAME>"));
					return true;
				}
				
				SurvivalGames.gameManager.createGame(p, args[2]);
				return true;
				
				
			} else if(args[1].equalsIgnoreCase("setspawn")) {
				
				if(args.length == 2) {
					p.sendMessage(MessageHandler.getMessage("game-must-enter").replace("%0%", "/sg lobby setspawn <NAME>"));
					return true;
				}
				
				SurvivalGames.gameManager.setSpawn(p, args[2]);
				return true;
				
				
			} else if(args[1].equalsIgnoreCase("unload")) {
				if(args.length == 2) {
					p.sendMessage(MessageHandler.getMessage("game-must-enter").replace("%0%", "/sg lobby unload <NAME>"));
					return true;
				}
				Game game = SurvivalGames.gameManager.getGame(args[2]);
				if(game == null) {
					p.sendMessage(MessageHandler.getMessage("game-not-loaded").replace("%0%", args[2]));
					return true;
				}
				game.sendMessage(MessageHandler.getMessage("prefix") + "§c§lYour lobby was stopped by an admin!");
				if(game.getState() == GameState.INGAME || game.getState() == GameState.DEATHMATCH) {
					p.sendMessage(MessageHandler.getMessage("prefix") + "§cIt my can be that the blocks of arena " + game.getCurrentArena().getName() + " aren't reseted yet. It will reset while loading lobby.");
				}
				
				SurvivalGames.gameManager.unload(game);
				p.sendMessage(MessageHandler.getMessage("game-success-unloaded").replace("%0%", args[2]));
				SurvivalGames.signManager.updateSigns();
				return true;
				
				
			} else if(args[1].equalsIgnoreCase("load")) {
				if(args.length == 2) {
					p.sendMessage(MessageHandler.getMessage("game-must-enter").replace("%0%", "/sg lobby unload <NAME>"));
					return true;
				}
				Game game = SurvivalGames.gameManager.getGame(args[2]);
				if(game != null) {
					p.sendMessage(MessageHandler.getMessage("game-already-loaded").replace("%0%", args[2]));
					return true;
				}
				
				boolean success = SurvivalGames.gameManager.load(args[2]);
				
				if(!success) {
					p.sendMessage(MessageHandler.getMessage("game-load-error").replace("%0%", args[2]).replace("%1%", "See console for informations! It may can be that a few arenas have to be reset. When this happens, the game will automatically load after all arenas were reset."));
				} else {
					p.sendMessage(MessageHandler.getMessage("game-success-loaded").replace("%0%", args[2]));
					SurvivalGames.signManager.updateSigns();
				}
				return true;
				
				
			} else if(args[1].equalsIgnoreCase("reload")) {
				if(args.length == 2) {
					p.sendMessage(MessageHandler.getMessage("game-must-enter").replace("%0%", "/sg lobby unload <NAME>"));
					return true;
				}
				p.performCommand("sg lobby unload " + args[2]);
				p.performCommand("sg lobby load " + args[2]);
				return true;
				
				
			} else if(args[1].equalsIgnoreCase("list")) {
				if(args.length == 2) {
					p.sendMessage(MessageHandler.getMessage("game-must-enter").replace("%0%", "/sg lobby list <NAME>"));
					return true;
				}
				Game game = SurvivalGames.gameManager.getGame(args[2]);
				if(game == null) {
					p.sendMessage(MessageHandler.getMessage("game-not-loaded").replace("%0%", args[2]));
					return true;
				}
				List<Arena> arenas = game.getArenas();
				p.sendMessage(MessageHandler.getMessage("prefix") + "Arenas in lobby " + game.getName() + "§8: §7(§b" + arenas.size() + "§7)");
				for(Arena a : arenas) {
					p.sendMessage("§7- §6" + a.getName());
				}
				return true;
			}
			
			
			p.sendMessage(MessageHandler.getMessage("prefix") + "§cCommand not found! Type /sg lobby for help!");
			return true;
			
		}
		
		
		return true;
		
	}

} public class StatsArgument {
	
	private CommandSender sender;
	private String[] args;
	
	public StatsArgument(CommandSender sender, String[] args) {
		this.sender = sender;
		this.args = args;
	}
	
	public boolean execute() {
		if(!PermissionHandler.hasPermission(sender, Permission.JOIN)) {
			sender.sendMessage(MessageHandler.getMessage("no-permission"));
			return true;
		}
		
		String name = sender.getName();
		
		if(args.length > 1) {
			name = args[1];
		}
		
		StatisticManager.sendStatistics(sender, name);
		return true;
	}


}
 public class MessageHandler {
	
	private static HashMap<String, String> messages = new HashMap<>();
	private static List<String> withoutPrefix = new ArrayList<>();
	
	public static void reload() {
		messages.clear();
		for(String key : SurvivalGames.messages.getConfigurationSection("").getKeys(false)) {
			messages.put(key, replaceColors(SurvivalGames.messages.getString(key)));
		}
		
		withoutPrefix.add("prefix");
		withoutPrefix.add("stats-kills");
		withoutPrefix.add("stats-deaths");
		withoutPrefix.add("stats-kdr");
		withoutPrefix.add("stats-wins");
		withoutPrefix.add("stats-played");
		withoutPrefix.add("stats-points");
		withoutPrefix.add("stats-footer");
		
		System.out.println("[SurvivalGames] " + messages.size() + " messages loaded!");
	}
	
	public static String getMessage(String name) {
		if(messages.containsKey(name)) {
			if(withoutPrefix.contains(name)) {
				return messages.get(name);
			} else {
				return messages.get("prefix") + messages.get(name);
			}
		} else {
			return "§cMessage not found!";
		}
	}
	
	public static String replaceColors(String s) {
		return ChatColor.translateAlternateColorCodes('&', s);
	}

}
 public enum Permission {
	
	ARENA("sg.arena"),
	
	CONFIG("sg.config"),
	
	START("sg.start"),
	
	GAME("sg.game"),
	LOBBY("sg.lobby"),
	
	LIST("sg.list"),
	
	JOIN("sg.join"),
	
	STATS("sg.stats"),
	
	SPECTATE("sg.spectate");
	
	private final String permission;
	
	private Permission(String permission) {
		this.permission = permission;
	}
	
	public String getPermission() {
		return permission;
	}

}
 public class PermissionHandler {
	
	private static boolean usePermission = SurvivalGames.instance.getConfig().getBoolean("use-permissions");
	
	public static void reinitializeUsePermission() {
		usePermission = SurvivalGames.instance.getConfig().getBoolean("use-permissions");
	}
	
	public static boolean hasPermission(CommandSender sender, Permission permission) {
		if(usePermission) {
			return sender.hasPermission(permission.getPermission());
		} else {
			if(sender.isOp()) {
				return true;
			} else {
				return permission == Permission.JOIN || permission == Permission.LIST || permission == Permission.SPECTATE || permission == Permission.STATS;
			}
		}
	}
	
	// DONATOR PERMISSONS
	
	private static List<String> joinPower;
	private static HashMap<String, Integer> votePower = new HashMap<>();
	
	public static void reinitializeDatabase() {
		FileConfiguration c = SurvivalGames.instance.getConfig();
		
		joinPower = c.getStringList("Donator-Permissions.Join-Full-Arena");
		
		votePower.clear();
		for(String key : c.getStringList("Donator-Permissions.Extra-Vote-Power")) {
			try {
				String[] split = key.split("//");
				votePower.put(split[0], Integer.parseInt(split[1]));
			} catch(NumberFormatException | ArrayIndexOutOfBoundsException e) { }
		}
	}
	
	public static int getVotePower(Player p) {
		int l = 1;
		
		for(Entry<String, Integer> vote : votePower.entrySet()) {
			if(p.hasPermission(vote.getKey())) {
				l = vote.getValue();
			}
		}
		
		return l;
	}
	
	public static User canJoin(Player p, Game game) {
		int pLevel = 0;
		int ii = 0;
		for(String key : joinPower) {
			ii++;
			if(p.hasPermission(key)) {
				pLevel = ii;
			}
		}
		
		if(pLevel == 0)
			return null;
		
		Collections.shuffle(game.getUsers());
		
		User lU = null;
		int l = pLevel;
		
		for(User u : game.getUsers()) {
			int level = 0;
			int i = 0;
			for(String key : joinPower) {
				i++;
				if(u.getPlayer().hasPermission(key)) {
					level = i;
				}
			}
			if(level < l) {
				l = level;
				lU = u;
			}
		}
		
		return lU;
	}

}
 public class Game {
	
	// STATIC VARIABLES
	private static ItemStack leaveItem;
	
	public static ItemStack getLeaveItem() {
		return leaveItem;
	}
	
	public static void reinitializeDatabase() {
		leaveItem = Util.parseItemStack(SurvivalGames.instance.getConfig().getString("Leave-Item"));
		playerNavigator = Util.parseItemStack(SurvivalGames.instance.getConfig().getString("Spectating.Player-Navigator.Item"));
		String s = SurvivalGames.instance.getConfig().getString("Spectating.Player-Navigator.Inventory-Title");
		if(s.length() > 32)
			s = s.substring(0, 32);
		inventoryTitle = ChatColor.translateAlternateColorCodes('&', s);
	}
	
	private String name;
	private Location lobby;
	private boolean voting, reset;
	private int maxVotingArenas;
	private List<Arena> arenas;
	private int reqplayers, maxplayers;
	private GameState state;
	private int lobbytime, cooldown = 30;
	private int death = 0;
	
	private VotingPhase votingPhase;
	private CooldownPhase cooldownPhase;
	private IngamePhase ingamePhase;
	private DeathmatchPhase deathmatchPhase;
	private FinishPhase finishPhase;
	
	private Arena arena;
	private List<User> users = new ArrayList<>();
	private List<Chest> chests = new ArrayList<>();
	private List<String> rChunks = new ArrayList<>();
	public ArrayList<String> voted = new ArrayList<>();
	
	public Game(String name, Location lobby, boolean voting, int lobbytime, int maxVotingArenas, int reqplayers, List<Arena> arenas, boolean reset) {
		this.name = name;
		this.lobby = lobby;
		this.voting = voting;
		this.lobbytime = lobbytime;
		this.maxVotingArenas = maxVotingArenas;
		this.arenas = arenas;
		this.reset = reset;

		if(reqplayers < 2) {
			reqplayers = 2;
		}
		
		this.reqplayers = reqplayers;
		this.maxplayers = getFewestArena().getSpawns().size();
		
		setScoreboardPhase(SurvivalGames.getScoreboardManager().getNewScoreboardPhase(GameState.WAITING));
		
		setState(GameState.WAITING);
	}
	
	public List<String> getVotedUsers() {
		return voted;
	}
	
	// START SPECTATOR
	private Inventory playerNavigatorInventory;
	private List<SpectatorUser> spectators = new ArrayList<>();
	
	private static ItemStack playerNavigator;
	private static String inventoryTitle;
	
	public static ItemStack getPlayerNavigatorItem() {
		return playerNavigator;
	}
	
	public static String getPlayerNavigatorInventoryTitle() {
		return inventoryTitle;
	}
	
	public Inventory getPlayerNavigatorInventory() {
		return playerNavigatorInventory;
	}
	
	public void redefinePlayerNavigatorInventory() {
		if(playerNavigator != null) {
			int amount = getPlayingUsers();
			int inv = 54;
			if(amount <= 9) {
				inv = 9;
			} else if(amount <= 18) {
				inv = 18;
			} else if(amount <= 27) {
				inv = 27;
			} else if(amount <= 36) {
				inv = 36;
			} else if(amount <= 45) {
				inv = 45;
			} else if(amount <= 54) {
				inv = 54;
			}
			
			playerNavigatorInventory = Bukkit.createInventory(null, inv, inventoryTitle);
			
			ItemStack head = new ItemStack(Material.SKULL_ITEM, 0);
			head.setDurability((short) 3);
			ItemMeta im = head.getItemMeta();
			List<String> lore = new ArrayList<>();
			lore.add("§7Click to spectate!");
			for(int i = 0; i < users.size(); i++) {
				if(i >= inv)
					break;
				User u = users.get(i);
				im.setDisplayName("§e" + u.getName());
				head.setItemMeta(im);
				playerNavigatorInventory.setItem(i, head);
			}
		}
	}
	
	public void joinSpectator(final SpectatorUser user) {
		spectators.add(user);
		Arena a = getCurrentArena();
		if(getState() == GameState.DEATHMATCH) {
			user.getPlayer().teleport(a.getDeathmatchSpawns().get(0));
		} else {
			user.getPlayer().teleport(a.getSpawns().get(0));
		}
		
		for(User u : users) {
			u.getPlayer().hidePlayer(user.getPlayer());
		}
		user.clear();
		user.getPlayer().setAllowFlight(true);
		user.getPlayer().setFlying(true);
		user.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 1000000, 1));
		
		Bukkit.getScheduler().scheduleSyncDelayedTask(SurvivalGames.instance, new Runnable() {
			public void run() {
				user.getPlayer().getInventory().setItem(8, leaveItem);
				user.getPlayer().getInventory().setItem(7, playerNavigator);
				user.getPlayer().updateInventory();
			}
		}, 2L);
		
		sendSpectators(MessageHandler.getMessage("spectator-join").replace("%0%", user.getName()));
		updateScoreboard();
	}
	
	public void leaveSpectator(SpectatorUser user) {
		spectators.remove(user);
		updateScoreboard();
	}
	
	
	public void sendSpectators(String msg) {
		for(SpectatorUser su : spectators) {
			su.sendMessage(msg);
		}
	}
	
	public void sendSpectators(BaseComponent[] msg) {
		for(SpectatorUser su : spectators) {
			su.sendMessage(msg);
		}
	}
	
	public List<SpectatorUser> getSpecators() {
		return spectators;
	}
	
	// END SPECTATOR
	
	public User getUser(String name) {
		for(User u : users) {
			if(u.getName().equals(name))
				return u;
		}
		return null;
	}
	
	public int getDeathAmount() {
		return death;
	}
	
	public void setDeathAmount(int death) {
		this.death = death;
	}
	
	public void join(User user) {
		users.add(user);
		Player p = user.getPlayer();
		if(arenas.size() == 1) {
			Arena arena = arenas.get(0);
			for(int i = 0; i < arena.getSpawns().size(); i++) {
				if(!hasUserIndex(i)) {
					p.teleport(arena.getSpawns().get(i));
					user.setSpawnIndex(i);
					break;
				}
			}
		} else if(getState() == GameState.COOLDOWN) {
			for(int i = 0; i < getCurrentArena().getSpawns().size(); i++) {
				if(!hasUserIndex(i)) {
					p.teleport(getCurrentArena().getSpawns().get(i));
					user.setSpawnIndex(i);
					break;
				}
			}
		} else {
			p.teleport(lobby);
		}
		user.clear();
		p.getInventory().setItem(7, leaveItem);
		p.updateInventory();
		
		if(getState() == GameState.VOTING) {
			getVotingPhrase().equipPlayer(user);
		}
		
		sendMessage(MessageHandler.getMessage("join-success").replace("%0%", p.getName()).replace("%1%", Integer.valueOf(users.size()).toString()).replace("%2%", Integer.valueOf(maxplayers).toString()));
		SurvivalGames.signManager.updateSigns();
		updateScoreboard();
		checkForStart();
//		updateBossBarMessage();
		Bukkit.getPluginManager().callEvent(new UserLobbyJoinedEvent(user, this));
	}
	
	public void forceStart(Player p) {
		if(users.size() < 2) {
			p.sendMessage(MessageHandler.getMessage("prefix") + "§cAt least 2 players are required to start the game!");
			return;
		}
		
		if((getVotingPhrase() != null && getVotingPhrase().isRunning()) || (getCooldownPhrase() != null && getCooldownPhrase().isRunning()) ) {
			p.sendMessage(MessageHandler.getMessage("prefix") + "§cThe game is already starting!");
			return;
		}
		
		forcedStart = true;
		checkForStart();
		p.sendMessage(MessageHandler.getMessage("prefix") + "You've started the game in lobby " + getName() + " successfully!");
	}
	
	public void leave(User user) {
		users.remove(user);
		if(getState() == GameState.INGAME || getState() == GameState.DEATHMATCH)
			redefinePlayerNavigatorInventory();
		checkForCancelStart();
		updateScoreboard();
		SurvivalGames.signManager.updateSigns();
//		updateBossBarMessage();
		Bukkit.getPluginManager().callEvent(new UserLobbyLeftEvent(user.getPlayer(), this));
	}
	
	public void kickall() {
		if(users.size() != 0) {
			int size = users.size();
			for(int i = 0; i < size; i++) {
				try {
					SurvivalGames.userManger.leaveGame(users.get(0).getPlayer());
				} catch(IndexOutOfBoundsException e) {
					break;
				}
			}
		}
		if(spectators.size() != 0) {
			int size = spectators.size();
			for(int i = 0; i < size; i++) {
				try {
					SurvivalGames.userManger.leaveGame(spectators.get(0));
				} catch(IndexOutOfBoundsException e) {
					break;
				}
			}
		}
	}
	
	public void end() {
		new ResetPhase(this);
	}
	
	public DeathmatchPhase getDeathmatch() {
		return deathmatchPhase;
	}
	
	public void startDeathmatch() {
		deathmatchPhase = new DeathmatchPhase(this);
		deathmatchPhase.load();
	}
	
	public void startIngame() {
		ingamePhase = new IngamePhase(this);
		ingamePhase.load();
	}
	
	public void startCooldown(Arena arena) {
		cooldownPhase = new CooldownPhase(this, arena);
		cooldownPhase.load();
	}
	
	public FinishPhase getFinishPhase() {
		return finishPhase;
	}
	
	public void startFinish() {
		finishPhase = new FinishPhase(this);
		finishPhase.load();
	}
	
	public boolean isFinishing() {
		return getFinishPhase() != null;
	}
	
	public boolean isResetEnabled() {
		return reset;
	}
	
	private boolean forcedStart = false;
	public void checkForStart() {
		if(users.size() == reqplayers || forcedStart) {
			if(cooldownPhase != null && cooldownPhase.isRunning())
				return;
			if(votingPhase != null && votingPhase.isRunning())
				return;
			
			if(getArenas().size() == 1) {
				startCooldown(getArenas().get(0));
			} else {
				if(cooldownPhase != null) {
					startCooldown(getArenas().get(0));
				} else {
					votingPhase = new VotingPhase(this);
					votingPhase.load();
					if(forcedStart) {
						if(votingPhase.getTime() > 30)
							votingPhase.setTime(30);
					}
				}
			}
		}
	}
	
	public void checkForCancelStart() {
		if(state != GameState.VOTING && state != GameState.COOLDOWN)
			return;
		
		if(forcedStart) {
			if(users.size() == 1) {
				if(getState() == GameState.COOLDOWN) {
					cooldownPhase.cancelTask();
					sendMessage(MessageHandler.getMessage("game-start-canceled"));
				} else if(getState() == GameState.VOTING){
					votingPhase.cancelTask();
					sendMessage(MessageHandler.getMessage("game-start-canceled"));
				}
				forcedStart = false;
			}
			
		} else {
			if(users.size() == reqplayers - 1) {
				if(getState() == GameState.COOLDOWN) {
					cooldownPhase.cancelTask();
					sendMessage(MessageHandler.getMessage("game-start-canceled"));
				} else if(getState() == GameState.VOTING){
					votingPhase.cancelTask();
					sendMessage(MessageHandler.getMessage("game-start-canceled"));
				}
			}
		}
	}
	
	public IngamePhase getIngamePhrase() {
		return ingamePhase;
	}
	
	public VotingPhase getVotingPhrase() {
		return votingPhase;
	}
	
	public CooldownPhase getCooldownPhrase() {
		return cooldownPhase;
	}
	
	public Arena getFewestArena() {
		int slot = Integer.MAX_VALUE;
		Arena arena = null;
		
		for(Arena a : arenas) {
			if(a.getSpawns().size() < slot) {
				slot = a.getSpawns().size();
				arena = a;
			}
		}
		
		return arena;
	}
	
	public Arena getArena(String name) {
		for(Arena arena : arenas) {
			if(arena.getName().equals(name)) {
				return arena;
			}
		}
		return null;
	}
	
	public void setCurrentArena(Arena arena) {
		this.arena = arena;
//		if(arena != null)
//			updateBossBarMessage();
	}
	
	public Arena getCurrentArena() {
		return arena;
	}
	
	public void setState(GameState state) {
		this.state = state;
		if(SurvivalGames.signManager != null)
			SurvivalGames.signManager.updateSigns();
//		updateBossBarMessage();
	}
	
	public List<User> getUsers() {
		return users;
	}
	
	public int getPlayingUsers() {
		return users.size();
	}
	
	public String getName() {
		return name;
	}
	
	public Location getLobby() {
		return lobby;
	}
	
	public int getLobbyTime() {
		return lobbytime;
	}
	
	public int getRequiredPlayers() {
		return reqplayers;
	}
	
	public int getMaximumPlayers() {
		return maxplayers;
	}
	
	public boolean isVotingEnabled() {
		return voting;
	}
	
	public int getMaxVotingArenas() {
		return maxVotingArenas;
	}
	
	public List<Arena> getArenas() {
		return arenas;
	}
	
	public GameState getState() {
		return state;
	}
	
	public int getCooldownTime() {
		return cooldown;
	}
	
	public void sendMessage(String message) {
		for(User user : users) {
			user.sendMessage(message);
		}
		for(SpectatorUser su : spectators) {
			su.sendMessage(message);
		}
	}
	
	public void sendMessage(BaseComponent[] message) {
		for(User user : users) {
			user.sendMessage(message);
		}
		for(SpectatorUser su : spectators) {
			su.sendMessage(message);
		}
	}
	
	public boolean hasUserIndex(int index) {
		for(User user : users) {
			if(user.getSpawnIndex() == index) {
				return true;
			}
		}
		return false;
	}
	
	public void registerChest(Chest chest) {
		chests.add(chest);
	}
	
	public List<Chest> getRegisteredChests() {
		return chests;
	}
	
	public Chest getChest(Location loc) {
		for(Chest chest : chests) {
			if(chest.getLocation().equals(loc))
				return chest;
		}
		return null;
	}
	
	public boolean isChestRegistered(Location loc) {
		for(Chest chest : chests) {
			if(chest.getLocation().equals(loc))
				return true;
		}
		return false;
	}
	
	public List<String> getChunksToReset() {
		return rChunks;
	}
	
	public String getAlivePlayers() {
		String s = new String();
		List<User> users = getUsers();
		for(int i = 0; i < users.size(); i++) {
			s += "§e" + users.get(i).getName();
			if(i != users.size() - 1)
				s += "§7, ";
		}
		return s;
	}
	
	// SCOREBOARD
	
	private ScoreboardPhase sp;
	
	public void setScoreboardPhase(ScoreboardPhase sp) {
		if(this.sp != null && sp == null) {
			for(User user : users) {
				user.getPlayer().setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
			}
			
			for(SpectatorUser user : spectators) {
				user.getPlayer().setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
			}
		}
		
		this.sp = sp;
		
		if(sp != null) {
			sp.initScoreboard(this);
			updateScoreboard();
		}

	}
	
	public ScoreboardPhase getScoreboardPhase() {
		return sp;
	}
	
	public void updateScoreboard() {
		if(sp != null) {
			for(CustomScore cs : sp.getScores()) {
				cs.update(this);
			}
			
			for(User user : users) {
				updateScoreboard(user);
			}
			
			for(SpectatorUser su : spectators) {
				updateScoreboard(su);
			}
		}
	}
	
	public void updateScoreboard(UserState user) {
		user.getPlayer().setScoreboard(sp.getScoreboard());
	}
	
	// BARAPI (OUTDATED)
	
//	private String cBarapi = null;
//	private float cPercentBarapi = 100;
//	
//	public void updateBossBarMessage() {
//		if(Bukkit.getPluginManager().isPluginEnabled("BarAPI")) {
//			GameState state = getState();
//			if(BarAPIManager.messages.containsKey(state)) {
//				
//				String str = BarAPIManager.messages.get(state);
//				
//				str = str.replace("%0%", Integer.valueOf(getRequiredPlayers()).toString());
//				str = str.replace("%1%", Integer.valueOf(getPlayingUsers()).toString());
//				str = str.replace("%2%", Integer.valueOf(getMaximumPlayers()).toString());
//				str = str.replace("%3%", getName());
//				str = str.replace("%4%", getCurrentArena() == null ? "" : getCurrentArena().getName());
//				
//				if(str.contains("%5%")) {
//					int time = -1;
//					float maxtime = 0;
//					
//					if(state == GameState.VOTING) {
//						time = getVotingPhrase().getTime();
//						maxtime = getLobbyTime();
//					} else if(state == GameState.COOLDOWN) {
//						time = getCooldownPhrase().getTime();
//						maxtime = getCooldownTime();
//					} else if(state == GameState.INGAME) {
//						time = getIngamePhrase().getTime();
//						maxtime = getCurrentArena().getAutomaticlyDeathmatchTime();
//					} else if(state == GameState.DEATHMATCH) {
//						time = getDeathmatch().getTime();
//						maxtime = getDeathmatch().getStartTime();
//					}
//					
//					if(time == -1) {
//						str = str.replace("%5%", "");
//					} else {
//						if(maxtime > 0) {
//							cPercentBarapi = ((float)time / maxtime) * 100;
//						}
//						
//						str = str.replace("%5%", Integer.valueOf(time).toString());
//					}
//					
//					
//				} else {
//					cPercentBarapi = 100;
//				}
//				
//				cBarapi = str;
//				
//				
//				Util.debug("Updated bossbar in game " + getName() + ": " + str + " (" + cPercentBarapi + "%)");
//				if(str.isEmpty()) {
//					for(User user : getUsers()) {
//						BarAPI.removeBar(user.getPlayer());
//					}
//				} else {
//					for(User user : getUsers()) {
//						BarAPI.setMessage(user.getPlayer(), str, cPercentBarapi);
//					}
//				}
//				
//			} else {
//				if(cBarapi != null) {
//					for(User user : getUsers()) {
//						BarAPI.removeBar(user.getPlayer());
//					}
//					cBarapi = null;
//				}
//			}
//		}
//	}
//	
//	public String getCurrentBossBarMessage() {
//		return cBarapi;
//	}
//	
//	public float getCurrentBossBarPercentage() {
//		return cPercentBarapi;
//	}
//	
//	public void updateBossBar(User user) {
//		if(cBarapi != null && Bukkit.getPluginManager().isPluginEnabled("BarAPI")) {
//			if(cBarapi.isEmpty()) {
//				BarAPI.removeBar(user.getPlayer());
//			} else {
//				BarAPI.setMessage(user.getPlayer(), cBarapi, cPercentBarapi);
//			}	
//		}
//	}

}
 public enum GameState {
	
	WAITING,
	
	VOTING,
	
	COOLDOWN,
	
	INGAME,
	
	DEATHMATCH,
	
	RESET;
	
	public boolean isIngame() {
		return this == INGAME || this == DEATHMATCH;
	}

} public class IngamePhase {

	private Game game;
	public BukkitTask task;
	private boolean running;
	private int time;
	private UserManager um = SurvivalGames.userManger;
	private boolean braodcastWin = SurvivalGames.instance.getConfig().getBoolean("broadcast-win");
	
	private boolean lightningOD = SurvivalGames.instance.getConfig().getBoolean("Lightning.on-death");
	private boolean lightningFP = SurvivalGames.instance.getConfig().getBoolean("Lightning.on-few-players");
	private int lightningFPc = SurvivalGames.instance.getConfig().getInt("Lightning.few-players");
	private int lightningFPt = SurvivalGames.instance.getConfig().getInt("Lightning.few-players-time");
	private BukkitTask lTask;
	
	public boolean grace = false;
	private int period; 
	
	// GAME STATISTIC
	private long start = System.currentTimeMillis();
	private int players;
	// GAME STATISTIC END
	
	private BukkitTask deathmatch, chestrefill, gracetask;
	
	public IngamePhase(Game game) {
		this.game = game;
		this.period = game.getCurrentArena().getGracePeriod();
		this.time = game.getCurrentArena().getAutomaticlyDeathmatchTime();
	}
	
	public void load() {
		game.setScoreboardPhase(SurvivalGames.getScoreboardManager().getNewScoreboardPhase(GameState.INGAME));
		start();
	}
	
	public void start() {		
		game.setState(GameState.INGAME);
		game.sendMessage(MessageHandler.getMessage("game-start").replace("%0%", Integer.valueOf(game.getPlayingUsers()).toString()));
		running = true;
		game.redefinePlayerNavigatorInventory();
		players = game.getPlayingUsers();
		
		game.getCurrentArena().getMinimumLocation().getWorld().setTime(0);
		
		if(game.getCurrentArena().chestRefill()) {
			chestrefill = Bukkit.getScheduler().runTaskLater(SurvivalGames.instance, new Runnable() {
				public void run() {
					long time = game.getCurrentArena().getMinimumLocation().getWorld().getTime();
					if(time >= 18000 && time <= 18200) {
						for(Chest c : game.getRegisteredChests()) {
							c.getLocation().getWorld().playEffect(c.getLocation(), Effect.MOBSPAWNER_FLAMES, 1);
							c.getLocation().getWorld().playSound(c.getLocation(), Sound.LEVEL_UP, 4.0F, 1.0F);
						}
						game.getRegisteredChests().clear();
						game.sendMessage(MessageHandler.getMessage("game-chestrefill"));
					}
				}
			}, 18001);
		}
		
		if(period != 0) {
			game.sendMessage(MessageHandler.getMessage("game-grace-period").replace("%0%", Integer.valueOf(period).toString()));
			grace = true;
			
			gracetask = Bukkit.getScheduler().runTaskLater(SurvivalGames.instance, new Runnable() {
				public void run() {
					game.sendMessage(MessageHandler.getMessage("game-grace-period-ended"));
					grace = false;
					startTask();
				}
			}, period * 20);
		} else {
			startTask();
		}
	}
	
	private void startTask() {
		if(lightningFP) {
			startLightningTask();
		}
		task = Bukkit.getScheduler().runTaskTimer(SurvivalGames.instance, new Runnable() {
			public void run() {
				
				if(game.getCurrentArena().isDeathmatchEnabled()) {
					if(time % 600 == 0 && time > 300) {
						game.sendMessage(MessageHandler.getMessage("game-deathmatch-cooldown").replace("%0%", Util.getFormatedTime(time)));
					} else if(time <= 300 && time % 60 == 0 && time > 60) {
						game.sendMessage(MessageHandler.getMessage("game-deathmatch-cooldown").replace("%0%", Util.getFormatedTime(time)));
					} else if(time <= 60 && time % 10 == 0 && time > 10) {
						game.sendMessage(MessageHandler.getMessage("game-deathmatch-cooldown").replace("%0%", Util.getFormatedTime(time)));
					} else if(time <= 10 && time > 0) {
						game.sendMessage(MessageHandler.getMessage("game-deathmatch-cooldown").replace("%0%", Util.getFormatedTime(time)));
					} else if(time == 0) {
						cancelTask();
						cancelLightningTask();
						game.startDeathmatch();
						return;
					}
				}

				game.updateScoreboard();
				time--;
			}
		}, 0L, 20L);
	}
	
	public void killUser(User user, User killer, boolean leave, boolean spectate) {
		int remain = game.getUsers().size() - 1;
		
		// STATISTIC
		user.getStatistics().addPlayed();
		user.getStatistics().addDeath();
		
		if(leave) {
			game.sendMessage(MessageHandler.getMessage("game-player-left").replace("%0%", user.getName()));
		} else {
			if(killer == null) {
				game.sendMessage(MessageHandler.getMessage("game-player-die-damage").replace("%0%", user.getName()));
			} else {
				// STATISTIC
				killer.getStatistics().addKill();
				game.sendMessage(MessageHandler.getMessage("game-player-die-killer").replace("%0%", user.getName()).replace("%1%", killer.getName()));
				double killMoney = game.getCurrentArena().getMoneyOnKill();
				if(killMoney > 0 && SurvivalGames.econ != null) {
					SurvivalGames.econ.depositPlayer(killer.getName(), killMoney);
					killer.sendMessage(MessageHandler.getMessage("arena-money-kill").replace("%0%", Double.valueOf(killMoney).toString()).replace("%1%", user.getName()));
				}
				
				// KILL STATISTIC
				DatabaseThread.addTask(new DatabaseTask("INSERT INTO `" + DatabaseManager.tablePrefix + "kills` " +
						"(`player`, `victim`, `health`, `time`) " +
						"VALUES ('" + killer.getPlayer().getUniqueId().toString() + "'," +
						"'" + user.getPlayer().getUniqueId().toString() + "'," +
						"'" + (int)((Damageable)killer.getPlayer()).getHealth() + "'," +
						"'" + new Date(System.currentTimeMillis()) + "')"));
				// KILL STATISTIC END
			}
		}
		
		game.sendMessage(MessageHandler.getMessage("game-remainplayers").replace("%0%", Integer.valueOf(remain).toString()));
		if(lightningOD)
			user.getPlayer().getWorld().strikeLightningEffect(user.getPlayer().getLocation());
		
		for(ItemStack is : user.getPlayer().getInventory().getContents()) {
			if(is == null || is.getType() == Material.AIR)
				continue;
			user.getPlayer().getWorld().dropItemNaturally(user.getPlayer().getLocation(), is);
		}
		
		for(ItemStack is : user.getPlayer().getInventory().getArmorContents()) {
			if(is == null || is.getType() == Material.AIR)
				continue;
			user.getPlayer().getWorld().dropItemNaturally(user.getPlayer().getLocation(), is);
		}
		final Player p = user.getPlayer();
		
		um.leaveGame(p);
		game.setDeathAmount(game.getDeathAmount() + 1);
		game.updateScoreboard();
		
		if(remain == 1) {
			User winner = game.getUsers().get(0);
			// STATISTIC
			winner.getStatistics().addWin();
			winner.getStatistics().addPlayed();
			
			if(braodcastWin) {
				Bukkit.broadcastMessage(MessageHandler.getMessage("game-win").replace("%0%", winner.getName()).replace("%1%", game.getCurrentArena().getName()).replace("%2%", game.getName()));
			}
			
			winner.sendMessage(MessageHandler.getMessage("game-win-winner-message").replace("%0%", game.getCurrentArena().getName()));
			double winMoney = game.getCurrentArena().getMoneyOnWin();
			if(winMoney > 0 && SurvivalGames.econ != null) {
				SurvivalGames.econ.depositPlayer(winner.getName(), winMoney);
				winner.sendMessage(MessageHandler.getMessage("arena-money-win").replace("%0%", Double.valueOf(winMoney).toString()));
			}
			
			// GAME STATISTIC
			DatabaseThread.addTask(new DatabaseTask("INSERT INTO `" + DatabaseManager.tablePrefix + "games` " +
					"(`arena`, `duration`, `end`, `players`, `winner`) " +
					"VALUES ('" + game.getCurrentArena().getName() + "'," +
					"'" + (System.currentTimeMillis() - start) / 1000 + "'," +
					"'" + new Date(System.currentTimeMillis()) + "'," +
					"'" + players + "'," +
					"'" + winner.getPlayer().getUniqueId().toString() + "')"));
			// GAME STATISTIC END
			
			game.startFinish();
		} else {
			if(spectate) {
				if(PermissionHandler.hasPermission(p, Permission.SPECTATE)) {
					Bukkit.getScheduler().scheduleSyncDelayedTask(SurvivalGames.instance, new Runnable() {
						public void run() {
							um.joinGameAsSpectator(p, game.getName());
						}
					}, 2L);
				}
			}

			if(remain == game.getCurrentArena().getPlayerDeathmatchAmount() && game.getCurrentArena().isDeathmatchEnabled())
				startDeathmatchTask();
		}

	}
	
	public void startDeathmatchTask() {
		if(game.getDeathmatch() != null)
			return;
		
		cancelTask();
		
		deathmatch = Bukkit.getScheduler().runTaskTimer(SurvivalGames.instance, new Runnable() {
			int time = 60;
			public void run() {
				
				if(time % 10 == 0 && time > 10) {
					game.sendMessage(MessageHandler.getMessage("game-deathmatch-cooldown").replace("%0%", Util.getFormatedTime(time)));
				} else if(time <= 10 && time > 0) {
					game.sendMessage(MessageHandler.getMessage("game-deathmatch-cooldown").replace("%0%", Util.getFormatedTime(time)));
				} else if(time == 0) {
					cancelDeathmatchTask();
					cancelLightningTask();
					game.startDeathmatch();
					return;
				}
				
				time--;
			}
		}, 0L, 20L);
	}
	
	public int getTime() {
		return time;
	}
	
	public void startLightningTask() {
		lTask = Bukkit.getScheduler().runTaskTimer(SurvivalGames.instance, new Runnable() {
			public void run() {
				if(game.getPlayingUsers() <= lightningFPc && !game.getCurrentArena().isDeathmatchEnabled()) {
					for(User user : game.getUsers()) {
						user.getPlayer().getWorld().strikeLightningEffect(user.getPlayer().getLocation());
					}
				}
			}
		}, lightningFPt * 20L, lightningFPt * 20L);
	}
	
	public void cancelLightningTask() {
		if(lTask != null)
			lTask.cancel();
	}
	
	public void cancelTask() {
		if(task != null)
			task.cancel();
		if(chestrefill != null)
			chestrefill.cancel();
		if(gracetask != null)
			gracetask.cancel();
	}
	
	public void cancelDeathmatchTask() {
		if(deathmatch != null)
			deathmatch.cancel();
	}
	
	public boolean isRunning() {
		return running;
	}
}
 public class VotingPhase {
	
	// STATIC VARIABLES
	private static ItemStack voteItem, arenaItem;
	private static String title;
	
	public static ItemStack getVotingOpenItemStack() {
		return voteItem;
	}
	
	public static String getVotingInventoryTitle() {
		return title;
	}
	
	public static void reinitializeDatabase() {
		voteItem = Util.parseItemStack(SurvivalGames.instance.getConfig().getString("Voting.Item"));
		arenaItem = Util.parseItemStack(SurvivalGames.instance.getConfig().getString("Voting.ArenaItem"));
		title = SurvivalGames.instance.getConfig().getString("Voting.InventoryTitle");
		if(title.length() > 32) {
			title = title.substring(0, 32);
		}
		title = ChatColor.translateAlternateColorCodes('&', title);
	}
	
	private Game game;
	private BukkitTask task;
	private boolean running = false;
	private int time;
	
	public ArrayList<Arena> voteArenas = new ArrayList<Arena>();
	private Inventory voteInventory;
	
	
	public VotingPhase(Game game) {
		reinitializeDatabase();
		this.game = game;

	}
	
	public void load() {
		game.setState(GameState.VOTING);
		chooseRandomArenas();
		game.setScoreboardPhase(SurvivalGames.getScoreboardManager().getNewScoreboardPhase(GameState.VOTING));
		time = game.getLobbyTime();
		start();
	}
	
	public void start() {
		running = true;
		
		if(game.isVotingEnabled()) {
			if(voteItem != null) {
				generateInventory();
				for(User user : game.getUsers()) {
					equipPlayer(user);
				}
			}
		}
		
		task = Bukkit.getScheduler().runTaskTimer(SurvivalGames.instance, new Runnable() {
			public void run() {
				
				for(User user : game.getUsers()) {
					user.getPlayer().setLevel(time);
					user.getPlayer().setExp(Util.getExpPercent((float)time, (float)game.getLobbyTime()));
				}
				
				if(time % 10 == 0 && time > 10) {
					game.sendMessage(MessageHandler.getMessage("game-voting-cooldown").replace("%0%", Util.getFormatedTime(time)));
				}
				
				if(time % 15 == 0 && time != 0) {
					sendVoteMessage();
				} else if(time <= 10 && time > 0){
					game.sendMessage(MessageHandler.getMessage("game-voting-cooldown").replace("%0%", Util.getFormatedTime(time)));
				} else if(time == 0) {
					for(User user : game.getUsers()) {
						user.getPlayer().getInventory().setItem(1, null);
						user.getPlayer().updateInventory();
					}
					
					task.cancel();
					running = false;
					time = game.getLobbyTime();
					game.sendMessage(MessageHandler.getMessage("game-voting-end"));
					Arena winner = getMostVotedArena();
					winner.getSpawns().get(0).getWorld().setTime(0);
					
					game.startCooldown(winner);
					for(Arena arena : voteArenas) {
						arena.setVotes(0);
					}
					voteArenas.clear();
					game.getVotedUsers().clear();
					return;
				}
				
				game.updateScoreboard();
				time--;

			}
		}, 0L, 20L);
	}
	
	public Inventory getVotingInventory() {
		return voteInventory;
	}
	
	public void equipPlayer(User user) {
		user.getPlayer().getInventory().setItem(1, voteItem);
		user.getPlayer().updateInventory();
	}
	
	public List<Arena> getArenas() {
		return voteArenas;
	}
	
	public void generateInventory() {
		int arenas = voteArenas.size();
		int size = 9;
		
		if(arenas >= 9) {
			size = 9;
		} else if(arenas >= 18) {
			size = 18;
		} else if(arenas >= 27) {
			size = 27;
		} else if(arenas >= 36) {
			size = 36;
		} else if(arenas >= 45) {
			size = 45;
		} else if(arenas >= 54) {
			size = 54;
		}
		
		voteInventory = Bukkit.createInventory(null, size, title);
		
		int place = size / arenas; 
		int c = 0;
		
		for(int i = 0; i < size; i++) {
			Arena a;
			try {
				a = voteArenas.get(i);
			} catch(IndexOutOfBoundsException e) {
				break;
			}
			
			if(a == null)
				break;
			
			ItemStack is = arenaItem.clone();
			ItemMeta im = is.getItemMeta();
			im.setDisplayName((i + 1) + ". §e§l" + a.getName());
			is.setItemMeta(im);
			
			voteInventory.setItem(c, is);
			c += place;
		}
	}
	
	public int getTime() {
		return time;
	}
	
	public void setTime(int time) {
		this.time = time;
	}
	
	public Arena getMostVotedArena() {
		Arena mostVoted = null;

		int votes = 0;
		for (Arena arena : voteArenas) {
			if (arena.getVotes() > votes) {
				votes = arena.getVotes();
				mostVoted = arena;
			}
		}
		
		if(mostVoted == null)
			mostVoted = voteArenas.get(0);
		return mostVoted;
	}
	
	public boolean canVote(String player) {
		return !game.getVotedUsers().contains(player);
	}


	public Arena vote(Player p, int id) {
		try {
			Arena a = voteArenas.get(id - 1);
			if(a != null) {
				int amount = PermissionHandler.getVotePower(p);
				a.setVotes(a.getVotes() + amount);
				game.getVotedUsers().add(p.getName());
				p.sendMessage(MessageHandler.getMessage("game-success-vote").replace("%0%", a.getName()));
				if(amount > 1) {
					p.sendMessage(MessageHandler.getMessage("game-extra-vote").replace("%0%", Integer.valueOf(amount).toString()));
				}
				game.updateScoreboard();
			}
				
			return a;
		} catch(IndexOutOfBoundsException e) {
			return null;
		}
	}
	
	private void sendVoteMessage() {
		if(game.isVotingEnabled()) {
			if(voteItem == null) {
				game.sendMessage(MessageHandler.getMessage("game-vote"));
			}
			
			int i = 1;
			for(Arena arena : voteArenas) {
				game.sendMessage(new ComponentBuilder("§3" + i + "§7. §6" + arena.getName() + " §7(§e" + arena.getVotes() + "§7)")
				.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("Click to vote for arena " + arena.getName()).create()))
				.event(new ClickEvent(Action.RUN_COMMAND, "/sg vote " + i)).create());
				i++;
			}
		}
	}
	
	private void chooseRandomArenas() {
		List<Arena> arenas = game.getArenas();

		voteArenas.clear();
		Collections.shuffle(arenas);

		int i = 0;
		for(Arena a : arenas) {
			if(i == game.getMaxVotingArenas())
				break;
			voteArenas.add(a);
			i++;
		}
	}
	
	public boolean isRunning() {
		return running;
	}
	
	public void cancelTask() {
		if(task != null)
			task.cancel();
		running = false;
		voteArenas.clear();
		time = game.getLobbyTime();
		return;
	}

}
 public class User extends UserState {
	
	private int spawnIndex = Integer.MIN_VALUE;
	private Chest currentChest;
	
	public User(Player player, Game game) {
		super(player, game);
		this.player = player;
	}

	public void setSpawnIndex(int index) {
		this.spawnIndex = index;
	}
	
	public int getSpawnIndex() {
		return spawnIndex;
	}
	
	public Chest getCurrentChest() {
		return currentChest;
	}
	
	public void setCurrentChest(Chest chest) {
		this.currentChest = chest;
	}
	
	public boolean isInChest() {
		return currentChest != null;
	}

	@Override
	public boolean isSpectator() {
		return false;
	}

}
 public class UserManager {
	
	private GameManager gm = SurvivalGames.gameManager;
	
	// START SPECTATOR
	
	public void leaveGame(SpectatorUser su) {
		Game g = su.getGame();
		for(User u : g.getUsers()) {
			u.getPlayer().showPlayer(su.getPlayer());
		}
		g.leaveSpectator(su);
		if(g.getScoreboardPhase() != null) {
			su.getPlayer().setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
		}
		setState(su.getPlayer(), su);
	}
	
	public void joinGameAsSpectator(Player p, String gamename) {
		if(!SurvivalGames.instance.getConfig().getBoolean("Spectating.Enabled")) {
			p.sendMessage(MessageHandler.getMessage("spectator-disabled"));
			return;
		}
		
		if(!PermissionHandler.hasPermission(p, Permission.SPECTATE)) {
			p.sendMessage(MessageHandler.getMessage("no-permission"));
			return;
		}
		
		if(isSpectator(p.getName()) || isPlaying(p.getName())) {
			p.sendMessage(MessageHandler.getMessage("join-already-playing"));
			return;
		}
		
		if(p.getVehicle() != null) {
			p.sendMessage(MessageHandler.getMessage("join-vehicle"));
			return;
		}
		
		Game g = gm.getGame(gamename);
		
		if(g == null) {
			p.sendMessage(MessageHandler.getMessage("join-unknown-game").replace("%0%", gamename));
			return;
		}
		
		int max = SurvivalGames.instance.getConfig().getInt("Spectating.Max-Spectators-Per-Arena", 8);
		if(g.getSpecators().size() >= max) {
			p.sendMessage(MessageHandler.getMessage("spectator-full").replace("%0%", Integer.valueOf(max).toString()));
			return;
		}
		
		GameState state = g.getState();
		
		if(state == GameState.VOTING || state == GameState.WAITING || state == GameState.COOLDOWN || state == GameState.RESET) {
			p.sendMessage(MessageHandler.getMessage("spectator-game-running"));
			return;
		}
		
		g.joinSpectator(new SpectatorUser(p, g));
		return;
	}
	
	// END SPECTATOR
	
	public void joinGame(Player p, String gamename) {
		if(!PermissionHandler.hasPermission(p, Permission.JOIN)) {
			p.sendMessage(MessageHandler.getMessage("no-permission"));
			return;
		}
		
		if(isPlaying(p.getName())) {
			p.sendMessage(MessageHandler.getMessage("join-already-playing"));
			return;
		}
		
		if(p.getVehicle() != null) {
			p.sendMessage(MessageHandler.getMessage("join-vehicle"));
			return;
		}
		
		Game g = gm.getGame(gamename);
		
		if(g == null) {
			p.sendMessage(MessageHandler.getMessage("join-unknown-game").replace("%0%", gamename));
			return;
		}
		
		GameState state = g.getState();
		
		if(state != GameState.VOTING && state != GameState.WAITING && state != GameState.COOLDOWN) {
			if(SurvivalGames.instance.getConfig().getBoolean("Spectating.Enabled")) {
				joinGameAsSpectator(p, gamename);
			} else {
				p.sendMessage(MessageHandler.getMessage("join-game-running"));
			}
			return;
		}
		
		if(g.getUsers().size() >= g.getMaximumPlayers()) {
			User kick = PermissionHandler.canJoin(p, g);
			if(kick != null) {
				kick.sendMessage(MessageHandler.getMessage("fulljoin-kick"));
				leaveGame(kick.getPlayer());
			} else {
				p.sendMessage(MessageHandler.getMessage("join-game-full"));
				return;
			}
		}
		
		User user = new User(p, g);
		g.join(user);
		return;
	}
	

	public void leaveGame(final Player p) {
		if(!isPlaying(p.getName())) {
			SpectatorUser su = getSpectator(p.getName());
			if(su != null) {
				leaveGame(su);
				return;
			}
			p.sendMessage(MessageHandler.getMessage("leave-not-playing"));
			return;
		}
		
		if(p.getVehicle() != null)
			p.getVehicle().setPassenger(null);
		if(p.getPassenger() != null) {
			p.setPassenger(null);
		}
		
		final User user = getUser(p.getName());
		user.getStatistics().updateStatistics();
		user.clear();
		Game game = user.getGame();
		
		for(SpectatorUser su : game.getSpecators()) {
			p.showPlayer(su.getPlayer());
		}
		
		
		if(game.getState() == GameState.WAITING || game.getState() == GameState.VOTING || game.getState() == GameState.COOLDOWN)
			game.sendMessage(MessageHandler.getMessage("game-leave").replace("%0%", p.getName()).replace("%1%", Integer.valueOf(game.getPlayingUsers() - 1).toString()).replace("%2%", Integer.valueOf(game.getMaximumPlayers()).toString()));
		game.leave(user);
		
		if(game.getScoreboardPhase() != null) {
			p.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
		}
		
		
		if(p.isDead()) {
			Bukkit.getScheduler().scheduleSyncDelayedTask(SurvivalGames.instance, new Runnable() {
				public void run() {
					setState(p, user);
				}
			}, 1);
		} else {
			setState(p, user);
		}
	}
	
	public void setState(Player p, UserState state) {
		p.teleport(state.getLocation());
		p.setFallDistance(state.getFallDistance());
		p.setGameMode(state.getGameMode());
		p.setAllowFlight(state.getAllowFlight());
		p.setFlying(state.isFlying());
		p.setLevel(state.getLevel());
		p.setExp(state.getExp());
		p.setFireTicks(state.getFireTicks());
		p.setMaxHealth(state.getMaxHealth());
		p.setHealth(state.getHealth());
		p.setFoodLevel(state.getFoodLevel());
		p.setWalkSpeed(state.getWalkSpeed());
		p.setFlySpeed(state.getFlySpeed());
		
		for (Iterator<PotionEffect> i = p.getActivePotionEffects().iterator(); i.hasNext();) {
			p.removePotionEffect(i.next().getType());
		}
		p.addPotionEffects(state.getActivePotionEffects());
		p.getInventory().setHeldItemSlot(state.getHeldItemSlot());
		
		final String name = p.getName();
		final ItemStack[] contents = state.getContents();
		final ItemStack[] armorcontents = state.getArmorContents();
		
		try {
			Bukkit.getScheduler().scheduleSyncDelayedTask(SurvivalGames.instance, new Runnable() {
				public void run() {
					Player fp = Bukkit.getPlayer(name);
					
					if(fp != null) {
						fp.getInventory().setContents(contents);
						fp.getInventory().setArmorContents(armorcontents);
						fp.updateInventory();
					}
				}
			}, 2L);
		} catch(IllegalPluginAccessException e) {
			p.getInventory().setContents(contents);
			p.getInventory().setArmorContents(armorcontents);
			p.updateInventory();
		}
	}
	
	public boolean isSpectator(String name) {
		return getSpectator(name) != null;
	}
	
	public SpectatorUser getSpectator(Player p) {
		return getSpectator(p.getName());
	}
	
	public SpectatorUser getSpectator(String name) {
		for(Game game : gm.getGames()) {
			for(SpectatorUser su : game.getSpecators()) {
				if(su.getName().equals(name))
					return su;
			}
		}
		return null;
	}
	
	public boolean isPlaying(String name) {
		return getUser(name) != null;
	}
	
	public User getUser(Player player) {
		return getUser(player.getName());
	}
	
	public User getUser(String name) {
		for(Game game : gm.getGames()) {
			User u = game.getUser(name);
			if(u != null)
				return u;
		}
		return null;
	}

}
 public abstract class UserState {
	
	protected Player player;
	private double health, maxhealth;
	private float walk, fly;
	private int food, level, fireticks, heldslot;
	private float exp;
	private ItemStack[][] inventory;
	private Location loc;
	private GameMode gamemode;
	private boolean allowFlying, flying;
	private Collection<PotionEffect> ape;
	private float fall;
	
	private long joinTime = System.currentTimeMillis();
	private Game game;
	
	public UserState(Player p, Game game) {
		this.game = game;
		this.player = p;
		this.maxhealth = ((Damageable) p).getMaxHealth();
		this.health = ((Damageable) p).getHealth();
		this.food = p.getFoodLevel();
		this.exp = p.getExp();
		this.level = p.getLevel();
		this.fireticks = p.getFireTicks();
		this.loc = p.getLocation();
		this.gamemode = p.getGameMode();
		this.allowFlying = p.getAllowFlight();
		this.flying = p.isFlying();
		this.ape = p.getActivePotionEffects();
		this.fall = p.getFallDistance();
		this.walk = p.getWalkSpeed();
		this.fly = p.getFlySpeed();
		this.heldslot = p.getInventory().getHeldItemSlot();

		ItemStack[][] store = new ItemStack[2][1];
		store[0] = p.getInventory().getContents();
		store[1] = p.getInventory().getArmorContents();
		this.inventory = store;
		
		StatisticLoader.load(this);
	}
	
	public double getMaxHealth() {
		return maxhealth;
	}
	
	public int getHeldItemSlot() {
		return heldslot;
	}
	
	public Game getGame() {
		return game;
	}
	
	public long getJoinTime() {
		return joinTime;
	}

	public float getFallDistance() {
		return fall;
	}
	
	public float getWalkSpeed() {
		return walk;
	}
	
	public float getFlySpeed() {
		return fly;
	}

	public ItemStack[] getContents() {
		return inventory[0];
	}

	public ItemStack[] getArmorContents() {
		return inventory[1];
	}

	public Collection<PotionEffect> getActivePotionEffects() {
		return ape;
	}

	public GameMode getGameMode() {
		return gamemode;
	}

	public boolean getAllowFlight() {
		return allowFlying;
	}

	public boolean isFlying() {
		return flying;
	}

	public Location getLocation() {
		return this.loc;
	}

	public double getHealth() {
		return this.health;
	}

	public int getFoodLevel() {
		return this.food;
	}

	public float getExp() {
		return this.exp;
	}

	public int getLevel() {
		return this.level;
	}

	public int getFireTicks() {
		return this.fireticks;
	}

	public ItemStack[][] getInventory() {
		return this.inventory;
	}

	public void clear() {
		for (Iterator<PotionEffect> i = player.getActivePotionEffects().iterator(); i.hasNext();) {
			player.removePotionEffect(i.next().getType());
		}
		player.setWalkSpeed(0.2F);
		player.setFlySpeed(0.1F);
		player.setMaxHealth(20D);
		player.setHealth(20D);
		player.setFoodLevel(20);
		player.setLevel(0);
		player.setExp(0);
		player.setFireTicks(0);
		player.setGameMode(GameMode.SURVIVAL);
		player.setFlying(false);
		player.setAllowFlight(false);
		player.getInventory().setHeldItemSlot(0);

		clearInventory();
	}

	public void clearInventory() {
		ItemStack[] inv = player.getInventory().getContents();
		for (int i = 0; i < inv.length; i++) {
			inv[i] = null;
		}
		player.getInventory().setContents(inv);
		inv = player.getInventory().getArmorContents();
		for (int i = 0; i < inv.length; i++) {
			inv[i] = null;
		}
		player.getInventory().setArmorContents(inv);
		player.updateInventory();
	}
	
	public Player getPlayer() {
		return player;
	}
	
	public String getName() {
		return player.getName();
	}
	
	public void sendMessage(BaseComponent[] message) {
		player.spigot().sendMessage(message);
	}
	
	public void sendMessage(String message) {
		player.sendMessage(message);
	}
	
	public String getUUID() {
		return player.getUniqueId().toString();
	}
	
	public abstract boolean isSpectator();
	
	// SQL
	private StatisticData sd;
	
	public StatisticData getStatistics() {
		return sd;
	}
	
	public void setStatistics(StatisticData sd) {
		this.sd = sd;
	}
	
	public boolean areStatisticsLoaded() {
		return sd != null;
	}

}
import java.util.List;
import me.maker56.survivalgames.SurvivalGames;
import me.maker56.survivalgames.Util;
import me.maker56.survivalgames.arena.Arena;
import me.maker56.survivalgames.commands.arguments.ArenaArgument;
import me.maker56.survivalgames.commands.arguments.ConfigArgument;
import me.maker56.survivalgames.commands.arguments.LobbyArgument;
import me.maker56.survivalgames.commands.arguments.StatsArgument;
import me.maker56.survivalgames.commands.messages.MessageHandler;
import me.maker56.survivalgames.commands.permission.Permission;
import me.maker56.survivalgames.commands.permission.PermissionHandler;
import me.maker56.survivalgames.game.Game;
import me.maker56.survivalgames.game.GameState;
import me.maker56.survivalgames.game.phases.IngamePhase;
import me.maker56.survivalgames.game.phases.VotingPhase;
import me.maker56.survivalgames.user.User;
import me.maker56.survivalgames.user.UserManager;
import me.maker56.survivalgames.user.UserState;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

package me.maker56.survivalgames.commands;




public class CommandSG implements CommandExecutor {
	
	private String authors;
	
	public CommandSG() {
		this.authors = " §7§m--§r §ePlugin developed by ";
		List<String> authors = SurvivalGames.getInstance().getDescription().getAuthors();
		for(int i = 0; i < authors.size(); i++) {
			this.authors += authors.get(i);
			
			if(i != authors.size() - 1) {
				this.authors += " and ";
			}
		}
	}

	@Override
	public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
		
		if(cmd.getName().equalsIgnoreCase("sg")) {
			
			
			if(args.length == 0) {
				sender.sendMessage(MessageHandler.getMessage("prefix") + "Version " + SurvivalGames.instance.getDescription().getVersion() + authors);
				
				if(PermissionHandler.hasPermission(sender, Permission.JOIN)) {
					sender.sendMessage("§8/§6sg join [LOBBY] §7- §eJoin a game!");
					sender.sendMessage("§8/§6sg leave §7- §eLeave a game!");
					sender.sendMessage("§8/§6sg vote <ID> §7- §eVote for an arena!");
				}
				
				sender.sendMessage("§8/§6sg stats [NAME] §7- §eShow you statistics of a player");
				
				if(PermissionHandler.hasPermission(sender, Permission.LIST)) {
					sender.sendMessage("§8/§6sg list §7- §eList of all available lobbys!");
				}
				
				if(PermissionHandler.hasPermission(sender, Permission.START)) {
					sender.sendMessage("§8/§6sg start §7- §eForce a lobby to start!");
				}
				
				if(PermissionHandler.hasPermission(sender, Permission.GAME)) {
					sender.sendMessage("§8/§6sg lobby §7- §eShows the lobby helpsite!");
				}
				
				if(PermissionHandler.hasPermission(sender, Permission.ARENA)) {
					sender.sendMessage("§8/§6sg arena §7- §eShows the arena helpsite!");
				}
				
				if(PermissionHandler.hasPermission(sender, Permission.CONFIG)) {
					sender.sendMessage("§8/§6sg config §7- §eShows the configuration management helpsite!");
				}
			} else {
				
				if(args[0].equalsIgnoreCase("arena")) {
					return new ArenaArgument(sender, args).execute();
					
				} else if(args[0].equalsIgnoreCase("lobby") || args[0].equalsIgnoreCase("game")) {
					return new LobbyArgument(sender, args).execute();
				} else if(args[0].equalsIgnoreCase("stats")) {
					return new StatsArgument(sender, args).execute();
				} else if(args[0].equalsIgnoreCase("config")) {
					return new ConfigArgument(sender, args).execute();
				}
				
				// JOIN
				if(args[0].equalsIgnoreCase("join")) {
					
					Player p = (Player)sender;
					
					if(args.length == 1 && SurvivalGames.gameManager.getGames().size() == 1) {
						SurvivalGames.userManger.joinGame(p, SurvivalGames.gameManager.getGames().get(0).getName());
						return true;
					} else if(args.length == 1) {
						p.sendMessage(MessageHandler.getMessage("game-must-enter").replace("%0%", "/sg join <GAMENAME>"));
						return true;
					}
					
					SurvivalGames.userManger.joinGame(p, args[1]);
					return true;
					
				// LEAVE
				} else if(args[0].equalsIgnoreCase("leave")) {
					Player p = (Player)sender;
					UserState user = SurvivalGames.userManger.getUser(p.getName());
					
					if(user == null) {
						user = SurvivalGames.userManger.getSpectator(p.getName());
						if(user == null) {
							p.sendMessage(MessageHandler.getMessage("leave-not-playing"));
							return true;
						}
					}
					
					Game game = user.getGame();
					

					if(game.getState() != GameState.INGAME && game.getState() != GameState.DEATHMATCH) {
